Skip to content

Commit bde4741

Browse files
authored
Implement debug logging for view model updates (#141)
closes #130
1 parent 7c01bdd commit bde4741

9 files changed

+89
-6
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ env:
1818
EXAMPLE_PROJECT: Example/ExampleApp.xcodeproj
1919
EXAMPLE_SCHEME: ExampleApp
2020

21-
DEVELOPER_DIR: /Applications/Xcode_16.0.app/Contents/Developer
21+
DEVELOPER_DIR: /Applications/Xcode_16.1.app/Contents/Developer
2222

2323
IOS_DEST: "platform=iOS Simulator,name=iPhone 16,OS=latest"
2424

.github/workflows/spm-ios.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ on:
1212
- main
1313

1414
env:
15-
DEVELOPER_DIR: /Applications/Xcode_16.0.app/Contents/Developer
15+
DEVELOPER_DIR: /Applications/Xcode_16.1.app/Contents/Developer
1616
IOS_DEST: "platform=iOS Simulator,name=iPhone 16,OS=latest"
1717
SCHEME: ReactiveCollectionsKit
1818

.swiftlint.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ analyzer_rules:
111111
- capture_variable
112112

113113
line_length: 200
114-
file_length: 600
114+
file_length: 650
115115

116116
type_body_length: 500
117117
function_body_length: 250

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ NEXT
66
-----
77

88
- Improve debug descriptions (i.e., `CustomDebugStringConvertible`) for various types. ([@nuomi1](https://github.com/nuomi1), [#139](https://github.com/jessesquires/ReactiveCollectionsKit/pull/139))
9+
- Implement (optional) debug logging for view model updates. You can now provide a logger for debugging purposes by setting `CollectionViewDriver.logger`. The library provides a default implementation via `RCKLogger.shared`. ([@nuomi1](https://github.com/nuomi1), [#141](https://github.com/jessesquires/ReactiveCollectionsKit/pull/141))
910
- TBA
1011

1112
0.1.8

Example/Sources/Grid/GridViewController.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@ final class GridViewController: ExampleViewController, CellEventCoordinator {
2626
didSet {
2727
// Every time the model updates, regenerate and set the view model
2828
let viewModel = self.makeViewModel()
29-
self.driver.update(viewModel: viewModel, animated: true) {
29+
self.driver.update(viewModel: viewModel, animated: true) { _ in
3030
print("grid did update!")
31-
print($0.viewModel)
3231
}
3332
}
3433
}
@@ -43,6 +42,8 @@ final class GridViewController: ExampleViewController, CellEventCoordinator {
4342

4443
override func viewDidLoad() {
4544
super.viewDidLoad()
45+
self.driver.logger = RCKLogger.shared
46+
4647
let viewModel = self.makeViewModel()
4748
self.driver.update(viewModel: viewModel)
4849
}

Example/Sources/List/ListViewController.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ final class ListViewController: ExampleViewController, CellEventCoordinator {
3636
Task { @MainActor in
3737
await self.driver.update(viewModel: viewModel)
3838
print("list did update! async")
39-
print(viewModel)
4039
}
4140
}
4241
}
@@ -66,6 +65,7 @@ final class ListViewController: ExampleViewController, CellEventCoordinator {
6665

6766
override func viewDidLoad() {
6867
super.viewDidLoad()
68+
self.driver.logger = RCKLogger.shared
6969

7070
let viewModel = self.makeViewModel()
7171
self.driver.update(viewModel: viewModel)

Sources/CollectionViewDriver.swift

+31
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ public final class CollectionViewDriver: NSObject {
5151

5252
private var _cachedRegistrations = Set<ViewRegistration>()
5353

54+
/// A debug logger to log messages that track the internal operations of the driver.
55+
/// The default value is `nil`.
56+
///
57+
/// - Note: You may wish to set this property to the library-provided logger, `RCKLogger.shared`.
58+
/// However, you can also provide your own implementation via the `Logging` protocol.
59+
public var logger: Logging? {
60+
didSet {
61+
self._dataSource.logger = self.logger
62+
}
63+
}
64+
5465
// MARK: Init
5566

5667
/// Initializes a new `CollectionViewDriver`.
@@ -195,6 +206,16 @@ public final class CollectionViewDriver: NSObject {
195206
if self.options.reloadDataOnReplacingViewModel {
196207
// if given a totally new model, simply reload instead of diff
197208
guard new.id == old.id else {
209+
self.logger?.log(
210+
"""
211+
Driver will reload view model.
212+
Old:
213+
\(old.debugDescription)
214+
New:
215+
\(new.debugDescription)
216+
"""
217+
)
218+
198219
self._dataSource.reload(self.viewModel) { [weak self] in
199220
// Note: UIKit guarantees this closure is called on the main queue.
200221
self?._displayEmptyViewIfNeeded(animated: animated, completion: completion)
@@ -203,6 +224,16 @@ public final class CollectionViewDriver: NSObject {
203224
}
204225
}
205226

227+
self.logger?.log(
228+
"""
229+
Driver will update view model.
230+
Old:
231+
\(old.debugDescription)
232+
New:
233+
\(new.debugDescription)
234+
"""
235+
)
236+
206237
self._dataSource.applySnapshot(
207238
from: old,
208239
to: new,

Sources/DiffableDataSource.swift

+11
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ final class DiffableDataSource: UICollectionViewDiffableDataSource<AnyHashable,
3434
autoreleaseFrequency: .workItem
3535
)
3636

37+
var logger: Logging?
38+
3739
// MARK: Init
3840

3941
init(
@@ -60,12 +62,15 @@ final class DiffableDataSource: UICollectionViewDiffableDataSource<AnyHashable,
6062
// MARK: Applying snapshots
6163

6264
func reload(_ viewModel: CollectionViewModel, completion: SnapshotCompletion?) {
65+
self.logger?.log("DataSource will apply reload snapshot")
66+
6367
let snapshot = DiffableSnapshot(viewModel: viewModel)
6468
self.applySnapshotUsingReloadData(snapshot) {
6569
// UIKit guarantees `completion` is called on the main queue.
6670
dispatchPrecondition(condition: .onQueue(.main))
6771
MainActor.assumeIsolated {
6872
completion?()
73+
self.logger?.log("DataSource reload snapshot completed")
6974
}
7075
}
7176
}
@@ -76,13 +81,16 @@ final class DiffableDataSource: UICollectionViewDiffableDataSource<AnyHashable,
7681
animated: Bool,
7782
completion: SnapshotCompletion?
7883
) {
84+
self.logger?.log("DataSource will apply diffing snapshot")
85+
7986
// Get all the currently visible items, so we can reconfigure them if needed.
8087
//
8188
// This queries the collection view for visible items, so it must happen on the main thread.
8289
// We need to inspect the current collection view state first, then pass this info downstream.
8390
let visibleItemIdentifiers = self._visibleItemIdentifiers()
8491

8592
if self._diffOnBackgroundQueue {
93+
self.logger?.log("DataSource using background queue")
8694
self._diffingQueue.async {
8795
self._applySnapshot(
8896
from: source,
@@ -93,6 +101,7 @@ final class DiffableDataSource: UICollectionViewDiffableDataSource<AnyHashable,
93101
)
94102
}
95103
} else {
104+
self.logger?.log("DataSource using main queue")
96105
dispatchPrecondition(condition: .onQueue(.main))
97106
self._applySnapshot(
98107
from: source,
@@ -188,6 +197,8 @@ final class DiffableDataSource: UICollectionViewDiffableDataSource<AnyHashable,
188197

189198
// Finally, we're done and can call completion.
190199
completion?()
200+
201+
self.logger?.log("DataSource diffing snapshot complete")
191202
}
192203
}
193204
}

Sources/Logger.swift

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// Created by Jesse Squires
3+
// https://www.jessesquires.com
4+
//
5+
// Documentation
6+
// https://jessesquires.github.io/ReactiveCollectionsKit
7+
//
8+
// GitHub
9+
// https://github.com/jessesquires/ReactiveCollectionsKit
10+
//
11+
// Copyright © 2019-present Jesse Squires
12+
//
13+
14+
import Foundation
15+
import OSLog
16+
17+
/// Describes a type that logs messages for the library.
18+
public protocol Logging: Sendable {
19+
20+
/// Logs the provided message.
21+
///
22+
/// - Parameter message: The message to log.
23+
func log(_ message: @escaping @autoclosure () -> String)
24+
}
25+
26+
/// A default ``Logging`` implementation to log debug messages.
27+
///
28+
/// You can set this logger for the ``CollectionViewDriver.logger``.
29+
public final class RCKLogger: Logging {
30+
/// The shared logger instance.
31+
public static let shared = RCKLogger()
32+
33+
private let _logger = Logger(subsystem: "com.jessesquires.ReactiveCollectionsKit", category: "")
34+
35+
/// :nodoc:
36+
public func log(_ message: @escaping @autoclosure () -> String) {
37+
self._logger.debug("\(message())")
38+
}
39+
}

0 commit comments

Comments
 (0)