Skip to content

Commit c39dc39

Browse files
authored
Merge pull request #153 from JensAyton/fix-logging-adapators
Fix and simplify logging adaptors
2 parents e62cd6c + c71a4c3 commit c39dc39

File tree

6 files changed

+54
-49
lines changed

6 files changed

+54
-49
lines changed

MobiusCore/Source/LoggingAdaptors.swift

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,51 +17,55 @@
1717
// specific language governing permissions and limitations
1818
// under the License.
1919

20-
/// Helper to wrap initator functions with log calls.
21-
///
22-
/// Also adds call stack annotation where we call into the client-provided initiator.
23-
final class LoggingInitiate<Model, Effect> {
24-
typealias Initiate = MobiusCore.Initiate<Model, Effect>
25-
typealias First = MobiusCore.First<Model, Effect>
26-
27-
private let realInit: Initiate
28-
private let willInit: (Model) -> Void
29-
private let didInit: (Model, First) -> Void
20+
typealias UpdateClosure<Model, Event, Effect> = (Model, Event) -> Next<Model, Effect>
3021

31-
init<Logger: MobiusLogger>(_ realInit: @escaping Initiate, logger: Logger)
32-
where Logger.Model == Model, Logger.Effect == Effect {
33-
self.realInit = realInit
34-
willInit = logger.willInitiate
35-
didInit = logger.didInitiate
22+
extension MobiusLogger {
23+
/// Wraps an Initiate in logging calls and stack annotations
24+
func wrap(initiate: @escaping Initiate<Model, Effect>) -> Initiate<Model, Effect> {
25+
return { model in
26+
self.willInitiate(model: model)
27+
let result = invokeInitiate(initiate, model: model)
28+
self.didInitiate(model: model, first: result)
29+
return result
30+
}
3631
}
3732

38-
func initiate(_ model: Model) -> First {
39-
willInit(model)
40-
let result = invokeInitiate(model: model)
41-
didInit(model, result)
42-
43-
return result
33+
/// Wraps an update closure in logging calls and stack annotations
34+
func wrap(update: @escaping UpdateClosure<Model, Event, Effect>) -> UpdateClosure<Model, Event, Effect> {
35+
return { model, event in
36+
self.willUpdate(model: model, event: event)
37+
let result = invokeUpdate(update, model: model, event: event)
38+
self.didUpdate(model: model, event: event, next: result)
39+
return result
40+
}
4441
}
4542

46-
@inline(never)
47-
@_silgen_name("__MOBIUS_IS_CALLING_AN_INITIATOR_FUNCTION__")
48-
private func invokeInitiate(model: Model) -> First {
49-
return realInit(model)
43+
/// Wraps an Update in logging calls and stack annotations
44+
func wrap(update: Update<Model, Event, Effect>) -> Update<Model, Event, Effect> {
45+
return Update(wrap(update: update.updateClosure))
5046
}
5147
}
5248

53-
extension Update {
54-
/// Helper to wrap update functions with log calls.
55-
///
56-
/// Also adds call stack annotation where we call into the client-provided update.
57-
@inline(never)
58-
@_silgen_name("__MOBIUS_IS_CALLING_AN_UPDATE_FUNCTION__")
59-
func logging<L: MobiusLogger>(_ logger: L) -> Update where L.Model == Model, L.Event == Event, L.Effect == Effect {
60-
return Update { model, event in
61-
logger.willUpdate(model: model, event: event)
62-
let next = self.update(model: model, event: event)
63-
logger.didUpdate(model: model, event: event, next: next)
64-
return next
65-
}
66-
}
49+
/// Invoke an initiate function, leaving a hint on the stack.
50+
///
51+
/// To work as intended, this function must be exactly like this. `@_silgen_name` can’t be used on a closure,
52+
/// for example.
53+
@inline(never)
54+
@_silgen_name("__MOBIUS_IS_CALLING_AN_INITIATOR_FUNCTION__")
55+
private func invokeInitiate<Model, Effect>(_ initiate: Initiate<Model, Effect>, model: Model) -> First<Model, Effect> {
56+
return initiate(model)
57+
}
58+
59+
/// Invoke an update function, leaving a hint on the stack.
60+
///
61+
/// To work as intended, this function must be exactly like this. `@_silgen_name` can’t be used on a closure,
62+
/// for example.
63+
@inline(never)
64+
@_silgen_name("__MOBIUS_IS_CALLING_AN_UPDATE_FUNCTION__")
65+
private func invokeUpdate<Model, Event, Effect>(
66+
_ update: @escaping (Model, Event) -> Next<Model, Effect>,
67+
model: Model,
68+
event: Event
69+
) -> Next<Model, Effect> {
70+
return update(model, event)
6771
}

MobiusCore/Source/Mobius.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,17 @@ import Foundation
3434
///
3535
/// [update function]: https://github.com/spotify/Mobius.swift/wiki/Concepts#update-function
3636
public struct Update<Model, Event, Effect> {
37-
private let update: (Model, Event) -> Next<Model, Effect>
37+
@usableFromInline let updateClosure: (Model, Event) -> Next<Model, Effect>
3838

3939
/// Creates an `Update` struct wrapping the provided function.
4040
public init(_ update: @escaping (Model, Event) -> Next<Model, Effect>) {
41-
self.update = update
41+
self.updateClosure = update
4242
}
4343

4444
/// Invokes the update function.
45+
@inlinable
4546
public func update(model: Model, event: Event) -> Next<Model, Effect> {
46-
return self.update(model, event)
47+
return updateClosure(model, event)
4748
}
4849

4950
/// Invokes the update function.

MobiusCore/Source/MobiusController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public final class MobiusController<Model, Event, Effect> {
129129
// Wrap initiator (if any) in a logger
130130
let actualInitiate: Initiate<Model, Effect>
131131
if let initiate = initiate {
132-
actualInitiate = LoggingInitiate(initiate, logger: logger).initiate
132+
actualInitiate = logger.wrap(initiate: initiate)
133133
} else {
134134
actualInitiate = { First(model: $0) }
135135
}

MobiusCore/Source/MobiusLoop.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public final class MobiusLoop<Model, Event, Effect>: Disposable, CustomDebugStri
117117
logger: AnyMobiusLogger<Model, Event, Effect>
118118
) -> MobiusLoop where EffectHandler.Input == Effect, EffectHandler.Output == Event {
119119
let accessGuard = ConcurrentAccessDetector()
120-
let loggingUpdate = update.logging(logger)
120+
let loggingUpdate = logger.wrap(update: update)
121121
let workBag = WorkBag(accessGuard: accessGuard)
122122

123123
// create somewhere for the event processor to push nexts to; later, we'll observe these nexts and

MobiusCore/Test/LoggingInitiateTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,21 @@ class LoggingInitiateTests: QuickSpec {
2525
override func spec() {
2626
describe("LoggingInitiate") {
2727
var logger: TestMobiusLogger!
28-
var loggingInitiate: LoggingInitiate<String, String>!
28+
var loggingInitiate: Initiate<String, String>!
2929

3030
beforeEach {
3131
logger = TestMobiusLogger()
32-
loggingInitiate = LoggingInitiate({ model in First(model: model) }, logger: logger)
32+
loggingInitiate = logger.wrap { model in First(model: model) }
3333
}
3434

3535
it("should log willInitiate and didInitiate for each initiate attempt") {
36-
_ = loggingInitiate.initiate("from this")
36+
_ = loggingInitiate("from this")
3737

3838
expect(logger.logMessages).to(equal(["willInitiate(from this)", "didInitiate(from this, First<String, String>(model: \"from this\", effects: []))"]))
3939
}
4040

4141
it("should return init from delegate") {
42-
let first = loggingInitiate.initiate("hey")
42+
let first = loggingInitiate("hey")
4343

4444
expect(first.model).to(equal("hey"))
4545
expect(first.effects).to(beEmpty())

MobiusCore/Test/LoggingUpdateTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class LoggingUpdateTests: QuickSpec {
2929

3030
beforeEach {
3131
logger = TestMobiusLogger()
32-
loggingUpdate = Update { model, event in Next(model: model, effects: [event]) }.logging(logger)
32+
loggingUpdate = logger.wrap(update: Update { model, event in Next(model: model, effects: [event]) })
3333
}
3434

3535
it("should log willUpdate and didUpdate for each update attempt") {

0 commit comments

Comments
 (0)