This content originally appeared on Level Up Coding - Medium and was authored by Pravin Tate
Method swizzling and OSLog usage
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!
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.
This content originally appeared on Level Up Coding - Medium and was authored by Pravin Tate
Pravin Tate | Sciencx (2024-07-09T16:43:00+00:00) Swift | Elegant and automated display view life cycle tracker.. Retrieved from https://www.scien.cx/2024/07/09/swift-elegant-and-automated-display-view-life-cycle-tracker/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.