Skip to content

Commit b43e2d9

Browse files
pilot34Gleb Tarasov
andauthored
Introduce ViewControllerInstanceObserver (#19)
* Revert using `after` in swizzler, as in this case we miss some time in viewDidAppear * Introduce ViewControllerInstanceObserver Separating view controllers by 2 types: - ViewControllerObserver. This is a single observer per app. It consumes events from all view controllers - ViewControllerInstanceObserver. Those are instance per view controller. They receive identifier in initializer and do not need view controller reference at all. Instance observers: - TTIObserver - RenderingObserver - LoggingObserver Other observers: - ViewControllerLeaksObserver - LastOpenedScreenObserver Renamed AppStateObserver to AppStateListener not to confuse with other observers. Reorganized sources folder structure a bit. * Fixing swiftlint issues --------- Co-authored-by: Gleb Tarasov <[email protected]>
1 parent 82d30cc commit b43e2d9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+504
-475
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//
2+
// LoggingObserver.swift
3+
// PerformanceSuite
4+
//
5+
// Created by Gleb Tarasov on 23/09/2022.
6+
//
7+
8+
import Foundation
9+
import UIKit
10+
import SwiftUI
11+
12+
/// Use this protocol for light-weight operations like logging only,
13+
/// it is not intended to be used for some business-logic.
14+
///
15+
/// If you execute something heavy, offload it to some other background thread.
16+
public protocol ViewControllerLoggingReceiver: ScreenMetricsReceiver {
17+
18+
/// Method is called during view controller's initialization
19+
func onInit(screen: ScreenIdentifier)
20+
21+
/// Method is called during view controller's `viewDidLoad`
22+
func onViewDidLoad(screen: ScreenIdentifier)
23+
24+
/// Method is called during view controller's `viewWillAppear`
25+
func onViewWillAppear(screen: ScreenIdentifier)
26+
27+
/// Method is called during view controller's `viewDidAppear`
28+
func onViewDidAppear(screen: ScreenIdentifier)
29+
30+
/// Method is called during view controller's `viewWillDisappear`
31+
func onViewWillDisappear(screen: ScreenIdentifier)
32+
}
33+
34+
35+
/// Observer which forward all delegate methods to its receiver for logging purposes
36+
final class LoggingObserver<V: ViewControllerLoggingReceiver>: ViewControllerInstanceObserver {
37+
38+
init(screen: V.ScreenIdentifier, receiver: V) {
39+
self.screen = screen
40+
self.receiver = receiver
41+
}
42+
43+
private let screen: V.ScreenIdentifier
44+
private let receiver: V
45+
46+
func beforeInit() {
47+
PerformanceMonitoring.consumerQueue.async {
48+
self.receiver.onInit(screen: self.screen)
49+
}
50+
}
51+
52+
func beforeViewDidLoad() {
53+
PerformanceMonitoring.consumerQueue.async {
54+
self.receiver.onViewDidLoad(screen: self.screen)
55+
}
56+
}
57+
58+
func afterViewWillAppear() {
59+
PerformanceMonitoring.consumerQueue.async {
60+
self.receiver.onViewWillAppear(screen: self.screen)
61+
}
62+
}
63+
64+
func afterViewDidAppear() {
65+
PerformanceMonitoring.consumerQueue.async {
66+
self.receiver.onViewDidAppear(screen: self.screen)
67+
}
68+
}
69+
70+
func beforeViewWillDisappear() {
71+
PerformanceMonitoring.consumerQueue.async {
72+
self.receiver.onViewWillDisappear(screen: self.screen)
73+
}
74+
}
75+
76+
static var identifier: AnyObject {
77+
return loggingObserverIdentifier
78+
}
79+
}
80+
81+
private let loggingObserverIdentifier = NSObject()

PerformanceSuite/Sources/RenderingObserver.swift renamed to PerformanceSuite/Sources/InstanceObservers/RenderingObserver.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import UIKit
99

10-
final class RenderingObserver<R: RenderingMetricsReceiver>: ViewControllerObserver, FramesMeterReceiver {
10+
final class RenderingObserver<R: RenderingMetricsReceiver>: ViewControllerInstanceObserver, FramesMeterReceiver {
1111

1212
init(
1313
screen: R.ScreenIdentifier,
@@ -26,28 +26,26 @@ final class RenderingObserver<R: RenderingMetricsReceiver>: ViewControllerObserv
2626

2727
private var metrics = RenderingMetrics.zero
2828

29-
func beforeInit(viewController: UIViewController) {}
29+
func beforeInit() {}
3030

31-
func beforeViewDidLoad(viewController: UIViewController) {}
31+
func beforeViewDidLoad() {}
3232

33-
func afterViewDidAppear(viewController: UIViewController) {
33+
func afterViewDidAppear() {
3434
PerformanceMonitoring.queue.async {
3535
self.metrics = RenderingMetrics.zero
3636
self.framesMeter.subscribe(receiver: self)
3737
}
3838
}
3939

40-
func afterViewWillAppear(viewController: UIViewController) {}
40+
func afterViewWillAppear() {}
4141

42-
func beforeViewWillDisappear(viewController: UIViewController) {
42+
func beforeViewWillDisappear() {
4343
PerformanceMonitoring.queue.async {
4444
self.framesMeter.unsubscribe(receiver: self)
4545
self.reportMetricsIfNeeded()
4646
}
4747
}
4848

49-
func beforeViewDidDisappear(viewController: UIViewController) {}
50-
5149
static var identifier: AnyObject {
5250
return renderingObserverIdentifier
5351
}

PerformanceSuite/Sources/TTIObserver+Extensions.swift renamed to PerformanceSuite/Sources/InstanceObservers/TTIObserver+Extensions.swift

File renamed without changes.

PerformanceSuite/Sources/TTIObserver.swift renamed to PerformanceSuite/Sources/InstanceObservers/TTIObserver.swift

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,23 @@
88
import UIKit
99

1010
/// Observer that calculates `TTIMetrics` during view controller lifetime.
11-
final class TTIObserver<T: TTIMetricsReceiver>: ViewControllerObserver, ScreenIsReadyProvider {
11+
final class TTIObserver<T: TTIMetricsReceiver>: ViewControllerInstanceObserver, ScreenIsReadyProvider {
1212

1313
init(screen: T.ScreenIdentifier,
1414
metricsReceiver: T,
1515
timeProvider: TimeProvider = defaultTimeProvider,
16-
appStateObserver: AppStateObserver = DefaultAppStateObserver()
16+
appStateListener: AppStateListener = DefaultAppStateListener()
1717
) {
1818
self.screen = screen
1919
self.metricsReceiver = metricsReceiver
2020
self.timeProvider = timeProvider
21-
self.appStateObserver = appStateObserver
21+
self.appStateListener = appStateListener
2222
}
2323

2424
private let screen: T.ScreenIdentifier
2525
private let metricsReceiver: T
2626
private let timeProvider: TimeProvider
27-
private let appStateObserver: AppStateObserver
27+
private let appStateListener: AppStateListener
2828

2929

3030
private var screenCreatedTime: DispatchTime?
@@ -37,7 +37,7 @@ final class TTIObserver<T: TTIMetricsReceiver>: ViewControllerObserver, ScreenIs
3737

3838
private var customCreationTime: DispatchTime?
3939

40-
func beforeInit(viewController: UIViewController) {
40+
func beforeInit() {
4141
let now = timeProvider.now()
4242
let action = {
4343
self.sameRunLoopAsTheInit = true
@@ -60,7 +60,7 @@ final class TTIObserver<T: TTIMetricsReceiver>: ViewControllerObserver, ScreenIs
6060
}
6161
}
6262

63-
func beforeViewDidLoad(viewController: UIViewController) {
63+
func beforeViewDidLoad() {
6464
// if there is time passed between `init` and `viewDidLoad`, it means view controller was created earlier, but displayed only recently,
6565
// in this case we don't consider `init` time, but start measuring in `viewDidLoad`.
6666
// Ideally we should start before `loadView`, but it is impossible to swizzle `loadView` because usually nobody calls `super.loadView`
@@ -80,7 +80,7 @@ final class TTIObserver<T: TTIMetricsReceiver>: ViewControllerObserver, ScreenIs
8080
}
8181
}
8282

83-
func afterViewWillAppear(viewController: UIViewController) {
83+
func afterViewWillAppear() {
8484
let now = timeProvider.now()
8585
let action = {
8686
if self.viewWillAppearTime != nil && self.ttiCalculated == false {
@@ -112,7 +112,7 @@ final class TTIObserver<T: TTIMetricsReceiver>: ViewControllerObserver, ScreenIs
112112
}
113113
}
114114

115-
func afterViewDidAppear(viewController: UIViewController) {
115+
func afterViewDidAppear() {
116116
let now = timeProvider.now()
117117
let action = {
118118
if self.shouldReportTTI && self.viewDidAppearTime == nil {
@@ -129,7 +129,7 @@ final class TTIObserver<T: TTIMetricsReceiver>: ViewControllerObserver, ScreenIs
129129
}
130130
}
131131

132-
func beforeViewWillDisappear(viewController: UIViewController) {
132+
func beforeViewWillDisappear() {
133133
// if screenIsReady wasn't called until now, we consider that screen was ready in `viewDidAppear`.
134134
let action = {
135135
if self.shouldReportTTI && self.screenIsReadyTime == nil {
@@ -146,8 +146,6 @@ final class TTIObserver<T: TTIMetricsReceiver>: ViewControllerObserver, ScreenIs
146146
}
147147
}
148148

149-
func beforeViewDidDisappear(viewController: UIViewController) {}
150-
151149
static var identifier: AnyObject {
152150
return TTIObserverHelper.identifier
153151
}
@@ -201,7 +199,7 @@ final class TTIObserver<T: TTIMetricsReceiver>: ViewControllerObserver, ScreenIs
201199
}
202200

203201
private var shouldReportTTI: Bool {
204-
return !ttiCalculated && !appStateObserver.wasInBackground && !ignoreThisScreen
202+
return !ttiCalculated && !appStateListener.wasInBackground && !ignoreThisScreen
205203
}
206204
}
207205

File renamed without changes.
File renamed without changes.
File renamed without changes.

PerformanceSuite/Sources/LoggingObserver.swift renamed to PerformanceSuite/Sources/Observers/LastOpenedScreenObserver.swift

Lines changed: 9 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,26 @@
11
//
2-
// LoggingObserver.swift
3-
// PerformanceSuite
2+
// LastOpenedScreenObserver.swift
3+
// Pods
44
//
5-
// Created by Gleb Tarasov on 23/09/2022.
5+
// Created by Gleb Tarasov on 19/10/2024.
66
//
77

8-
import Foundation
9-
import UIKit
108
import SwiftUI
119

12-
/// Use this protocol for light-weight operations like logging only,
13-
/// it is not intended to be used for some business-logic.
14-
///
15-
/// If you execute something heavy, offload it to some other background thread.
16-
public protocol ViewControllerLoggingReceiver: ScreenMetricsReceiver {
10+
final class LastOpenedScreenObserver: ViewControllerObserver {
11+
func beforeInit(viewController: UIViewController) {}
1712

18-
/// Method is called during view controller's initialization
19-
func onInit(screen: ScreenIdentifier)
13+
func beforeViewDidLoad(viewController: UIViewController) {}
2014

21-
/// Method is called during view controller's `viewDidLoad`
22-
func onViewDidLoad(screen: ScreenIdentifier)
23-
24-
/// Method is called during view controller's `viewWillAppear`
25-
func onViewWillAppear(screen: ScreenIdentifier)
26-
27-
/// Method is called during view controller's `viewDidAppear`
28-
func onViewDidAppear(screen: ScreenIdentifier)
29-
30-
/// Method is called during view controller's `viewWillDisappear`
31-
func onViewWillDisappear(screen: ScreenIdentifier)
32-
33-
/// Method is called during view controller's `viewDidDisappear`
34-
func onViewDidDisappear(screen: ScreenIdentifier)
35-
}
36-
37-
38-
/// Observer which forward all delegate methods to its receiver for logging purposes
39-
final class LoggingObserver<V: ViewControllerLoggingReceiver>: ViewControllerObserver {
40-
41-
init(screen: V.ScreenIdentifier, receiver: V) {
42-
self.screen = screen
43-
self.receiver = receiver
44-
}
45-
46-
private let screen: V.ScreenIdentifier
47-
private let receiver: V
48-
49-
50-
func beforeInit(viewController: UIViewController) {
51-
PerformanceMonitoring.consumerQueue.async {
52-
self.receiver.onInit(screen: self.screen)
53-
}
54-
}
55-
56-
func beforeViewDidLoad(viewController: UIViewController) {
57-
PerformanceMonitoring.consumerQueue.async {
58-
self.receiver.onViewDidLoad(screen: self.screen)
59-
}
60-
}
61-
62-
func afterViewWillAppear(viewController: UIViewController) {
63-
PerformanceMonitoring.consumerQueue.async {
64-
self.receiver.onViewWillAppear(screen: self.screen)
65-
}
66-
}
15+
func afterViewWillAppear(viewController: UIViewController) {}
6716

6817
func afterViewDidAppear(viewController: UIViewController) {
6918
rememberOpenedScreenIfNeeded(viewController)
70-
PerformanceMonitoring.consumerQueue.async {
71-
self.receiver.onViewDidAppear(screen: self.screen)
72-
}
7319
}
7420

75-
func beforeViewWillDisappear(viewController: UIViewController) {
76-
PerformanceMonitoring.consumerQueue.async {
77-
self.receiver.onViewWillDisappear(screen: self.screen)
78-
}
79-
}
21+
func beforeViewWillDisappear(viewController: UIViewController) {}
8022

81-
func beforeViewDidDisappear(viewController: UIViewController) {
82-
PerformanceMonitoring.consumerQueue.async {
83-
self.receiver.onViewDidDisappear(screen: self.screen)
84-
}
85-
}
86-
87-
static var identifier: AnyObject {
88-
return loggingObserverIdentifier
89-
}
23+
func beforeViewDidDisappear(viewController: UIViewController) {}
9024

9125
// MARK: - Top screen detection
9226

@@ -194,8 +128,6 @@ final class LoggingObserver<V: ViewControllerLoggingReceiver>: ViewControllerObs
194128

195129
}
196130

197-
private let loggingObserverIdentifier = NSObject()
198-
199131
// We cannot check `viewController is UIHostingController` because of generics,
200132
// so we use helper protocol here
201133
protocol HostingControllerIdentifier { }

PerformanceSuite/Sources/ViewControllerLeaksObserver.swift renamed to PerformanceSuite/Sources/Observers/ViewControllerLeaksObserver.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@ final class ViewControllerLeaksObserver: ViewControllerObserver {
107107
}
108108
}
109109

110-
static let identifier: AnyObject = NSObject()
111-
112110
// MARK: - Helpers
113111

114112
private func selfAndAllChildren(viewController: UIViewController) -> [UIViewController] {

0 commit comments

Comments
 (0)