Skip to content

Commit

Permalink
fix: Calling -[MParticle reset] doesn't clean up our app delegate proxy
Browse files Browse the repository at this point in the history
When app delegate proxying is enabled, when resetting the mParticle SDK we were previously leaving our proxy object set as UIApplication.sharedApplication.delegate. This would cause issues if the SDK is initialized again as we would now proxy our proxy and handle events more than once.

This change cleanly replaces the proxy with the original app delegate, so that no matter how many times the SDK is reset and initialized, there will only ever be one app delegate proxy in place.
  • Loading branch information
einsteinx2 authored Sep 15, 2021
1 parent cc57145 commit d7932da
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 3 deletions.
1 change: 1 addition & 0 deletions mParticle-Apple-SDK/MPBackendController.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ extern const NSInteger kInvalidKey;
- (void)removeUserAttribute:(nonnull NSString *)key timestamp:(nonnull NSDate *)timestamp completionHandler:(void (^ _Nullable)(NSString * _Nullable key, id _Nullable value, MPExecStatus execStatus))completionHandler;
- (void)setUserIdentity:(nullable NSString *)identityString identityType:(MPUserIdentity)identityType timestamp:(nonnull NSDate *)timestamp completionHandler:(void (^ _Nonnull)(NSString * _Nullable identityString, MPUserIdentity identityType, MPExecStatus execStatus))completionHandler;
- (void)startWithKey:(nonnull NSString *)apiKey secret:(nonnull NSString *)secret firstRun:(BOOL)firstRun installationType:(MPInstallationType)installationType proxyAppDelegate:(BOOL)proxyAppDelegate startKitsAsync:(BOOL)startKitsAsync consentState:(MPConsentState *_Nullable)consentState completionHandler:(dispatch_block_t _Nonnull)completionHandler;
- (void)unproxyOriginalAppDelegate;
- (void)saveMessage:(nonnull MPMessage *)message updateSession:(BOOL)updateSession;
- (void)skipNextUpload;
- (MPExecStatus)waitForKitsAndUploadWithCompletionHandler:(void (^ _Nullable)(void))completionHandler;
Expand Down
35 changes: 32 additions & 3 deletions mParticle-Apple-SDK/MPBackendController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -546,15 +546,44 @@ - (void)processPendingArchivedMessages {
}

- (void)proxyOriginalAppDelegate {
if (originalAppDelegateProxied) {
if (originalAppDelegateProxied && !appDelegateProxy) {
return;
}

originalAppDelegateProxied = YES;

// Add our proxy object to hook calls to the app delegate
UIApplication *application = [MPApplication sharedUIApplication];
appDelegateProxy = [[MPAppDelegateProxy alloc] initWithOriginalAppDelegate:application.delegate];
application.delegate = appDelegateProxy;

originalAppDelegateProxied = YES;
}

/**
NOTE: This static variable is used to retain the app delegate after unproxying. Removing this will cause a crash when calling the MParticle "reset" method.
The reason for this is that when an iOS app is first launched, the app delegate is implicitly retained. However, after we proxy it and call the setter for the UIApplication delegate property, the system no longer "magically" retains the app delegate. Because the property is marked with "assign", setting the original delegate object back to the UIApplication delegate property will not cause it to be retained again by UIApplication, causing it to be deallocated as soon as our appDelegateProxy object is deallocated as it is the only thing still holding a reference. There is no real downside to doing this as app delegates are meant to live for the life of the application anyway. We're just using this reference in place of the "magic" reference/retain that iOS does when first launching the app.
*/
static id unproxiedAppDelegateReference = nil;

- (void)unproxyOriginalAppDelegate {
if (!originalAppDelegateProxied && appDelegateProxy) {
return;
}

UIApplication *application = [MPApplication sharedUIApplication];
if (application.delegate != appDelegateProxy) {
MPILogWarning(@"Tried to unproxy the app delegate, but our proxy is no longer in place, application.delegate: %@", application.delegate);
return;
}

// Hold a strong reference to the app delegate to prevent it from being deallocated
unproxiedAppDelegateReference = appDelegateProxy.originalAppDelegate;

// Return the app delegate to it's original state and remove our proxy object
application.delegate = appDelegateProxy.originalAppDelegate;
appDelegateProxy = nil;

originalAppDelegateProxied = NO;
}

- (void)requestConfig:(void(^ _Nullable)(BOOL uploadBatch))completionHandler {
Expand Down
1 change: 1 addition & 0 deletions mParticle-Apple-SDK/mParticle.m
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,7 @@ - (void)reset {
dispatch_sync(messageQueue, ^{
[[MPIUserDefaults standardUserDefaults] resetDefaults];
[[MParticle sharedInstance].persistenceController resetDatabase];
[[MParticle sharedInstance].backendController unproxyOriginalAppDelegate];
[MParticle setSharedInstance:nil];
});
}
Expand Down

0 comments on commit d7932da

Please sign in to comment.