Skip to content

Commit c2c7e14

Browse files
committed
Fix unsubscribe, align syntax closer to JS SDK, add redirect and update query
1 parent 9d9edfb commit c2c7e14

File tree

7 files changed

+97
-52
lines changed

7 files changed

+97
-52
lines changed

ParseSwift.playground/Pages/11 - LiveQuery.xcplaygroundpage/Contents.swift

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,13 @@ let subscription = query.subscribe!
3838

3939
//: This is how you receive notifications about the success
4040
//: of your subscription.
41-
subscription.handleSubscribe { _ in
42-
print("Successfully subscribed to query")
43-
44-
//: You can check this subscription is for this query
45-
do {
46-
if try ParseLiveQuery.getDefault()!.isSubscribed(query) {
47-
print("Subscribed")
48-
} else {
49-
print("Not Subscribed")
50-
}
51-
} catch {
52-
fatalError("Error checking if subscribed...")
41+
subscription.handleSubscribe { subscribedQuery, isNew in
42+
43+
//: You can check this subscription is for this query\
44+
if isNew {
45+
print("Successfully subscribed to new query \(subscribedQuery)")
46+
} else {
47+
print("Successfully updated subscription to new query \(subscribedQuery)")
5348
}
5449
}
5550

@@ -73,17 +68,17 @@ subscription.handleEvent { _, event in
7368
//: Now go to your dashboard, goto the GameScore table and add, update, remove rows.
7469
//: You should receive notifications for each.
7570

76-
//: To update the query for your subscription.
77-
query = GameScore.query("score" > 40)
78-
query.update(subscription)
79-
8071
//: This is how you register to receive notificaitons about being unsubscribed.
8172
subscription.handleUnsubscribe { query in
8273
print("Unsubscribed from \(query)")
8374
}
8475

8576
//: To unsubscribe from your query.
86-
query.unsubscribe()
77+
do {
78+
try query.unsubscribe()
79+
} catch {
80+
print(error)
81+
}
8782

8883
PlaygroundPage.current.finishExecution()
8984
//: [Next](@next)

Sources/ParseSwift/LiveQuery/Messages.swift

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ import Foundation
1010

1111
// MARK: Sending
1212
struct StandardMessage: LiveQueryable, Encodable {
13-
var op: Operation // swiftlint:disable:this identifier_name
13+
var op: ClientOperation // swiftlint:disable:this identifier_name
1414
var applicationId: String?
1515
var clientKey: String?
1616
var masterKey: String? // swiftlint:disable:this inclusive_language
1717
var sessionToken: String?
1818
var installationId: String?
1919
var requestId: Int?
2020

21-
init(operation: Operation, additionalProperties: Bool = false) {
21+
init(operation: ClientOperation, additionalProperties: Bool = false) {
2222
self.op = operation
2323
if additionalProperties {
2424
self.applicationId = ParseConfiguration.applicationId
@@ -29,7 +29,7 @@ struct StandardMessage: LiveQueryable, Encodable {
2929
}
3030
}
3131

32-
init(operation: Operation, requestId: RequestId) {
32+
init(operation: ClientOperation, requestId: RequestId) {
3333
self.init(operation: operation)
3434
self.requestId = requestId.value
3535
}
@@ -42,15 +42,15 @@ struct SubscribeQuery: Encodable {
4242
}
4343

4444
struct SubscribeMessage<T: ParseObject>: LiveQueryable, Encodable {
45-
var op: Operation // swiftlint:disable:this identifier_name
45+
var op: ClientOperation // swiftlint:disable:this identifier_name
4646
var applicationId: String?
4747
var clientKey: String?
4848
var sessionToken: String?
4949
var installationId: String?
5050
var requestId: Int?
5151
var query: SubscribeQuery?
5252

53-
init(operation: Operation,
53+
init(operation: ClientOperation,
5454
requestId: RequestId,
5555
query: Query<T>? = nil,
5656
additionalProperties: Bool = false) {
@@ -64,17 +64,22 @@ struct SubscribeMessage<T: ParseObject>: LiveQueryable, Encodable {
6464
}
6565

6666
// MARK: Receiving
67+
struct RedirectResponse: LiveQueryable, Decodable {
68+
let op: ServerResponse // swiftlint:disable:this identifier_name
69+
let url: URL
70+
}
71+
6772
struct ConnectionResponse: LiveQueryable, Decodable {
68-
let op: OperationResponses // swiftlint:disable:this identifier_name
73+
let op: ServerResponse // swiftlint:disable:this identifier_name
6974
}
7075

7176
struct UnsubscribedResponse: LiveQueryable, Decodable {
72-
let op: OperationResponses // swiftlint:disable:this identifier_name
77+
let op: ServerResponse // swiftlint:disable:this identifier_name
7378
let requestId: Int
7479
}
7580

7681
struct EventResponse<T: ParseObject>: LiveQueryable, Decodable {
77-
let op: OperationResponses // swiftlint:disable:this identifier_name
82+
let op: ServerResponse // swiftlint:disable:this identifier_name
7883
let requestId: Int
7984
let object: T
8085
}
@@ -87,6 +92,6 @@ struct ErrorResponse: LiveQueryable, Decodable {
8792
}
8893

8994
struct PreliminaryMessageResponse: LiveQueryable, Decodable {
90-
let op: OperationResponses // swiftlint:disable:this identifier_name
95+
let op: ServerResponse // swiftlint:disable:this identifier_name
9196
let requestId: Int
9297
}

Sources/ParseSwift/LiveQuery/Operations.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88

99
import Foundation
1010

11-
enum Operation: String, Encodable {
11+
enum ClientOperation: String, Encodable {
1212
case connect
1313
case subscribe
1414
case unsubscribe
15+
case update
1516
}
1617

17-
enum OperationResponses: String, Decodable {
18-
case connected, subscribed, unsubscribed,
18+
enum ServerResponse: String, Decodable {
19+
case connected, subscribed, unsubscribed, redirect,
1920
create, enter, update, leave, delete
2021
}
2122

Sources/ParseSwift/LiveQuery/ParseLiveQuery.swift

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,18 @@ extension ParseLiveQuery: LiveQuerySocketDelegate {
217217

218218
func received(_ data: Data) {
219219

220+
if let redirect = try? ParseCoding.jsonDecoder().decode(RedirectResponse.self, from: data) {
221+
if redirect.op == .redirect {
222+
url = redirect.url
223+
if isConnected {
224+
try? self.close(false)
225+
//Try to reconnect
226+
self.createTask()
227+
}
228+
}
229+
return
230+
}
231+
220232
if !self.isConnected {
221233
//Check if this is a connected response
222234
guard let response = try? ParseCoding.jsonDecoder().decode(ConnectionResponse.self, from: data),
@@ -261,11 +273,22 @@ extension ParseLiveQuery: LiveQuerySocketDelegate {
261273

262274
switch preliminaryMessage.op {
263275
case .subscribed:
276+
264277
if let subscribed = pendingQueue
265278
.first(where: { $0.0.value == preliminaryMessage.requestId }) {
279+
let requestId = RequestId(value: preliminaryMessage.requestId)
280+
let isNew: Bool!
281+
if subscriptions[requestId] != nil {
282+
isNew = false
283+
/*pendingQueue.removeAll(where: { $0.0.value == preliminaryMessage.requestId })
284+
subscriptions[subscribed.0] = subscribed.1
285+
subscribed.1.subscribeHandlerClosure?(false)*/
286+
} else {
287+
isNew = true
288+
}
266289
removePendingSubscription(subscribed.0.value)
267290
subscriptions[subscribed.0] = subscribed.1
268-
subscribed.1.subscribeHandlerClosure?()
291+
subscribed.1.subscribeHandlerClosure?(isNew)
269292
}
270293
case .unsubscribed:
271294
let requestId = RequestId(value: preliminaryMessage.requestId)
@@ -347,10 +370,12 @@ extension ParseLiveQuery {
347370
}
348371

349372
///Manually disconnect from the `ParseLiveQuery` server.
350-
public func close() throws {
373+
public func close(_ isUser: Bool = true) throws {
351374
if isConnected {
352375
task.cancel()
353-
isDisconnectedByUser = true
376+
if isUser {
377+
isDisconnectedByUser = true
378+
}
354379
}
355380
URLSession.liveQuery.delegates.removeValue(forKey: task)
356381
}
@@ -372,7 +397,7 @@ extension ParseLiveQuery {
372397
var queryData: Data
373398
var subscriptionHandler: AnyObject
374399
var eventHandlerClosure: ((Data) -> Void)?
375-
var subscribeHandlerClosure: (() -> Void)?
400+
var subscribeHandlerClosure: ((Bool) -> Void)?
376401
var unsubscribeHandlerClosure: (() -> Void)?
377402

378403
init?<T: SubscriptionHandlable>(query: Query<T.Object>, message: SubscribeMessage<T.Object>, handler: T) {
@@ -392,11 +417,11 @@ extension ParseLiveQuery {
392417
try? handler.didReceive(event)
393418
}
394419

395-
subscribeHandlerClosure = { () in
420+
subscribeHandlerClosure = { (new) in
396421
guard let handler = self.subscriptionHandler as? T else {
397422
return
398423
}
399-
handler.didSubscribe()
424+
handler.didSubscribe(new)
400425
}
401426

402427
unsubscribeHandlerClosure = { () in
@@ -458,7 +483,6 @@ extension ParseLiveQuery {
458483
}
459484

460485
func unsubscribe(matching matcher: @escaping (SubscriptionRecord) -> Bool) throws {
461-
var temp = [RequestId: SubscriptionRecord]()
462486
try subscriptions.forEach { (key, value) -> Void in
463487
if matcher(value) {
464488
let encoded = try ParseCoding
@@ -469,10 +493,11 @@ extension ParseLiveQuery {
469493
updatedRecord.messageData = encoded
470494
self.send(record: updatedRecord, requestId: key) { _ in }
471495
} else {
472-
temp[key] = value
496+
let error = ParseError(code: .unknownError,
497+
message: "ParseLiveQuery Error: Not subscribed to this query")
498+
throw error
473499
}
474500
}
475-
subscriptions = temp
476501
}
477502
}
478503

@@ -483,10 +508,9 @@ extension ParseLiveQuery {
483508
func update<T>(_ handler: T) throws where T: SubscriptionHandlable {
484509
try subscriptions.forEach {(key, value) -> Void in
485510
if value.subscriptionHandler === handler {
486-
let message = SubscribeMessage<T.Object>(operation: .subscribe, requestId: key, query: handler.query)
511+
let message = SubscribeMessage<T.Object>(operation: .update, requestId: key, query: handler.query)
487512
let updatedRecord = value
488513
try updatedRecord.update(query: handler.query, message: message)
489-
subscriptions.removeValue(forKey: key)
490514
self.send(record: updatedRecord, requestId: key) { _ in }
491515
}
492516
}

Sources/ParseSwift/LiveQuery/Protocols/SubscriptionHandlable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public protocol SubscriptionHandlable: AnyObject {
2828
Tells the handler that a query has been successfully registered with the server.
2929
- note: This may be invoked multiple times if the client disconnects/reconnects.
3030
*/
31-
func didSubscribe()
31+
func didSubscribe(_ new: Bool)
3232

3333
/**
3434
Tells the handler that a query has been successfully deregistered from the server.

Sources/ParseSwift/LiveQuery/Subscription.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ open class Subscription<Q: Query<T>, T: ParseObject>: SubscriptionHandlable {
6666
public typealias Object = T
6767
fileprivate var errorHandlers: [(Query<T>, Error) -> Void] = []
6868
fileprivate var eventHandlers: [(Query<T>, Event<T>) -> Void] = []
69-
fileprivate var subscribeHandlers: [(Query<T>) -> Void] = []
69+
fileprivate var subscribeHandlers: [(Query<T>, Bool) -> Void] = []
7070
fileprivate var unsubscribeHandlers: [(Query<T>) -> Void] = []
7171

7272
/**
@@ -91,7 +91,7 @@ open class Subscription<Q: Query<T>, T: ParseObject>: SubscriptionHandlable {
9191
- parameter handler: The callback to register.
9292
- returns: The same subscription, for easy chaining.
9393
*/
94-
@discardableResult open func handleSubscribe(_ handler: @escaping (Query<T>) -> Void) -> Subscription {
94+
@discardableResult open func handleSubscribe(_ handler: @escaping (Query<T>, Bool) -> Void) -> Subscription {
9595
subscribeHandlers.append(handler)
9696
return self
9797
}
@@ -115,8 +115,8 @@ open class Subscription<Q: Query<T>, T: ParseObject>: SubscriptionHandlable {
115115
eventHandlers.forEach { $0(query, event) }
116116
}
117117

118-
open func didSubscribe() {
119-
subscribeHandlers.forEach { $0(query) }
118+
open func didSubscribe(_ new: Bool) {
119+
subscribeHandlers.forEach { $0(query, new) }
120120
}
121121

122122
open func didUnsubscribe() {

Tests/ParseSwiftTests/ParseLiveQueryTests.swift

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,23 @@ class ParseLiveQueryTests: XCTestCase {
5353
/*
5454
func testSubscribe() throws {
5555
if #available(iOS 13.0, *) {
56-
let query = GameScore.query("score" > 9)
56+
var query = GameScore.query("score" > 9)
5757
guard let subscription = query.subscribe else {
5858
return
5959
}
6060

6161
let expectation1 = XCTestExpectation(description: "Fetch user1")
62+
63+
subscription.handleSubscribe { subscribedQuery, isNew in
64+
65+
//: You can check this subscription is for this query\
66+
if isNew {
67+
print("Successfully subscribed to new query \(subscribedQuery)")
68+
} else {
69+
print("Successfully updated subscription to new query \(subscribedQuery)")
70+
}
71+
}
72+
6273
subscription.handleEvent { query, event in
6374
print(query)
6475
print(event)
@@ -75,17 +86,26 @@ class ParseLiveQueryTests: XCTestCase {
7586
case .deleted(let delete):
7687
print(delete)
7788
}
78-
expectation1.fulfill()
7989
}
80-
/*
81-
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
82-
try? query.unsubscribe()
83-
}*/
90+
91+
subscription.handleUnsubscribe { query in
92+
print("Unsubscribed from \(query)")
93+
}
94+
95+
DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
96+
//try? query.unsubscribe()
97+
query = GameScore.query("score" > 40)
98+
do {
99+
try query.update(subscription)
100+
} catch {
101+
print(error)
102+
}
103+
}
84104

85105
wait(for: [expectation1], timeout: 200.0)
86106
} else {
87107
// Fallback on earlier versions
88108
}
89109
}
90-
*/
110+
*/
91111
}

0 commit comments

Comments
 (0)