Skip to content

Commit 2f7f8c4

Browse files
committed
Add PusherDelegate.
Add NativePusherTests. Restructure how the MockSession works in Mocks.swift so that it can better handle multiple “stubbed” requests in a single test.
1 parent dd72e4b commit 2f7f8c4

File tree

6 files changed

+185
-19
lines changed

6 files changed

+185
-19
lines changed

PusherSwift/PusherSwift.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
3389F57A1CAEDEC800563F49 /* PusherClientOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3389F5791CAEDEC800563F49 /* PusherClientOptions.swift */; };
2222
33A962741D88A9C900DA421E /* PusherConnectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33A962731D88A9C800DA421E /* PusherConnectionDelegate.swift */; };
2323
33A962771D89483600DA421E /* PusherConnectionDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33A962751D8943CA00DA421E /* PusherConnectionDelegateTests.swift */; };
24+
33BA541E1D90351B00CD853B /* NativePusherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BA541D1D90351B00CD853B /* NativePusherTests.swift */; };
25+
33BA54201D9035BD00CD853B /* PusherDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BA541F1D9035BD00CD853B /* PusherDelegate.swift */; };
2426
33BB99731D21230100B25C2A /* AuthenticationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BB995D1D21226C00B25C2A /* AuthenticationTests.swift */; };
2527
33BB99751D21230100B25C2A /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BB995F1D21226C00B25C2A /* Helpers.swift */; };
2628
33BB99761D21230100B25C2A /* Mocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BB99601D21226C00B25C2A /* Mocks.swift */; };
@@ -66,6 +68,8 @@
6668
3389F5791CAEDEC800563F49 /* PusherClientOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PusherClientOptions.swift; sourceTree = "<group>"; };
6769
33A962731D88A9C800DA421E /* PusherConnectionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PusherConnectionDelegate.swift; sourceTree = "<group>"; };
6870
33A962751D8943CA00DA421E /* PusherConnectionDelegateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PusherConnectionDelegateTests.swift; path = ../Tests/PusherConnectionDelegateTests.swift; sourceTree = "<group>"; };
71+
33BA541D1D90351B00CD853B /* NativePusherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NativePusherTests.swift; path = ../Tests/NativePusherTests.swift; sourceTree = "<group>"; };
72+
33BA541F1D9035BD00CD853B /* PusherDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PusherDelegate.swift; sourceTree = "<group>"; };
6973
33BB995D1D21226C00B25C2A /* AuthenticationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AuthenticationTests.swift; path = ../Tests/AuthenticationTests.swift; sourceTree = "<group>"; };
7074
33BB995F1D21226C00B25C2A /* Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Helpers.swift; path = ../Tests/Helpers.swift; sourceTree = "<group>"; };
7175
33BB99601D21226C00B25C2A /* Mocks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Mocks.swift; path = ../Tests/Mocks.swift; sourceTree = "<group>"; };
@@ -124,6 +128,7 @@
124128
children = (
125129
33831CD31A9CFFD900B124F1 /* PusherSwift.swift */,
126130
3389F5791CAEDEC800563F49 /* PusherClientOptions.swift */,
131+
33BA541F1D9035BD00CD853B /* PusherDelegate.swift */,
127132
3389F5651CAEDD5F00563F49 /* PusherConnection.swift */,
128133
33A962731D88A9C800DA421E /* PusherConnectionDelegate.swift */,
129134
3389F5691CAEDD9100563F49 /* PusherWebsocketDelegate.swift */,
@@ -163,6 +168,7 @@
163168
33A962751D8943CA00DA421E /* PusherConnectionDelegateTests.swift */,
164169
33BB99651D21226C00B25C2A /* PusherIncomingEventHandlingTests.swift */,
165170
33BB99661D21226C00B25C2A /* PusherTopLevelAPITests.swift */,
171+
33BA541D1D90351B00CD853B /* NativePusherTests.swift */,
166172
33BB99671D21226C00B25C2A /* Info.plist */,
167173
);
168174
name = Tests;
@@ -323,6 +329,7 @@
323329
buildActionMask = 2147483647;
324330
files = (
325331
3389F5661CAEDD5F00563F49 /* PusherConnection.swift in Sources */,
332+
33BA54201D9035BD00CD853B /* PusherDelegate.swift in Sources */,
326333
3389F56A1CAEDD9100563F49 /* PusherWebsocketDelegate.swift in Sources */,
327334
330D7A6D1CAEDA750032FF2C /* PusherChannel.swift in Sources */,
328335
33A962741D88A9C900DA421E /* PusherConnectionDelegate.swift in Sources */,
@@ -351,6 +358,7 @@
351358
33BB997B1D21230100B25C2A /* PusherIncomingEventHandlingTests.swift in Sources */,
352359
3342F3BD1D808AC900C0296E /* ClientEventTests.swift in Sources */,
353360
33BB99751D21230100B25C2A /* Helpers.swift in Sources */,
361+
33BA541E1D90351B00CD853B /* NativePusherTests.swift in Sources */,
354362
33BB99731D21230100B25C2A /* AuthenticationTests.swift in Sources */,
355363
33BB99781D21230100B25C2A /* PusherChannelTests.swift in Sources */,
356364
33BB99761D21230100B25C2A /* Mocks.swift in Sources */,

Source/NativePusher.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@
2424
private let CLIENT_API_V1_ENDPOINT = "https://nativepushclient-cluster1.pusher.com/client_api/v1"
2525
private let LIBRARY_NAME_AND_VERSION = "pusher-websocket-swift " + VERSION
2626

27-
private let URLSession = Foundation.URLSession.shared
27+
public var URLSession = Foundation.URLSession.shared
2828
private var failedRequestAttempts: Int = 0
2929
private let maxFailedRequestAttempts: Int = 6
3030

3131
internal var socketConnection: PusherConnection? = nil
32+
internal var pusher: Pusher? = nil
3233

3334
private var requestQueue = TaskQueue()
3435

@@ -61,12 +62,11 @@
6162
requestQueue.run()
6263
}
6364

