Skip to content

Commit 2a61981

Browse files
committed
Fixing a race condition in the scheduler. THis fixes an issue where the sub components of the SDK would get updated before the request for the user's connections would finish
1 parent 26fbf74 commit 2a61981

File tree

4 files changed

+79
-7
lines changed

4 files changed

+79
-7
lines changed

IFTTT SDK/ConnectionsMonitor.swift

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,19 @@ class ConnectionsMonitor: SynchronizationSubscriber {
112112

113113
/// Defines a cancellable `Operation` subclass to allow for cancellable network requests.
114114
private class CancellableNetworkOperation: Operation {
115+
/// The current in-flight network request
115116
private var task: URLSessionDataTask? = nil
117+
118+
/// The completion closure to invoke once the request is completed
116119
private let completion: ConnectionNetworkController.ConnectionResponseClosure
120+
121+
/// The network controller to perform requests with
117122
private weak var networkController: ConnectionNetworkController?
123+
124+
/// The request to perform
118125
private let request: Connection.Request
119126

127+
/// Creates an instance of `CancellableNetworkOperation`.
120128
init(networkController: ConnectionNetworkController,
121129
request: Connection.Request,
122130
_ completion: @escaping ConnectionNetworkController.ConnectionResponseClosure) {
@@ -125,17 +133,83 @@ private class CancellableNetworkOperation: Operation {
125133
self.completion = completion
126134
}
127135

136+
/// State for this operation.
137+
@objc private enum OperationState: Int {
138+
case ready
139+
case executing
140+
case finished
141+
}
142+
143+
/// Concurrent queue for synchronizing access to `state`.
144+
private let stateQueue = DispatchQueue(label: "com.ifttt.connections_monitor.rw.state", attributes: .concurrent)
145+
146+
/// Private backing stored property for `state`.
147+
private var _state: OperationState = .ready
148+
149+
/// The state of the operation
150+
@objc private dynamic var state: OperationState {
151+
get {
152+
return stateQueue.sync { _state }
153+
}
154+
set {
155+
stateQueue.async(flags: .barrier) { self._state = newValue }
156+
}
157+
}
158+
159+
override var isReady: Bool {
160+
return state == .ready && super.isReady
161+
}
162+
163+
override var isExecuting: Bool {
164+
return state == .executing
165+
}
166+
167+
override var isFinished: Bool {
168+
return state == .finished
169+
}
170+
171+
override var isAsynchronous: Bool {
172+
return true
173+
}
174+
175+
open override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
176+
let keys = ["isReady", "isFinished", "isExecuting"]
177+
if keys.contains(key) {
178+
return .init(arrayLiteral: #keyPath(state))
179+
}
180+
181+
return super.keyPathsForValuesAffectingValue(forKey: key)
182+
}
183+
184+
override func start() {
185+
if isCancelled {
186+
state = .finished
187+
return
188+
}
189+
190+
state = .executing
191+
192+
main()
193+
}
194+
128195
override func main() {
129-
task = networkController?.start(request: request, completion: { response in
196+
task = networkController?.start(request: request, completion: { [weak self] response in
197+
guard let self = self else { return }
130198
self.completion(response)
131-
self.completionBlock?()
199+
self.finish()
132200
})
133201
task?.resume()
134202
}
135203

136204
override func cancel() {
137-
super.cancel()
138205
task?.cancel()
206+
finish()
139207
}
140208

209+
/// Call this method to finish the operation
210+
private func finish() {
211+
if !isFinished {
212+
state = .finished
213+
}
214+
}
141215
}

IFTTT SDK/ConnectionsSynchronizer.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,6 @@ final class ConnectionsSynchronizer {
184184

185185
/// Call this to stop the synchronization completely. Safe to be called multiple times.
186186
private func stop() {
187-
if state == .stopped { return }
188-
189187
stopNotifications()
190188
Keychain.resetIfNecessary(force: true)
191189
scheduler.stop()

IFTTT SDK/Keychain.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ final class Keychain {
9595
}
9696

9797
private class func reset() {
98-
Key.AllCases().forEach {
98+
Key.allCases.forEach {
9999
removeValue(for: $0)
100100
}
101101
}

IFTTTConnectSDK.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |spec|
22
spec.name = "IFTTTConnectSDK"
3-
spec.version = "2.5.8"
3+
spec.version = "2.5.9"
44
spec.summary = "Allows your users to activate programmable IFTTT Connections directly in your app."
55
spec.description = <<-DESC
66
- Easily authenticate your services to IFTTT through the Connect Button

0 commit comments

Comments
 (0)