Skip to content

Commit d8c70ac

Browse files
committed
Added TaskQueue and used it in NativePusher.
Made use of connection delegate debugLog function in NativePusher
1 parent 5c5fdd5 commit d8c70ac

File tree

4 files changed

+312
-64
lines changed

4 files changed

+312
-64
lines changed

PusherSwift/PusherSwift.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
/* Begin PBXBuildFile section */
1010
330D7A6A1CAEDA6C0032FF2C /* PusherSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33831CD31A9CFFD900B124F1 /* PusherSwift.swift */; };
1111
330D7A6D1CAEDA750032FF2C /* PusherChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3384C1B81CAECD9C00F10796 /* PusherChannel.swift */; };
12+
33160A001D8FE15B002EC524 /* TaskQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331609FF1D8FE15B002EC524 /* TaskQueue.swift */; };
1213
3341A33A1D819FBC007191AD /* NativePusher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3341A3391D819FBC007191AD /* NativePusher.swift */; };
1314
3342F3BD1D808AC900C0296E /* ClientEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3342F3BB1D808AC500C0296E /* ClientEventTests.swift */; };
1415
335BCA0D1C38ABF000B47A66 /* PusherSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 33831CD61A9CFFF200B124F1 /* PusherSwift.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -47,6 +48,7 @@
4748
/* End PBXContainerItemProxy section */
4849

4950
/* Begin PBXFileReference section */
51+
331609FF1D8FE15B002EC524 /* TaskQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskQueue.swift; sourceTree = "<group>"; };
5052
3341A3391D819FBC007191AD /* NativePusher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NativePusher.swift; sourceTree = "<group>"; };
5153
3342F3BB1D808AC500C0296E /* ClientEventTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ClientEventTests.swift; path = ../Tests/ClientEventTests.swift; sourceTree = "<group>"; };
5254
337C9EE01C38B708006CC757 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
@@ -172,6 +174,7 @@
172174
33C0D2D41CB5C1F2003FE13E /* CryptoSwiftHMACModule.swift */,
173175
33C0D2DA1CB5C364003FE13E /* Starscream.swift */,
174176
33C0D2E31CB5C539003FE13E /* Reachability.swift */,
177+
331609FF1D8FE15B002EC524 /* TaskQueue.swift */,
175178
);
176179
name = Dependencies;
177180
sourceTree = "<group>";
@@ -326,6 +329,7 @@
326329
3389F5721CAEDDF300563F49 /* PusherChannels.swift in Sources */,
327330
3389F5761CAEDE2800563F49 /* PusherGlobalChannel.swift in Sources */,
328331
3389F57A1CAEDEC800563F49 /* PusherClientOptions.swift in Sources */,
332+
33160A001D8FE15B002EC524 /* TaskQueue.swift in Sources */,
329333
3389F56E1CAEDDD800563F49 /* PusherPresenceChannel.swift in Sources */,
330334
3341A33A1D819FBC007191AD /* NativePusher.swift in Sources */,
331335
33C0D2E41CB5C539003FE13E /* Reachability.swift in Sources */,

Source/NativePusher.swift

Lines changed: 90 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,31 @@
2525
private let LIBRARY_NAME_AND_VERSION = "pusher-websocket-swift " + VERSION
2626

2727
private let URLSession = Foundation.URLSession.shared
28-
private var failedNativeServiceRequests: Int = 0
28+
private var failedRequestAttempts: Int = 0
2929
private let maxFailedRequestAttempts: Int = 6
3030

31+
internal var socketConnection: PusherConnection? = nil
32+
33+
private var requestQueue = TaskQueue()
34+
3135
/**
32-
Identifies a Pusher app.
33-
This app should have push notifications enabled.
36+
Identifies a Pusher app, which should have push notifications enabled
37+
and a certificate added for the push notifications to work.
3438
*/
3539
private var pusherAppKey: String? = nil
3640

41+
/**
42+
The id issued to this app instance by Pusher, which is received upon
43+
registrations. It's used to identify a client when subscribe /
44+
unsubscribe requests are made.
45+
*/
46+
private var clientId: String? = nil
47+
48+
/**
49+
Normal clients should access the shared instance via Pusher.nativePusher().
50+
*/
51+
private override init() {}
52+
3753
/**
3854
Sets the pusherAppKey property and then attempts to flush
3955
the outbox of any pending requests
@@ -42,25 +58,9 @@
4258
*/
4359
open func setPusherAppKey(pusherAppKey: String) {
4460
self.pusherAppKey = pusherAppKey
45-
tryFlushOutbox()
61+
requestQueue.run()
4662
}
4763

