Adrien B
Adrien B

Reputation: 41

iOS equivalent to Android registerActivityLifecycleCallbacks

I developed an Android app where I used the application.registerActivityLifecycleCallbacks (http://developer.android.com/reference/android/app/Application.html) to know when each Activity is started and stopped and record it for analytics purposes. I am now developing the iOS version and I cannot find an equivalent to this method to monitor the UIViewControllers or UIView displayed by the app.

Anybody has an idea ? I am a beginner on iOS so I may not be taking the right approach, feel free to suggest other ideas.

Edit
After the first answer I felt I should be more precise. I am actually developing a SDK for other developers to include in their apps so I want the impact of the SDK on their code to be as small as possible.

I first thought about doing a BaseActivity/BaseUIViewController that developers would have to extend in all the Activity/UIViewController but it felt heavy and since both language don't allow multiple inheritance this would greatly impact their code. This is why the registerActivityLifecycleCallbacks method is great in Android because they only have to give me an Application or Activity object.

Is there a solution for iOS or I will have to create a BaseController ?

Thank you in advance.

Upvotes: 4

Views: 867

Answers (2)

Dmitriy Mitiai
Dmitriy Mitiai

Reputation: 1200

It's too late for the answer, but perhaps it will help someone else. To achieve the similar behaviour you can use "Swizzling" approach. Below is an example of the solution in Swift:

  1. Define UIViewController extension with methods you want to intercept. For example viewDidAppear(:):
extension UIViewController {   
  
    @objc func internal_viewDidAppear(_ animated: Bool) {
        self.internal_viewDidAppear(animated)// self here is the UIViewController which was appeared on the screen. Do with it whatever you want 
    } 
}
  1. Implement swizzling similar to this:
func swizzleViewDidAppear(){

    let clazz = UIViewController.self
    let originalSelector =  #selector(clazz.viewDidAppear(_:))
    let swizledSelector = #selector(clazz.internal_viewDidAppear(_:))    
    swizzle(originalClass: clazz, swizzledClass: clazz, originalSelector: originalSelector, swizzledSelector: swizledSelector)
}
  1. Main magic of the swizzling:
func swizzle(originalClass: AnyClass, swizzledClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {
    let originalMethod = class_getInstanceMethod(originalClass, originalSelector)
    let swizzledMethod = class_getInstanceMethod(swizzledClass, swizzledSelector)
    let didAddMethod = class_addMethod(originalClass, originalSelector, method_getImplementation(swizzledMethod!),method_getTypeEncoding(swizzledMethod!))
    if didAddMethod {
        class_replaceMethod(originalClass, swizzledSelector,method_getImplementation(originalMethod!),method_getTypeEncoding(originalMethod!))
    } else {
        method_exchangeImplementations(originalMethod!, swizzledMethod!)
    }
}

Now you need to call swizzleViewDidAppear on your SDK startup (best in viewDidLaunchWIthOptions). This code let's system know that instead of the original viewDidAppear(:) implementation it should call your's implementation. And to trigger the original implementation inside intercepted method body we call the method itself.

Notes:

  1. Swizzling should be called once per session, so you need to add some flag indication that swizzling was already called, because otherwise it'll replace implementations back to the origins.

  2. When developer overrides the viewDidAppear(:) it's mandatory to call super.viewDidAppear(animated) because without it swizzling will not work.

Good luck and happy coding :)

Upvotes: 0

c_rath
c_rath

Reputation: 3628

I've not run into anything that specific as the application.registerActivityLifecycleCallbacks in iOS, but you could always implement your own using the methods that exist within the AppDelegate and each class.

From the AppDelegate, you are provided the methods to determine the state of the overall application such as determining when the application finished loading, when it enters the background, and so forth. Details on these states can be found in the UIApplicationDelegate Protocol Reference page.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    NSLog("Application did finish launching with options: %@", launchOptions);
    return YES;
}

enter image description here

For each view controller, you can add your implementation to the individual view controller lifecycle methods within each file. Methods such as viewDidLoad, viewWillAppear, viewDidAppear, and viewDidLayoutSubviews are available.

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"View %@ did load", self);
}

Upvotes: 0

Related Questions