Skip to content

Commit 38caf2e

Browse files
authored
LiveQuery: Always receive and pass connection errors (#211)
* LiveQuery: Always receive and pass connection errors * Add receive tests * Fix failing testcase * Fix error for tvOS * Fix reconnections * Improve and prepare for release * Update .codecov.yml
1 parent edbe3c6 commit 38caf2e

File tree

10 files changed

+145
-40
lines changed

10 files changed

+145
-40
lines changed

.codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ coverage:
44
status:
55
patch:
66
default:
7-
target: auto
7+
target: 75
88
changes: false
99
project:
1010
default:

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
# Parse-Swift Changelog
22

33
### main
4-
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.9.3...main)
4+
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.9.4...main)
55
* _Contributing to this repo? Add info about your change here to be included in the next release_
66

7+
### 1.9.4
8+
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.9.3...1.9.4)
9+
__Fixes__
10+
- Fix LiveQuery reconnections when server disconnects. Always receive and pass connection errors to ParseLiveQuery delegate ([#211](https://github.com/parse-community/Parse-Swift/pull/211)), thanks to [Corey Baker](https://github.com/cbaker6).
11+
712
### 1.9.3
813
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.9.2...1.9.3)
914
__Improvements__

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,26 @@ struct GameScore: ParseObject {
2929
}
3030
}
3131

32+
//: Create a delegate for LiveQuery errors
33+
class LiveQueryDelegate: ParseLiveQueryDelegate {
34+
35+
func received(_ error: Error) {
36+
print(error)
37+
}
38+
39+
func closedSocket(_ code: URLSessionWebSocketTask.CloseCode?, reason: Data?) {
40+
print("Socket closed with \(String(describing: code)) and \(String(describing: reason))")
41+
}
42+
}
43+
3244
//: Be sure you have LiveQuery enabled on your server.
3345

46+
//: Set the delegate.
47+
let delegate = LiveQueryDelegate()
48+
if let socket = ParseLiveQuery.getDefault() {
49+
socket.receiveDelegate = delegate
50+
}
51+
3452
//: Create a query just as you normally would.
3553
var query = GameScore.query("score" < 11)
3654

