@@ -41,25 +41,27 @@ extension LifecycleTask {
41
41
42
42
/// Supported startup and shutdown method styles
43
43
public struct LifecycleHandler {
44
+ @available ( * , deprecated, renamed: " Handler " )
44
45
public typealias Callback = ( @escaping ( Error ? ) -> Void ) -> Void
45
46
46
- private let body : Callback ?
47
+ public typealias Handler = ( @escaping ( Error ? ) -> Void ) -> Void
48
+
49
+ private let underlying : Handler ?
47
50
48
51
/// Initialize a `LifecycleHandler` based on a completion handler.
49
52
///
50
53
/// - parameters:
51
- /// - callback: the underlying completion handler
52
- /// - noop: the underlying completion handler is a no-op
53
- public init ( _ callback: Callback ? ) {
54
- self . body = callback
54
+ /// - handler: the underlying completion handler
55
+ public init ( _ handler: Handler ? ) {
56
+ self . underlying = handler
55
57
}
56
58
57
59
/// Asynchronous `LifecycleHandler` based on a completion handler.
58
60
///
59
61
/// - parameters:
60
- /// - callback : the underlying completion handler
61
- public static func async ( _ callback : @escaping Callback ) -> LifecycleHandler {
62
- return LifecycleHandler ( callback )
62
+ /// - handler : the underlying async handler
63
+ public static func async ( _ handler : @escaping Handler ) -> LifecycleHandler {
64
+ return LifecycleHandler ( handler )
63
65
}
64
66
65
67
/// Asynchronous `LifecycleHandler` based on a blocking, throwing function.
@@ -82,15 +84,101 @@ public struct LifecycleHandler {
82
84
return LifecycleHandler ( nil )
83
85
}
84
86
85
- internal func run( _ callback : @escaping ( Error ? ) -> Void ) {
86
- let body = self . body ?? { callback in
87
+ internal func run( _ completionHandler : @escaping ( Error ? ) -> Void ) {
88
+ let body = self . underlying ?? { callback in
87
89
callback ( nil )
88
90
}
89
- body ( callback )
91
+ body ( completionHandler )
90
92
}
91
93
92
94
internal var noop : Bool {
93
- return self . body == nil
95
+ return self . underlying == nil
96
+ }
97
+ }
98
+
99
+ // MARK: - Stateful Lifecycle Handlers
100
+
101
+ /// LifecycleHandler for starting stateful tasks. The state can then be fed into a LifecycleShutdownHandler
102
+ public struct LifecycleStartHandler < State> {
103
+ public typealias Handler = ( @escaping ( Result < State , Error > ) -> Void ) -> Void
104
+
105
+ private let underlying : Handler
106
+
107
+ /// Initialize a `LifecycleHandler` based on a completion handler.
108
+ ///
109
+ /// - parameters:
110
+ /// - callback: the underlying completion handler
111
+ public init ( _ handler: @escaping Handler ) {
112
+ self . underlying = handler
113
+ }
114
+
115
+ /// Asynchronous `LifecycleStartHandler` based on a completion handler.
116
+ ///
117
+ /// - parameters:
118
+ /// - handler: the underlying async handler
119
+ public static func async ( _ handler: @escaping Handler ) -> LifecycleStartHandler {
120
+ return LifecycleStartHandler ( handler)
121
+ }
122
+
123
+ /// Asynchronous `LifecycleStartHandler` based on a blocking, throwing function.
124
+ ///
125
+ /// - parameters:
126
+ /// - body: the underlying function
127
+ public static func sync( _ body: @escaping ( ) throws -> State ) -> LifecycleStartHandler {
128
+ return LifecycleStartHandler { completionHandler in
129
+ do {
130
+ let state = try body ( )
131
+ completionHandler ( . success( state) )
132
+ } catch {
133
+ completionHandler ( . failure( error) )
134
+ }
135
+ }
136
+ }
137
+
138
+ internal func run( _ completionHandler: @escaping ( Result < State , Error > ) -> Void ) {
139
+ self . underlying ( completionHandler)
140
+ }
141
+ }
142
+
143
+ /// LifecycleHandler for shutting down stateful tasks. The state comes from a LifecycleStartHandler
144
+ public struct LifecycleShutdownHandler < State> {
145
+ public typealias Handler = ( State , @escaping ( Error ? ) -> Void ) -> Void
146
+
147
+ private let underlying : Handler
148
+
149
+ /// Initialize a `LifecycleShutdownHandler` based on a completion handler.
150
+ ///
151
+ /// - parameters:
152
+ /// - handler: the underlying completion handler
153
+ public init ( _ handler: @escaping Handler ) {
154
+ self . underlying = handler
155
+ }
156
+
157
+ /// Asynchronous `LifecycleShutdownHandler` based on a completion handler.
158
+ ///
159
+ /// - parameters:
160
+ /// - handler: the underlying async handler
161
+ public static func async ( _ handler: @escaping Handler ) -> LifecycleShutdownHandler {
162
+ return LifecycleShutdownHandler ( handler)
163
+ }
164
+
165
+ /// Asynchronous `LifecycleShutdownHandler` based on a blocking, throwing function.
166
+ ///
167
+ /// - parameters:
168
+ /// - body: the underlying function
169
+ public static func sync( _ body: @escaping ( State ) throws -> Void ) -> LifecycleShutdownHandler {
170
+ return LifecycleShutdownHandler { state, completionHandler in
171
+ do {
172
+ try body ( state)
173
+ completionHandler ( nil )
174
+ } catch {
175
+ completionHandler ( error)
176
+ }
177
+ }
178
+ }
179
+
180
+ internal func run( state: State , _ completionHandler: @escaping ( Error ? ) -> Void ) {
181
+ self . underlying ( state, completionHandler)
94
182
}
95
183
}
96
184
@@ -516,6 +604,16 @@ extension LifecycleTasksContainer {
516
604
public func registerShutdown( label: String , _ handler: LifecycleHandler ) {
517
605
self . register ( label: label, start: . none, shutdown: handler)
518
606
}
607
+
608
+ /// Adds a stateful `LifecycleTask` to a `LifecycleTasks` collection.
609
+ ///
610
+ /// - parameters:
611
+ /// - label: label of the item, useful for debugging.
612
+ /// - start: `LifecycleStartHandler` to perform the startup and return the state.
613
+ /// - shutdown: `LifecycleShutdownHandler` to perform the shutdown given the state.
614
+ public func registerStateful< State> ( label: String , start: LifecycleStartHandler < State > , shutdown: LifecycleShutdownHandler < State > ) {
615
+ self . register ( StatefulLifecycleTask ( label: label, start: start, shutdown: shutdown) )
616
+ }
519
617
}
520
618
521
619
internal struct _LifecycleTask : LifecycleTask {
@@ -539,3 +637,42 @@ internal struct _LifecycleTask: LifecycleTask {
539
637
self . shutdown. run ( callback)
540
638
}
541
639
}
640
+
641
+ internal class StatefulLifecycleTask < State> : LifecycleTask {
642
+ let label : String
643
+ let shutdownIfNotStarted : Bool = false
644
+ let start : LifecycleStartHandler < State >
645
+ let shutdown : LifecycleShutdownHandler < State >
646
+
647
+ let stateLock = Lock ( )
648
+ var state : State ?
649
+
650
+ init ( label: String , start: LifecycleStartHandler < State > , shutdown: LifecycleShutdownHandler < State > ) {
651
+ self . label = label
652
+ self . start = start
653
+ self . shutdown = shutdown
654
+ }
655
+
656
+ func start( _ callback: @escaping ( Error ? ) -> Void ) {
657
+ self . start. run { result in
658
+ switch result {
659
+ case . failure( let error) :
660
+ callback ( error)
661
+ case . success( let state) :
662
+ self . stateLock. withLock {
663
+ self . state = state
664
+ }
665
+ callback ( nil )
666
+ }
667
+ }
668
+ }
669
+
670
+ func shutdown( _ callback: @escaping ( Error ? ) -> Void ) {
671
+ guard let state = ( self . stateLock. withLock { self . state } ) else {
672
+ return callback ( UnknownState ( ) )
673
+ }
674
+ self . shutdown. run ( state: state, callback)
675
+ }
676
+
677
+ struct UnknownState : Error { }
678
+ }
0 commit comments