64-
6565
/**
6666
Makes device token presentable to server
6767

6868
- parameter deviceToken: the deviceToken received when registering
69-
to receive push notifications, as NSData
69+
to receive push notifications, as Data
7070

7171
- returns: the deviceToken formatted as a String
7272
*/
@@ -79,12 +79,11 @@
7979
}
8080

8181
/**
82-
Registers this app instance with Pusher for push notifications.
82+
Registers (asynchronously) this app instance with Pusher for push notifications.
8383
This must be done before we can subscribe to interests.
84-
Registration happens asynchronously; any errors are reported by print statements.
8584

8685
- parameter deviceToken: the deviceToken received when registering
87-
to receive push notifications, as NSData
86+
to receive push notifications, as Data
8887
*/
8988
open func register(deviceToken: Data) {
9089
var request = URLRequest(url: URL(string: CLIENT_API_V1_ENDPOINT + "/clients")!)
@@ -122,6 +121,7 @@
122121
if let clientIdJson = json["id"] {
123122
if let clientId = clientIdJson as? String {
124123
self.clientId = clientId
124+
self.pusher?.delegate?.didRegisterForPushNotifications?(clientId: clientId)
125125
self.socketConnection?.delegate?.debugLog?(message: "Successfully registered for push notifications and got clientId: \(clientId)")
126126
self.requestQueue.run()
127127
} else {
@@ -241,6 +241,13 @@
241241
return
242242
}
243243

244+
switch change {
245+
case .subscribe:
246+
self.pusher?.delegate?.didSubscribeToInterest?(named: interest)
247+
case .unsubscribe:
248+
self.pusher?.delegate?.didUnsubscribeFromInterest?(named: interest)
249+
}
250+
244251
self.socketConnection?.delegate?.debugLog?(message: "Success making \(change.stringValue) to \(interest)")
245252

246253
self.failedRequestAttempts = 0

Source/PusherDelegate.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// PusherDelegate.swift
3+
// PusherSwift
4+
//
5+
// Created by Hamilton Chapman on 19/09/2016.
6+
//
7+
//
8+
9+
@objc public protocol PusherDelegate: class {
10+
@objc optional func didRegisterForPushNotifications(clientId: String)
11+
@objc optional func didSubscribeToInterest(named name: String)
12+
@objc optional func didUnsubscribeFromInterest(named name: String)
13+
}

Source/PusherSwift.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ let CLIENT_NAME = "pusher-websocket-swift"
1313

1414
@objc open class Pusher: NSObject {
1515
open let connection: PusherConnection
16+
open weak var delegate: PusherDelegate? = nil
1617
private let key: String
1718

1819
#if os(iOS)
@@ -33,6 +34,8 @@ let CLIENT_NAME = "pusher-websocket-swift"
3334
connection.createGlobalChannel()
3435
nativePusher.setPusherAppKey(pusherAppKey: key)
3536
nativePusher.socketConnection = connection
37+
super.init()
38+
nativePusher.pusher = self
3639
}
3740

3841
#endif

Tests/Mocks.swift

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -206,32 +206,46 @@ open class FunctionCall {
206206
}
207207
}
208208

209-
class MockSession: URLSession {
210-
var completionHandler: ((Data?, URLResponse?, NSError?) -> Void)?
209+
public typealias Response = (data: Data?, urlResponse: URLResponse?, error: NSError?)
211210

212-
static var mockResponse: (data: Data?, urlResponse: URLResponse?, error: NSError?) = (data: nil, urlResponse: nil, error: nil)
213-
override class var shared: URLSession {
211+
public class MockSession: URLSession {
212+
static public var mockResponses: [String: Response] = [:]
213+
static public var mockResponse: (data: Data?, urlResponse: URLResponse?, error: NSError?) = (data: nil, urlResponse: nil, error: nil)
214+
215+
override public class var shared: URLSession {
214216
get {
215217
return MockSession()
216218
}
217219
}
218220

219-
override func dataTask(with: URLRequest, completionHandler: @escaping(Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
220-
self.completionHandler = completionHandler
221-
return MockTask(response: MockSession.mockResponse, completionHandler: completionHandler)
221+
override public func dataTask(with: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
222+
var response: Response
223+
let mockedMethodAndUrlString = "\(with.httpMethod!)||\((with.url?.absoluteString)!)"
224+
225+
if let mockedResponse = MockSession.mockResponses[mockedMethodAndUrlString] {
226+
response = mockedResponse
227+
} else {
228+
response = MockSession.mockResponse
229+
}
230+
return MockTask(response: response, completionHandler: completionHandler)
231+
}
232+
233+
public class func addMockResponse(for url: URL, httpMethod: String, data: Data?, urlResponse: URLResponse?, error: NSError?) {
234+
let response = (data: data, urlResponse: urlResponse, error: error)
235+
let mockedResponseString = "\(httpMethod)||\(url.absoluteString)"
236+
mockResponses[mockedResponseString] = response
222237
}
223238

224-
class MockTask: URLSessionDataTask {
225-
typealias Response = (data: Data?, urlResponse: URLResponse?, error: NSError?)
226-
var mockResponse: Response
227-
let completionHandler: ((Data?, URLResponse?, NSError?) -> Void)?
239+
public class MockTask: URLSessionDataTask {
240+
public var mockResponse: Response
241+
public let completionHandler: ((Data?, URLResponse?, NSError?) -> Void)?
228242

229-
init(response: Response, completionHandler: ((Data?, URLResponse?, NSError?) -> Void)?) {
243+
public init(response: Response, completionHandler: ((Data?, URLResponse?, NSError?) -> Void)?) {
230244
self.mockResponse = response
231245
self.completionHandler = completionHandler
232246
}
233247

234-
override func resume() {
248+
override public func resume() {
235249
DispatchQueue.global(qos: .default).async {
236250
self.completionHandler!(self.mockResponse.data, self.mockResponse.urlResponse, self.mockResponse.error)
237251
}

Tests/NativePusherTests.swift

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//
2+
// NativePusherTests.swift
3+
// PusherSwift
4+
//
5+
// Created by Hamilton Chapman on 19/09/2016.
6+
//
7+
//
8+
9+
#if os(iOS)
10+
11+
import PusherSwift
12+
import XCTest
13+
14+
class NativePusherTests: XCTestCase {
15+
public class DummyDelegate: PusherDelegate {
16+
public var testClientId: String? = nil
17+
public var registerEx: XCTestExpectation? = nil
18+
public var subscribeEx: XCTestExpectation? = nil
19+
public var unsubscribeEx: XCTestExpectation? = nil
20+
public var interestName: String? = nil
21+
22+
public func didSubscribeToInterest(named name: String) {
23+
if interestName == name {
24+
subscribeEx!.fulfill()
25+
}
26+
}
27+
28+
public func didUnsubscribeFromInterest(named name: String) {
29+
if interestName == name {
30+
unsubscribeEx!.fulfill()
31+
}
32+
}
33+
34+
public func didRegisterForPushNotifications(clientId: String) {
35+
XCTAssertEqual(clientId, testClientId)
36+
registerEx!.fulfill()
37+
}
38+
}
39+
40+
var key: String!
41+
var pusher: Pusher!
42+
var socket: MockWebSocket!
43+
var dummyDelegate: DummyDelegate!
44+
var testClientId: String!
45+
46+
override func setUp() {
47+
super.setUp()
48+
49+
key = "testKey123"
50+
testClientId = "your_client_id"
51+
let options = PusherClientOptions(
52+
authMethod: AuthMethod.inline(secret: "secret"),
53+
autoReconnect: false
54+
)
55+
56+
pusher = Pusher(key: key, options: options)
57+
socket = MockWebSocket()
58+
socket.delegate = pusher.connection
59+
pusher.connection.socket = socket
60+
dummyDelegate = DummyDelegate()
61+
pusher.delegate = dummyDelegate
62+
63+
let jsonData = "{\"id\":\"\(testClientId!)\"}".data(using: String.Encoding.utf8, allowLossyConversion: false)!
64+
let url = URL(string: "https://nativepushclient-cluster1.pusher.com/client_api/v1/clients")!
65+
let urlResponse = HTTPURLResponse(url: url, statusCode: 201, httpVersion: nil, headerFields: nil)
66+
MockSession.addMockResponse(for: url, httpMethod: "POST", data: jsonData, urlResponse: urlResponse, error: nil)
67+
68+
let emptyJsonData = "".data(using: String.Encoding.utf8)!
69+
let subscriptionModificationUrl = URL(string: "https://nativepushclient-cluster1.pusher.com/client_api/v1/clients/\(testClientId!)/interests/donuts")!
70+
let susbcriptionModificationResponse = HTTPURLResponse(url: subscriptionModificationUrl, statusCode: 204, httpVersion: nil, headerFields: nil)
71+
let httpMethodForSubscribe = "POST"
72+
MockSession.addMockResponse(for: subscriptionModificationUrl, httpMethod: httpMethodForSubscribe, data: emptyJsonData, urlResponse: susbcriptionModificationResponse, error: nil)
73+
let httpMethodForUnsubscribe = "DELETE"
74+
MockSession.addMockResponse(for: subscriptionModificationUrl, httpMethod: httpMethodForUnsubscribe, data: emptyJsonData, urlResponse: susbcriptionModificationResponse, error: nil)
75+
76+
pusher.nativePusher().URLSession = MockSession.shared
77+
}
78+
79+
func testReceivingAClientIdAfterRegisterIsCalled() {
80+
let ex = expectation(description: "the clientId should be received when registration succeeds")
81+
dummyDelegate.testClientId = testClientId
82+
dummyDelegate.registerEx = ex
83+
84+
pusher.nativePusher().register(deviceToken: "SOME_DEVICE_TOKEN".data(using: String.Encoding.utf8)!)
85+
waitForExpectations(timeout: 0.5)
86+
}
87+
88+
func testSubscribingToAnInterest() {
89+
let registerEx = expectation(description: "the clientId should be received when registration succeeds")
90+
let subscribeEx = expectation(description: "the client should successfully subscribe to an interest")
91+
92+
dummyDelegate.testClientId = testClientId
93+
dummyDelegate.interestName = "donuts"
94+
dummyDelegate.registerEx = registerEx
95+
dummyDelegate.subscribeEx = subscribeEx
96+
97+
pusher.nativePusher().subscribe(interestName: "donuts")
98+
pusher.nativePusher().register(deviceToken: "SOME_DEVICE_TOKEN".data(using: String.Encoding.utf8)!)
99+
100+
waitForExpectations(timeout: 0.5)
101+
}
102+
103+
func testUnsubscribingFromAnInterest() {
104+
let registerEx = expectation(description: "the clientId should be received when registration succeeds")
105+
let subscribeEx = expectation(description: "the client should successfully subscribe to an interest")
106+
let unsubscribeEx = expectation(description: "the client should successfully unsubscribe from an interest")
107+
dummyDelegate.testClientId = testClientId
108+
dummyDelegate.interestName = "donuts"
109+
dummyDelegate.registerEx = registerEx
110+
dummyDelegate.subscribeEx = subscribeEx
111+
dummyDelegate.unsubscribeEx = unsubscribeEx
112+
113+
pusher.nativePusher().subscribe(interestName: "donuts")
114+
pusher.nativePusher().register(deviceToken: "SOME_DEVICE_TOKEN".data(using: String.Encoding.utf8)!)
115+
pusher.nativePusher().unsubscribe(interestName: "donuts")
116+
117+
waitForExpectations(timeout: 0.5)
118+
}
119+
}
120+
121+
#endif

0 commit comments

Comments
 (0)