Method swizzling and OSLog usage

Photo by Thomas Peham on Unsplash

When collaborating on a large application with multiple developers, debugging screens outside of your team requires obtaining the current view controller name before proceeding with code debugging. The “Debug view hierarchy” tool is typically utilised in such cases, although it may load slowly due to the size of the application.

Currently, we are facing an issue that requires a resolution.

It is possible to achieve this by implementing a BaseViewController and tracking the names of the view life cycle methods. Nevertheless, this method results in unnecessary overhead and should be steered clear of.

Peviously, we performed method swizzling in Objective-C, and now we will implement it in Swift.

We will utilise Apple’s recommended OSLog for printing instead of the print() or debugPrint() methods when working with print.

Initially, we develop a UIViewController extension for the purpose of method swizzling. In this particular case, we are focusing solely on the viewDidAppear method.extension UIViewController {
@objc
func _tracked_viewWillApper(_ animated: Bool) {
let name = String(describing: type(of: self))
logScreenWillApper(name: name)
_tracked_viewWillApper(animated)
}

static func swizzle() {
let originalSelector = #selector(UIViewController.viewWillAppear)
let sizzleSelector = #selector(UIViewController._tracked_viewWillApper(_:))

if let originalMethod = class_getInstanceMethod(self, originalSelector),
let swizzledMethod = class_getInstanceMethod(self, sizzleSelector) {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}

We must now develop a logger. Additionally, we must ensure that we log the screen life cycle on the crash analytics to better comprehend the user flow. Establishing a protocol for logging is crucial, as dependencies rely on the protocol rather than direct implementation.

protocol LoggerRepository {
func screenWillApper(viewControllerName: String)
}

console logger implementation:

struct LoggerRepositoryImpl: LoggerRepository {
func screenWillApper(viewControllerName: String) {
let logger = os.Logger(subsystem: “AppName”, category: “viewWillApper”)
logger.debug(“(viewControllerName)”)
}
}

Crash analytics logger implementation:

struct FireBaseRepositoryImpl: LoggerRepository {
func screenWillApper(viewControllerName: String) {
// log screen name under the crash analytics
print(“crash analytics (viewControllerName)”)
}
}

We possess two loggers mentioned earlier, however, to streamline the process, we have developed a single adapter that can accommodate multiple loggers.

final class ViewLifeCycleLogger: LoggerRepository {
let repositories: [LoggerRepository] init(repositories: [LoggerRepository]) {
self.repositories = repositories
}

func screenWillApper(viewControllerName: String) {
repositories.forEach { $0.screenWillApper(
viewControllerName: viewControllerName) }
}
}The class accepts an array of the LoggerRepository protocol and simply calls the screenWillAppear method of those loggers when its method is called. In the future, we can easily pass another logger into it. Additionally, we can easily test this class using mockLogger.

Call this logger from the viewController extension, which we write at first.

extension UIViewController {
@objc
func _tracked_viewWillApper(_ animated: Bool) {
let name = String(describing: type(of: self))
// calling logger method
logScreenWillApper(name: name)
_tracked_viewWillApper(animated)
}
// Logger methods
private var logger: LoggerRepository {
ViewLifeCycleLogger(repositories: [LoggerRepositoryImpl(), FireBaseRepositoryImpl()])
}
// we just need viewController name so as per the we are just passing string for safe side
// because logger should not have access of the viewController
func logScreenWillApper(name: String) {
let name = String(describing: type(of: self))
logger.screenWillApper(viewControllerName: name)
}
}

At last we need to configure our ViewController swilling method, so we will call method from AppDelegate’s didFinishLaunching method

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UIViewController.swizzle()
// other code
}

We will discuss the various features of OSLog in a separate article. Now, let’s execute this code and review the output in our console log.

OSLog offers a greater amount of information compared to print() and debugPrint(), as evidenced by the screenshot above. According to the article, our view life cycle is now “fully automatic”, meaning that the life cycle methods of any loaded viewController will be logged on the console and in Firebase. (I’ve just used a placeholder for that, but you can log it.)

Thank you for reading!

Pravin Tate – Medium

Swift | Elegant and automated display view life cycle tracker. was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.

​ Level Up Coding – Medium

about Infinite Loop Digital

We support businesses by identifying requirements and helping clients integrate AI seamlessly into their operations.

Gartner
Gartner Digital Workplace Summit Generative Al

GenAI sessions:

  • 4 Use Cases for Generative AI and ChatGPT in the Digital Workplace
  • How the Power of Generative AI Will Transform Knowledge Management
  • The Perils and Promises of Microsoft 365 Copilot
  • How to Be the Generative AI Champion Your CIO and Organization Need
  • How to Shift Organizational Culture Today to Embrace Generative AI Tomorrow
  • Mitigate the Risks of Generative AI by Enhancing Your Information Governance
  • Cultivate Essential Skills for Collaborating With Artificial Intelligence
  • Ask the Expert: Microsoft 365 Copilot
  • Generative AI Across Digital Workplace Markets
10 – 11 June 2024

London, U.K.