48-
/**
49-
The id issued to this app instance by Pusher.
50-
We get it upon registration.
51-
We use it to identify ourselves when subscribing/unsubscribing.
52-
*/
53-
private var clientId: String? = nil
54-
55-
/**
56-
Queued actions to perform when the client is registered.
57-
*/
58-
private var outbox: [(String, SubscriptionChange)] = []
59-
60-
/**
61-
Normal clients should access the shared instance via Pusher.nativePusher().
62-
*/
63-
private override init() {}
6464

6565
/**
6666
Makes device token presentable to server
@@ -101,8 +101,6 @@
101101
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
102102
request.addValue(LIBRARY_NAME_AND_VERSION, forHTTPHeaderField: "X-Pusher-Library" )
103103

104-
105-
106104
let task = URLSession.dataTask(with: request, completionHandler: { data, response, error in
107105
if let httpResponse = response as? HTTPURLResponse,
108106
(httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
@@ -124,20 +122,21 @@
124122
if let clientIdJson = json["id"] {
125123
if let clientId = clientIdJson as? String {
126124
self.clientId = clientId
127-
self.tryFlushOutbox()
125+
self.socketConnection?.delegate?.debugLog?(message: "Successfully registered for push notifications and got clientId: \(clientId)")
126+
self.requestQueue.run()
128127
} else {
129-
print("Value at \"id\" key in JSON response was not a string: \(json)")
128+
self.socketConnection?.delegate?.debugLog?(message: "Value at \"id\" key in JSON response was not a string: \(json)")
130129
}
131130
} else {
132-
print("No \"id\" key in JSON response: \(json)")
131+
self.socketConnection?.delegate?.debugLog?(message: "No \"id\" key in JSON response: \(json)")
133132
}
134133
} else {
135-
print("Could not parse body as JSON object: \(data)")
134+
self.socketConnection?.delegate?.debugLog?(message: "Could not parse body as JSON object: \(data)")
136135
}
137136
} else {
138137
if data != nil && response != nil {
139138
let responseBody = String(data: data!, encoding: .utf8)
140-
print("Bad HTTP response: \(response!) with body: \(responseBody)")
139+
self.socketConnection?.delegate?.debugLog?(message: "Bad HTTP response: \(response!) with body: \(responseBody)")
141140
}
142141
}
143142
})
@@ -151,8 +150,7 @@
151150
- parameter interestName: the name of the interest you want to subscribe to
152151
*/
153152
open func subscribe(interestName: String) {
154-
outbox.append(interestName, SubscriptionChange.subscribe)
155-
tryFlushOutbox()
153+
addSubscriptionChangeToTaskQueue(interestName: interestName, change: .subscribe)
156154
}
157155

158156
/**
@@ -162,25 +160,27 @@
162160
from
163161
*/
164162
open func unsubscribe(interestName: String) {
165-
outbox.append(interestName, SubscriptionChange.unsubscribe)
166-
tryFlushOutbox()
163+
addSubscriptionChangeToTaskQueue(interestName: interestName, change: .unsubscribe)
167164
}
168165

