diff --git a/Examples/HubSamplePhone/Info.plist b/Examples/HubSamplePhone/Info.plist index f41381c..f87da23 100644 --- a/Examples/HubSamplePhone/Info.plist +++ b/Examples/HubSamplePhone/Info.plist @@ -20,6 +20,11 @@ 1 LSRequiresIPhoneOS + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -41,10 +46,5 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - diff --git a/Examples/HubSamplePhone/ViewController.swift b/Examples/HubSamplePhone/ViewController.swift index ee8326f..b35d077 100644 --- a/Examples/HubSamplePhone/ViewController.swift +++ b/Examples/HubSamplePhone/ViewController.swift @@ -11,7 +11,7 @@ import SignalRClient class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { // Update the Url accordingly - private let serverUrl = "http://192.168.86.250:5000/chat" // /chat or /chatLongPolling or /chatWebSockets + private let serverUrl = "http://127.0.0.1:5000/chat" // /chat or /chatLongPolling or /chatWebSockets private let dispatchQueue = DispatchQueue(label: "hubsamplephone.queue.dispatcheueuq") private var chatHubConnection: HubConnection? @@ -29,20 +29,19 @@ class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSour self.chatTableView.delegate = self self.chatTableView.dataSource = self } - + override func viewDidAppear(_ animated: Bool) { let alert = UIAlertController(title: "Enter your Name", message:"", preferredStyle: UIAlertController.Style.alert) alert.addTextField() { textField in textField.placeholder = "Name"} let OKAction = UIAlertAction(title: "OK", style: .default) { action in self.name = alert.textFields?.first?.text ?? "John Doe" - self.chatHubConnectionDelegate = ChatHubConnectionDelegate(controller: self) self.chatHubConnection = HubConnectionBuilder(url: URL(string: self.serverUrl)!) .withLogging(minLogLevel: .debug) .withAutoReconnect() .withHubConnectionDelegate(delegate: self.chatHubConnectionDelegate!) .build() - + self.chatHubConnection!.on(method: "NewMessage", callback: {(user: String, message: String) in self.appendMessage(message: "\(user): \(message)") }) @@ -51,11 +50,11 @@ class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSour alert.addAction(OKAction) self.present(alert, animated: true) } - + override func viewWillDisappear(_ animated: Bool) { chatHubConnection?.stop() } - + override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } diff --git a/Examples/SignalRClient.xcodeproj/project.pbxproj b/Examples/SignalRClient.xcodeproj/project.pbxproj index b603309..8ff25f5 100644 --- a/Examples/SignalRClient.xcodeproj/project.pbxproj +++ b/Examples/SignalRClient.xcodeproj/project.pbxproj @@ -74,8 +74,6 @@ 7391A94722510170007BEF23 /* TransferFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7391A91922510170007BEF23 /* TransferFormat.swift */; }; 7391A94822510170007BEF23 /* HubProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7391A91A22510170007BEF23 /* HubProtocol.swift */; }; 7391A94922510170007BEF23 /* HubProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7391A91A22510170007BEF23 /* HubProtocol.swift */; }; - 7391A94A22510170007BEF23 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7391A91B22510170007BEF23 /* Util.swift */; }; - 7391A94B22510170007BEF23 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7391A91B22510170007BEF23 /* Util.swift */; }; 7391A94E22510170007BEF23 /* JSONHubProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7391A91D22510170007BEF23 /* JSONHubProtocol.swift */; }; 7391A94F22510170007BEF23 /* JSONHubProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7391A91D22510170007BEF23 /* JSONHubProtocol.swift */; }; 7391A95022510170007BEF23 /* HttpConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7391A91E22510170007BEF23 /* HttpConnection.swift */; }; @@ -207,7 +205,6 @@ 7391A91822510170007BEF23 /* NegotiationResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NegotiationResponse.swift; sourceTree = ""; }; 7391A91922510170007BEF23 /* TransferFormat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransferFormat.swift; sourceTree = ""; }; 7391A91A22510170007BEF23 /* HubProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HubProtocol.swift; sourceTree = ""; }; - 7391A91B22510170007BEF23 /* Util.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = ""; }; 7391A91D22510170007BEF23 /* JSONHubProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONHubProtocol.swift; sourceTree = ""; }; 7391A91E22510170007BEF23 /* HttpConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpConnection.swift; sourceTree = ""; }; 7391A91F22510170007BEF23 /* ConnectionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionDelegate.swift; sourceTree = ""; }; @@ -380,7 +377,6 @@ 7391A91822510170007BEF23 /* NegotiationResponse.swift */, 7391A91922510170007BEF23 /* TransferFormat.swift */, 7391A91A22510170007BEF23 /* HubProtocol.swift */, - 7391A91B22510170007BEF23 /* Util.swift */, 7391A91D22510170007BEF23 /* JSONHubProtocol.swift */, 7391A91E22510170007BEF23 /* HttpConnection.swift */, 7391A91F22510170007BEF23 /* ConnectionDelegate.swift */, @@ -629,7 +625,6 @@ 7391A92F22510170007BEF23 /* ServerInvocationHandler.swift in Sources */, 7391A94322510170007BEF23 /* HttpClientProtocol.swift in Sources */, 7391A93122510170007BEF23 /* DefaultHttpClient.swift in Sources */, - 7391A94B22510170007BEF23 /* Util.swift in Sources */, 7391A93F22510170007BEF23 /* HandshakeProtocol.swift in Sources */, 5ABD6A632439AE6B00BFA93D /* ReconnectableConnection.swift in Sources */, 7391A94722510170007BEF23 /* TransferFormat.swift in Sources */, @@ -667,7 +662,6 @@ 7391A92E22510170007BEF23 /* ServerInvocationHandler.swift in Sources */, 7391A94222510170007BEF23 /* HttpClientProtocol.swift in Sources */, 7391A93022510170007BEF23 /* DefaultHttpClient.swift in Sources */, - 7391A94A22510170007BEF23 /* Util.swift in Sources */, 7391A93E22510170007BEF23 /* HandshakeProtocol.swift in Sources */, 5ABD6A622439AE6B00BFA93D /* ReconnectableConnection.swift in Sources */, 7391A94622510170007BEF23 /* TransferFormat.swift in Sources */, diff --git a/Sources/SignalRClient/HubConnection.swift b/Sources/SignalRClient/HubConnection.swift index 785e37d..bafb740 100755 --- a/Sources/SignalRClient/HubConnection.swift +++ b/Sources/SignalRClient/HubConnection.swift @@ -90,7 +90,8 @@ public class HubConnection { // TODO: add negative test (e.g. invalid protocol) let handshakeRequest = HandshakeProtocol.createHandshakeRequest(hubProtocol: hubProtocol) logger.log(logLevel: .debug, message: "Sending handshake request: \(handshakeRequest)") - connection.send(data: "\(handshakeRequest)".data(using: .utf8)!) { error in + connection.send(data: "\(handshakeRequest)".data(using: .utf8)!) { [weak self] error in + guard let self else { return } if let e = error { self.logger.log(logLevel: .error, message: "Sending handshake request failed: \(e)") // TODO: (BUG) if this fails when reconnecting the callback should not be called and there @@ -156,7 +157,8 @@ public class HubConnection { let invocationData = try hubProtocol.writeMessage(message: invocationMessage) resetKeepAlive() - connection.send(data: invocationData, sendDidComplete: { error in + connection.send(data: invocationData, sendDidComplete: { [weak self] error in + guard let self else { return } if error == nil { self.resetKeepAlive() } @@ -281,7 +283,8 @@ public class HubConnection { let cancelInvocationMessage = CancelInvocationMessage(invocationId: streamHandle.invocationId) do { let cancelInvocationData = try hubProtocol.writeMessage(message: cancelInvocationMessage) - connection.send(data: cancelInvocationData, sendDidComplete: {error in + connection.send(data: cancelInvocationData, sendDidComplete: { [weak self] error in + guard let self else { return } if let e = error { self.logger.log(logLevel: .error, message: "Sending cancellation of server side streaming hub returned error: \(e)") self.callbackQueue.async { @@ -312,7 +315,8 @@ public class HubConnection { let invocationMessage = invocationHandler.createInvocationMessage(invocationId: id, method: method, arguments: arguments, streamIds: []) let invocationData = try hubProtocol.writeMessage(message: invocationMessage) - connection.send(data: invocationData) { error in + connection.send(data: invocationData) { [weak self] error in + guard let self else { return } if let e = error { self.logger.log(logLevel: .error, message: "Invoking server hub method \(method) returned error: \(e)") self.failInvocationWithError(invocationHandler: invocationHandler, invocationId: id, error: e) @@ -364,17 +368,20 @@ public class HubConnection { // TODO: (BUG) if this fails when reconnecting the callback should not be called and there // will be no further reconnect attempts logger.log(logLevel: .error, message: "Parsing handshake response failed: \(e)") - callbackQueue.async { + callbackQueue.async { [weak self] in + guard let self else { return } self.delegate?.connectionDidFailToOpen(error: e) } return } if originalHandshakeStatus.isReconnect { - callbackQueue.async { + callbackQueue.async { [weak self] in + guard let self else { return } self.delegate?.connectionDidReconnect() } } else { - callbackQueue.async { + callbackQueue.async { [weak self] in + guard let self else { return } self.delegate?.connectionDidOpen(hubConnection: self) } resetKeepAlive() @@ -515,7 +522,10 @@ public class HubConnection { } logger.log(logLevel: .debug, message: "Resetting keep alive") keepAlivePingTask!.cancel() - keepAlivePingTask = DispatchWorkItem { self.sendKeepAlivePing() } + keepAlivePingTask = DispatchWorkItem { [weak self] in + guard let self else { return } + self.sendKeepAlivePing() + } hubConnectionQueue.asyncAfter(deadline: DispatchTime.now() + keepAliveInterval, execute: keepAlivePingTask!) } } @@ -531,7 +541,8 @@ public class HubConnection { do { let cachedPingMessage = try hubProtocol.writeMessage(message: PingMessage.instance) logger.log(logLevel: .debug, message: "Sending keep alive") - connection.send(data: cachedPingMessage, sendDidComplete: { error in + connection.send(data: cachedPingMessage, sendDidComplete: { [weak self] error in + guard let self else { return } if let error = error { self.logger.log(logLevel: .error, message: "Keep alive send error: \(error.localizedDescription)") } else { diff --git a/Sources/SignalRClient/HubConnectionBuilder.swift b/Sources/SignalRClient/HubConnectionBuilder.swift index e0aba64..cd9de27 100644 --- a/Sources/SignalRClient/HubConnectionBuilder.swift +++ b/Sources/SignalRClient/HubConnectionBuilder.swift @@ -24,7 +24,7 @@ public class HubConnectionBuilder { private let httpConnectionOptions = HttpConnectionOptions() private let hubConnectionOptions = HubConnectionOptions() private var logger: Logger = NullLogger() - private var delegate: HubConnectionDelegate? + private weak var delegate: HubConnectionDelegate? private var reconnectPolicy: ReconnectPolicy = NoReconnectPolicy() private var permittedTransportTypes: TransportType = .all private var transportFactory: ((Logger, TransportType) -> TransportFactory) = diff --git a/Sources/SignalRClient/LongPollingTransport.swift b/Sources/SignalRClient/LongPollingTransport.swift index 6016dd1..892b178 100644 --- a/Sources/SignalRClient/LongPollingTransport.swift +++ b/Sources/SignalRClient/LongPollingTransport.swift @@ -9,7 +9,7 @@ import Foundation public class LongPollingTransport: Transport { - public var delegate: TransportDelegate? + public weak var delegate: TransportDelegate? private let logger: Logger private let closeQueue = DispatchQueue(label: "LongPollingTransportCloseQueue") @@ -44,6 +44,7 @@ public class LongPollingTransport: Transport { return } httpClient.post(url: url, body: data) { (responseOptional, errorOptional) in + //If for any reason you need self instance please added as weak to prevent ARC count up and lead to memory leak if let error = errorOptional { sendDidComplete(error) } else if let response = responseOptional { @@ -57,12 +58,14 @@ public class LongPollingTransport: Transport { } public func close() { - closeQueue.sync { + closeQueue.sync { [weak self] in + guard let self else { return } if !closeCalled { closeCalled = true active = false self.logger.log(logLevel: .debug, message: "Sending LongPolling session DELETE request...") - self.httpClient?.delete(url: self.url!, completionHandler: { (_, errorOptional) in + self.httpClient?.delete(url: self.url!, completionHandler: { [weak self] (_, errorOptional) in + guard let self else { return } if let error = errorOptional { self.logger.log(logLevel: .error, message: "Error while DELETE-ing long polling session: \(error)") self.delegate?.transportDidClose(error) @@ -81,7 +84,10 @@ public class LongPollingTransport: Transport { if self.active { let pollUrl = self.getPollUrl() self.logger.log(logLevel: .debug, message: "Polling \(pollUrl)") - self.httpClient?.get(url: pollUrl, completionHandler: self.handlePollResponse(response:error:)) + self.httpClient?.get(url: pollUrl, completionHandler: { [weak self] response,error in + guard let self else { return } + self.handlePollResponse(response: response, error: error) + }) } else { self.logger.log(logLevel: .debug, message: "Long Polling transport polling complete.") self.close() diff --git a/Sources/SignalRClient/WebsocketsTransport.swift b/Sources/SignalRClient/WebsocketsTransport.swift index 74416be..3df7acc 100644 --- a/Sources/SignalRClient/WebsocketsTransport.swift +++ b/Sources/SignalRClient/WebsocketsTransport.swift @@ -18,7 +18,7 @@ public class WebsocketsTransport: NSObject, Transport, URLSessionWebSocketDelega private var isTransportClosed = false - public var delegate: TransportDelegate? + public weak var delegate: TransportDelegate? public let inherentKeepAlive = false init(logger: Logger) { diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e97eb96 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "SignalR-Client-Swift", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}