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

Download this comprehensive Mobile Testing Reference Guide to help prioritize which mobile devices and OSs to test against, brought to you in partnership with Sauce Labs.

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.

Analysts agree that a mix of emulators/simulators and real devices are necessary to optimize your mobile app testing - learn more in this white paper, brought to you in partnership with Sauce Labs.

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.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}