Showing posts with label Objective C. Show all posts
Showing posts with label Objective C. Show all posts

Sunday, 25 August 2024

Track screen Name Firebase from iOS app using single file and single line. Using Objective C feature: Method Swizzling

Only add logger function in app delegate once and get logs from every screen:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // Override point for customization after application launch.

        // Ensure swizzling happens at app launch to log screens

        UIViewController.swizzleViewDidAppear
//// rest of your code...........


Create a logger File "ViewContronller+Extension.swift"

import UIKit 
import Foundation

import FirebaseAnalytics


extension UIViewController {

    /// Tracks the screen view event using Firebase Analytics.

    /// - Parameters:

    ///   - screenName: Custom name for the screen. If not provided, the view controller's class name is used.

    ///   - screenClass: Optional custom class name. Defaults to the view controller's class.

    func trackScreenView(screenName: String? = nil, screenClass: String? = nil) {

        let name = screenName ?? String(describing: type(of: self))

        let className = screenClass ?? String(describing: type(of: self))


        Analytics.logEvent(AnalyticsEventScreenView, parameters: [

            AnalyticsParameterScreenName: name,

            AnalyticsParameterScreenClass: className

        ])

    }

    /// Returns the name of the current screen, which is the view controller's class name.

     var currentScreenName: String {

         return String(describing: type(of: self))

     }


     /// Returns the class name of the current screen.

     var currentScreenClass: String {

         return NSStringFromClass(type(of: self))

     }

    /// Logs the screen view event using Firebase Analytics.

      func logScreenViewEvent() {

          print("FireBase Log: CSreenName \(currentScreenName), CScreenClass: \(currentScreenClass)")

          Analytics.logEvent(AnalyticsEventScreenView, parameters: [

              AnalyticsParameterScreenName: currentScreenName,

              AnalyticsParameterScreenClass: currentScreenClass

          ])

      }

}


extension UIViewController {

    // Swizzle the `viewDidAppear` method

    static let swizzleViewDidAppear: Void = {

        let originalSelector = #selector(UIViewController.viewDidAppear(_:))// original fuinction

        let swizzledSelector = #selector(swizzled_viewDidAppear(_:))// Swizzled Function

        guard let originalMethod = class_getInstanceMethod(UIViewController.self, originalSelector),

              let swizzledMethod = class_getInstanceMethod(UIViewController.self, swizzledSelector) else { return}

        method_exchangeImplementations(originalMethod, swizzledMethod)// Swicth the functions

    }()

    

    // New implementation of `viewDidAppear`

    @objc func swizzled_viewDidAppear(_ animated: Bool) {

        swizzled_viewDidAppear(animated)// Call the original `viewDidAppear` (actually the swizzled method)

        logScreenViewEvent()// Log the screen view event

    }

}


// Ensure swizzling happens at app launch

extension UIApplication {

    private static let runOnce: Void = {

        UIViewController.swizzleViewDidAppear

    }()

    

    override open var next: UIResponder? { // calling original functionality after logging

        UIApplication.runOnce

        return super.next

    }

}

Thursday, 5 July 2018

Xcode iPhone/iPad find the top View Controller in the current View Hierarchy

There are three possiblities. 
Case 1: If the view controller is embedded in UINavigationController
Case 2: If the view controller is embedded in UITabControllerBar
Case 3: If the view controller is presented  via other UIViewController
So we have to traverse through the possible combination until we reach the topmost layer.


#pragma mark- View Hierarchy-
+ (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

+ (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
//Type 1: Travese in tabbar controller-
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)rootViewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
//Type 2: Travese in navigation controller-
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navigationController = (UINavigationController*)rootViewController;
        return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
//Type 3: Travese in view controller-
    } else if (rootViewController.presentedViewController) {
        UIViewController* presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    } else {
        return rootViewController;
    }

}