Sources/ParseSwift/LiveQuery/LiveQuerySocket.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import FoundationNetworking
1515
final class LiveQuerySocket: NSObject {
1616
private var session: URLSession!
1717
var delegates = [URLSessionWebSocketTask: LiveQuerySocketDelegate]()
18+
var receivingTasks = [URLSessionWebSocketTask: Bool]()
1819
weak var authenticationDelegate: LiveQuerySocketDelegate?
1920

2021
override init() {
@@ -25,10 +26,12 @@ final class LiveQuerySocket: NSObject {
2526
func createTask(_ url: URL, taskDelegate: LiveQuerySocketDelegate) -> URLSessionWebSocketTask {
2627
let task = session.webSocketTask(with: url)
2728
delegates[task] = taskDelegate
29+
receive(task)
2830
return task
2931
}
3032

3133
func removeTaskFromDelegates(_ task: URLSessionWebSocketTask) {
34+
receivingTasks.removeValue(forKey: task)
3235
delegates.removeValue(forKey: task)
3336
}
3437

@@ -90,7 +93,13 @@ extension LiveQuerySocket {
9093
extension LiveQuerySocket {
9194

9295
func receive(_ task: URLSessionWebSocketTask) {
96+
if receivingTasks[task] != nil {
97+
// Receive has already been called for this task
98+
return
99+
}
100+
receivingTasks[task] = true
93101
task.receive { result in
102+
self.receivingTasks.removeValue(forKey: task)
94103
switch result {
95104
case .success(.string(let message)):
96105
if let data = message.data(using: .utf8) {

Sources/ParseSwift/LiveQuery/ParseLiveQuery.swift

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ public final class ParseLiveQuery: NSObject {
6565
var attempts: Int = 1 {
6666
willSet {
6767
if newValue >= ParseLiveQueryConstants.maxConnectionAttempts + 1 {
68+
let error = ParseError(code: .unknownError,
69+
message: """
70+
ParseLiveQuery Error: Reached max attempts of
71+
\(ParseLiveQueryConstants.maxConnectionAttempts).
72+
Not attempting to open ParseLiveQuery socket anymore
73+
""")
74+
notificationQueue.async {
75+
self.receiveDelegate?.received(error)
76+
}
6877
close() // Quit trying to reconnect
6978
}
7079
}
@@ -231,13 +240,17 @@ extension ParseLiveQuery {
231240
synchronizationQueue.sync {
232241
switch self.task.state {
233242
case .suspended:
243+
URLSession.liveQuery.receive(task)
234244
self.task.resume()
235245
completion(nil)
236246
case .completed, .canceling:
237-
URLSession.liveQuery.removeTaskFromDelegates(self.task)
247+
let oldTask = self.task
238248
self.task = URLSession.liveQuery.createTask(self.url,
239249
taskDelegate: self)
240250
self.task.resume()
251+
if let oldTask = oldTask {
252+
URLSession.liveQuery.removeTaskFromDelegates(oldTask)
253+
}
241254
completion(nil)
242255
case .running:
243256
self.open(isUserWantsToConnect: false, completion: completion)
@@ -481,23 +494,49 @@ extension ParseLiveQuery: LiveQuerySocketDelegate {
481494
}
482495

483496
func receivedError(_ error: Error) {
497+
if !isPosixError(error) {
498+
if !isURLError(error) {
499+
notificationQueue.async {
500+
self.receiveDelegate?.received(error)
501+
}
502+
}
503+
}
504+
}
505+
506+
func isPosixError(_ error: Error) -> Bool {
484507
guard let posixError = error as? POSIXError else {
485508
notificationQueue.async {
486509
self.receiveDelegate?.received(error)
487510
}
488-
return
511+
return false
489512
}
490513
if posixError.code == .ENOTCONN {
491-
if attempts + 1 >= ParseLiveQueryConstants.maxConnectionAttempts + 1 {
492-
let parseError = ParseError(code: .unknownError,
493-
message: """
494-
Max attempts (\(ParseLiveQueryConstants.maxConnectionAttempts) reached.
495-
Not attempting to connect to LiveQuery server anymore.
496-
""")
497-
notificationQueue.async {
498-
self.receiveDelegate?.received(parseError)
514+
isSocketEstablished = false
515+
open(isUserWantsToConnect: false) { error in
516+
guard let error = error else {
517+
// Resumed task successfully
518+
return
519+
}
520+
self.notificationQueue.async {
521+
self.receiveDelegate?.received(error)
499522
}
500523
}
524+
} else {
525+
notificationQueue.async {
526+
self.receiveDelegate?.received(error)
527+
}
528+
}
529+
return true
530+
}
531+
532+
func isURLError(_ error: Error) -> Bool {
533+
guard let urlError = error as? URLError else {
534+
notificationQueue.async {
535+
self.receiveDelegate?.received(error)
536+
}
537+
return false
538+
}
539+
if urlError.errorCode == -1005 {
501540
isSocketEstablished = false
502541
open(isUserWantsToConnect: false) { error in
503542
guard let error = error else {
@@ -513,6 +552,7 @@ Not attempting to connect to LiveQuery server anymore.
513552
self.receiveDelegate?.received(error)
514553
}
515554
}
555+
return true
516556
}
517557

518558
func receivedUnsupported(_ data: Data?, socketMessage: URLSessionWebSocketTask.Message?) {
@@ -596,12 +636,15 @@ extension ParseLiveQuery {
596636
if self.isConnected {
597637
self.task.cancel(with: .goingAway, reason: nil)
598638
self.isDisconnectedByUser = true
639+
let oldTask = self.task
640+
isSocketEstablished = false
641+
// Prepare new task for future use.
642+
self.task = URLSession.liveQuery.createTask(self.url,
643+
taskDelegate: self)
644+
if let oldTask = oldTask {
645+
URLSession.liveQuery.removeTaskFromDelegates(oldTask)
646+
}
599647
}
600-
URLSession.liveQuery.removeTaskFromDelegates(self.task)
601-
isSocketEstablished = false
602-
// Prepare new task for future use.
603-
self.task = URLSession.liveQuery.createTask(self.url,
604-
taskDelegate: self)
605648
}
606649
}
607650

@@ -636,22 +679,28 @@ extension ParseLiveQuery {
636679
synchronizationQueue.async {
637680
if self.isConnected {
638681
self.task.cancel(with: .goingAway, reason: nil)
682+
let oldTask = self.task
683+
self.isSocketEstablished = false
684+
// Prepare new task for future use.
685+
self.task = URLSession.liveQuery.createTask(self.url,
686+
taskDelegate: self)
687+
if let oldTask = oldTask {
688+
URLSession.liveQuery.removeTaskFromDelegates(oldTask)
689+
}
639690
}
640-
URLSession.liveQuery.removeTaskFromDelegates(self.task)
641-
self.isSocketEstablished = false
642-
// Prepare new task for future use.
643-
self.task = URLSession.liveQuery.createTask(self.url,
644-
taskDelegate: self)
645691
}
646692
} else {
647693
if self.isConnected {
648694
self.task.cancel(with: .goingAway, reason: nil)
695+
let oldTask = task
696+
isSocketEstablished = false
697+
// Prepare new task for future use.
698+
self.task = URLSession.liveQuery.createTask(self.url,
699+
taskDelegate: self)
700+
if let oldTask = oldTask {
701+
URLSession.liveQuery.removeTaskFromDelegates(oldTask)
702+
}
649703
}
650-
URLSession.liveQuery.removeTaskFromDelegates(self.task)
651-
isSocketEstablished = false
652-
// Prepare new task for future use.
653-
self.task = URLSession.liveQuery.createTask(self.url,
654-
taskDelegate: self)
655704
}
656705
}
657706

Sources/ParseSwift/LiveQuery/Protocols/ParseLiveQueryDelegate.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public protocol ParseLiveQueryDelegate: AnyObject {
3939
/**
4040
Receive errors from the ParseLiveQuery task/connection.
4141
- parameter error: An error from the session task.
42+
- note: The type of error received can vary from `ParseError`, `URLError`, `POSIXError`, etc.
4243
*/
4344
func received(_ error: Error)
4445

Sources/ParseSwift/ParseConstants.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010

1111
enum ParseConstants {
1212
static let sdk = "swift"
13-
static let version = "1.9.3"
13+
static let version = "1.9.4"
1414
static let fileManagementDirectory = "parse/"
1515
static let fileManagementPrivateDocumentsDirectory = "Private Documents/"
1616
static let fileManagementLibraryDirectory = "Library/"

TestHostTV/Info.plist

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,10 @@
3030
</array>
3131
<key>UIUserInterfaceStyle</key>
3232
<string>Automatic</string>
33+
<key>NSAppTransportSecurity</key>
34+
<dict>
35+
<key>NSAllowsArbitraryLoads</key>
36+
<true/>
37+
</dict>
3338
</dict>
3439
</plist>

Tests/ParseSwiftTests/ParseLiveQueryCombineTests.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,14 @@ class ParseLiveQueryCombineTests: XCTestCase {
8686
XCTFail("Should have produced failure")
8787
case .failure(let error):
8888
XCTAssertEqual(client.isSocketEstablished, false)
89-
guard let parseError = error as? ParseError else {
89+
guard let urlError = error as? URLError else {
9090
XCTFail("Should have casted to ParseError.")
9191
expectation1.fulfill()
9292
return
9393
}
94-
XCTAssertEqual(parseError.code, ParseError.Code.unknownError)
95-
XCTAssertTrue(parseError.message.contains("socket status"))
94+
// "Could not connect to the server"
95+
// because webSocket connections are not intercepted.
96+
XCTAssertEqual(urlError.errorCode, -1004)
9697
}
9798
expectation1.fulfill()
9899

0 commit comments

Comments
 (0)