169166
/**
170-
Attempts to flush the outbox by making the appropriate requests to either
171-
subscribe to or unsubscribe from an interest
167+
Adds subscribe / unsubscribe tasts to task queue
168+
169+
- parameter interestName: the name of the interest you want to interact with
170+
- parameter change: specifies whether the change is to subscribe or
171+
unsubscribe
172+
172173
*/
173-
private func tryFlushOutbox() {
174-
switch (self.pusherAppKey, self.clientId) {
175-
case (.some(let pusherAppKey), .some(let clientId)):
176-
if (0 < outbox.count) {
177-
let (interest, change) = outbox.remove(at: 0)
178-
modifySubscription(pusherAppKey: pusherAppKey, clientId: clientId, interest: interest, change: change) {
179-
self.tryFlushOutbox()
180-
}
181-
}
182-
case _: break
174+
private func addSubscriptionChangeToTaskQueue(interestName: String, change: SubscriptionChange) {
175+
requestQueue.tasks += { _, next in
176+
self.modifySubscription(
177+
interest: interestName,
178+
change: change,
179+
successCallback: next
180+
)
183181
}
182+
183+
requestQueue.run()
184184
}
185185

186186
/**
@@ -193,60 +193,86 @@
193193
- parameter change: Whether to subscribe or unsubscribe
194194
- parameter callback: Callback to be called upon success
195195
*/
196-
private func modifySubscription(pusherAppKey: String, clientId: String, interest: String, change: SubscriptionChange, callback: @escaping (Void) -> (Void)) {
197-
let url = "\(CLIENT_API_V1_ENDPOINT)/clients/\(clientId)/interests/\(interest)"
198-
var request = URLRequest(url: URL(string: url)!)
199-
switch (change) {
200-
case .subscribe:
201-
request.httpMethod = "POST"
202-
case .unsubscribe:
203-
request.httpMethod = "DELETE"
196+
private func modifySubscription(interest: String, change: SubscriptionChange, successCallback: @escaping (Any?) -> Void) {
197+
guard pusherAppKey != nil, clientId != nil else {
198+
self.socketConnection?.delegate?.debugLog?(message: "pusherAppKey \(pusherAppKey) or clientId \(clientId) not set - will retry in 1 second")
199+
return self.requestQueue.retry(1)
204200
}
205201

206-
let params: [String: Any] = ["app_key": pusherAppKey]
202+
self.socketConnection?.delegate?.debugLog?(message: "Attempt number: \(self.failedRequestAttempts + 1) of \(maxFailedRequestAttempts)")
203+
204+
let url = "\(CLIENT_API_V1_ENDPOINT)/clients/\(clientId!)/interests/\(interest)"
205+
var request = URLRequest(url: URL(string: url)!)
206+
request.httpMethod = change.httpMethod()
207207

208+
let params: [String: Any] = ["app_key": pusherAppKey!]
208209
try! request.httpBody = JSONSerialization.data(withJSONObject: params, options: [])
210+
209211
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
210212
request.addValue(LIBRARY_NAME_AND_VERSION, forHTTPHeaderField: "X-Pusher-Library")
211213

212214
let task = URLSession.dataTask(
213215
with: request,
214216
completionHandler: { data, response, error in
215217
guard let httpResponse = response as? HTTPURLResponse,
216-
(200 <= httpResponse.statusCode && httpResponse.statusCode < 300) ||
218+
(200 <= httpResponse.statusCode && httpResponse.statusCode < 300) &&
217219
error == nil
218220
else {
219-
self.outbox.insert((interest, change), at: 0)
221+
self.failedRequestAttempts += 1
220222

221223
if error != nil {
222-
print("Error when trying to modify subscription to interest: \(error?.localizedDescription)")
224+
self.socketConnection?.delegate?.debugLog?(message: "Error when trying to modify subscription to interest: \(error?.localizedDescription)")
225+
} else if data != nil && response != nil {
226+
let responseBody = String(data: data!, encoding: .utf8)
227+
self.socketConnection?.delegate?.debugLog?(message: "Bad response from server: \(response!) with body: \(responseBody)")
223228
} else {
224-
print("Bad response from server when trying to modify subscription to interest " + interest)
229+
self.socketConnection?.delegate?.debugLog?(message: "Bad response from server when trying to modify subscription to interest: \(interest)")
225230
}
226-
self.failedNativeServiceRequests += 1
227231

228-
if (self.failedNativeServiceRequests < self.maxFailedRequestAttempts) {
229-
callback()
232+
if self.failedRequestAttempts > self.maxFailedRequestAttempts {
233+
self.socketConnection?.delegate?.debugLog?(message: "Max number of failed native service requests reached")
234+
235+
self.requestQueue.paused = true
230236
} else {
231-
print("Max number of failed native service requests reached")
237+
self.socketConnection?.delegate?.debugLog?(message: "Retrying subscription modification request for interest: \(interest)")
238+
self.requestQueue.retry(Double(self.failedRequestAttempts * self.failedRequestAttempts))
232239
}
240+
233241
return
234242
}
235243

236-
// Reset number of failed requests to 0 upon success
237-
self.failedNativeServiceRequests = 0
244+
self.socketConnection?.delegate?.debugLog?(message: "Success making \(change.stringValue) to \(interest)")
238245

239-
callback()
246+
self.failedRequestAttempts = 0
247+
successCallback(nil)
240248
}
241249
)
242250

243251
task.resume()
244252
}
245253
}
246254

247-
private enum SubscriptionChange {
255+
internal enum SubscriptionChange {
248256
case subscribe
249257
case unsubscribe
258+
259+
internal func stringValue() -> String {
260+
switch self {
261+
case .subscribe:
262+
return "subscribe"
263+
case .unsubscribe:
264+
return "unsubscribe"
265+
}
266+
}
267+
268+
internal func httpMethod() -> String {
269+
switch self {
270+
case .subscribe:
271+
return "POST"
272+
case .unsubscribe:
273+
return "DELETE"
274+
}
275+
}
250276
}
251277

252278
#endif

Source/PusherSwift.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ let CLIENT_NAME = "pusher-websocket-swift"
3232
connection = PusherConnection(key: key, socket: ws, url: urlString, options: options)
3333
connection.createGlobalChannel()
3434
nativePusher.setPusherAppKey(pusherAppKey: key)
35+
nativePusher.socketConnection = connection
3536
}
3637

3738
#endif

0 commit comments

Comments
 (0)