Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How the Apteligent iOS SDK Safeguards Itself Against Crashes

DZone's Guide to

How the Apteligent iOS SDK Safeguards Itself Against Crashes

Check out how Apteligent's iOS SDK works to help minimize the crashes that come from mistakes.

· Mobile Zone
Free Resource

As you might imagine, at Apteligent we take app crashes very seriously.  As a result, we have gone to great lengths to ensure we are providing our customers with a quality product. Our number one priority on the SDK team is to “do no harm.” We take testing very seriously; we have excellent unit test coverage and we have also built a “monkey test” suite that randomly prods our SDK from different threads at a high volume (which is perhaps a topic for another post). Despite our focus on clean design and testing, we know we’re still liable to make a mistake.  That’s why we have taken extra precautions to prevent our SDK from crashing your app.

From the beginning, we’ve wrapped all of our top level SDK functions in try/catch blocks that do a blanket catch of all exceptions that could be thrown from our code.  

Here’s an example of what a typical function used to look like in our code:

+ (void)leaveBreadcrumb:(NSString *)breadcrumb {
    @try {
        [_internalInstance leaveBreadcrumb:breadcrumb];
        } @catch (NSException *exception) {
            // Perform internal error logging
        }
    }


This is functional but a bit cumbersome since it requires boilerplate code for every entry point into our SDK.  That’s why we came up with an elegant way to protect any class from leaking exceptions.  The solution is to use an NSProxy object.  NSProxy makes use of Objective-C’s forwardInvocation: method, which allows an object to pass along a message to another object.  The Objective-C runtime automatically calls this method if the original object being called does not respond to a given selector.

By using this technique, our new code can be written without the boilerplate try/catch block:

+ (void)leaveBreadcrumb:(NSString *)breadcrumb {
    [_internalInstance leaveBreadcrumb:breadcrumb];
}

Here’s what our NSProxy class implementation looks like:

@implementation CRErrorBarrier

- (instancetype)initWithDelegate:(id)errorBarrierDelegate
{
    self.errorBarrierDelegate = errorBarrierDelegate;
    return self;
}

- (void)forwardInvocation:(NSInvocation *)invocation
{
    @try {
        [invocation invokeWithTarget:self.errorBarrierDelegate];
    } @catch (NSException *exception) {
        // Perform internal error logging
    }
}

- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
    @synchronized (self) {
        return [self.errorBarrierDelegate methodSignatureForSelector:sel];
    }
}

@end


Our CRErrorBarrier class inherits from NSProxy and it only implements two methods.  This class is initialized with a single delegate object which is the class that we want to protect from throwing exceptions.  

To protect a class from throwing exceptions we use the following code:

objectToProtect = (id)[[CRErrorBarrior alloc] initWithDelegate:objectToProtect];

From then on, we can use the object as we normally would.  The CRErrorBarrier class will forward all messages to the original object; the only difference is that every call is wrapped in a try/catch block.  

We are constantly thinking about ways to keep your app safe from SDKs — this is just one  approach that we use to keep a low profile when our SDK is installed in your apps.

Topics:
app ,code ,functions ,test ,implementation ,unit ,sdk

Published at DZone with permission of David Shirley, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}