From b58274aae76bd0e97e55c2826b562ff253342239 Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 20 Apr 2018 16:20:39 +0100 Subject: [PATCH 01/22] fix compiler warnings --- Projects/SSEKit/SSEKit/EventSourceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift b/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift index d04d9f2..8fd950e 100644 --- a/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift +++ b/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift @@ -28,7 +28,7 @@ public struct EventSourceConfiguration { public init(withHost host: String, port: Int = 80, endpoint: String, timeout: TimeInterval = 5, events: [String]? = nil, name: String? = nil) { - precondition(endpoint.characters.first == "/", "Endpoint does not begin with a /") + precondition(endpoint.first == "/", "Endpoint does not begin with a /") self.hostAddress = host self.port = port From 6d01f5488b39cdad396197611f0839961ab6e0bf Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Sun, 22 Apr 2018 14:45:23 +0100 Subject: [PATCH 02/22] update project settings (fix compiler warnings) --- Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj | 6 +++++- .../xcshareddata/xcschemes/SSEKit iOS.xcscheme | 4 +--- .../xcshareddata/xcschemes/SSEKit macOS.xcscheme | 4 +--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj index 4e1a9c0..4f35f1d 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj +++ b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj @@ -193,7 +193,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0930; ORGANIZATIONNAME = "Naim Audio Ltd"; TargetAttributes = { 2AA0D22F1C7C9A3B006E83D0 = { @@ -306,12 +306,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -362,12 +364,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme b/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme index 0a7496c..1f2eb8a 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme +++ b/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" From f18b17be6e1e730b233fe2342e8f31d4d5070243 Mon Sep 17 00:00:00 2001 From: Steve Wadsworth Date: Fri, 4 Jan 2019 16:38:57 +0000 Subject: [PATCH 03/22] Create CODEOWNERS --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..28f05ea --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @naimaudio/apps From 45b547baf5fe195a84e5f70ee317f3cd42e2c5a9 Mon Sep 17 00:00:00 2001 From: Gary Newell Date: Fri, 4 Jan 2019 15:01:46 +0000 Subject: [PATCH 04/22] APP-2203: [ios] Standardise indentation across iOS projects --- Playgrounds/Scratch.playground/Contents.swift | 42 +-- Projects/MacSSE/MacSSE/AppDelegate.swift | 12 +- Projects/MacSSE/MacSSE/ViewController.swift | 26 +- Projects/SSEKit/SSEKit/EventSource.swift | 338 +++++++++--------- .../SSEKit/EventSourceConfiguration.swift | 38 +- Projects/SSEKit/SSEKit/SSEManager.swift | 150 ++++---- Projects/SSEKit/SSEKitTests/SSEKitTests.swift | 36 +- .../SSEKitExample/AppDelegate.swift | 44 +-- .../SSEKitExample/ViewController.swift | 130 +++---- .../SSEKitExampleTests.swift | 36 +- .../MyPlayground.playground/Contents.swift | 26 +- .../Operation Queue.playground/Contents.swift | 56 +-- .../Sources/OperationQueue.swift | 18 +- .../Contents.swift | 272 +++++++------- .../SSEKitExample/AppDelegate.swift | 44 +-- .../SSEKitExample/ViewController.swift | 62 ++-- 16 files changed, 665 insertions(+), 665 deletions(-) diff --git a/Playgrounds/Scratch.playground/Contents.swift b/Playgrounds/Scratch.playground/Contents.swift index 49ae50a..6640e67 100644 --- a/Playgrounds/Scratch.playground/Contents.swift +++ b/Playgrounds/Scratch.playground/Contents.swift @@ -4,25 +4,25 @@ import UIKit func extractValue(scanner: NSScanner) -> (String?, String?) { - var field: NSString? - scanner.scanUpToString(":", intoString: &field) - scanner.scanString(":", intoString: nil) + var field: NSString? + scanner.scanUpToString(":", intoString: &field) + scanner.scanString(":", intoString: nil) - var value: NSString? - scanner.scanUpToString("\n", intoString: &value) + var value: NSString? + scanner.scanUpToString("\n", intoString: &value) - return (field?.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()), value?.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())) + return (field?.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()), value?.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())) } func extractValue(forField field: String, scanner: NSScanner) -> String? { - scanner.scanUpToString(field, intoString: nil) - scanner.scanString(field, intoString: nil) + scanner.scanUpToString(field, intoString: nil) + scanner.scanString(field, intoString: nil) - var value: NSString? - scanner.scanUpToString("\n", intoString: &value) + var value: NSString? + scanner.scanUpToString("\n", intoString: &value) - return value as? String + return value as? String } var str = "id: 88e92600-e220-11e5-8f19-6b8864780aab\n\rdata: I'm busy" @@ -30,25 +30,25 @@ var str2 = "id: 8ab35eb0-e220-11e5-8f19-6b8864780aab\n\revent: user-connected\n\ for event in [str, str2] { - let scanner = NSScanner(string: event as String) - scanner.charactersToBeSkipped = NSCharacterSet.whitespaceCharacterSet() + let scanner = NSScanner(string: event as String) + scanner.charactersToBeSkipped = NSCharacterSet.whitespaceCharacterSet() // let identifier = extractValue(forField: "id:", scanner: scanner) // let event = extractValue(forField: "event:", scanner: scanner) // let data = extractValue(forField: "data:", scanner: scanner) - var entity: (String?, String?) + var entity: (String?, String?) - repeat { + repeat { - entity = extractValue(scanner) + entity = extractValue(scanner) - if entity.1 != nil { - print("\(entity.0!):\t\(entity.1!)") - } + if entity.1 != nil { + print("\(entity.0!):\t\(entity.1!)") + } - } while(entity.0 != nil && entity.1 != nil) + } while(entity.0 != nil && entity.1 != nil) // let identifier = extractValue(scanner) @@ -67,5 +67,5 @@ for event in [str, str2] { // print("DATA:\t\(data.1!)") // } - print("---------------------------------------------------------------") + print("---------------------------------------------------------------") } \ No newline at end of file diff --git a/Projects/MacSSE/MacSSE/AppDelegate.swift b/Projects/MacSSE/MacSSE/AppDelegate.swift index fc8a069..478c0a4 100644 --- a/Projects/MacSSE/MacSSE/AppDelegate.swift +++ b/Projects/MacSSE/MacSSE/AppDelegate.swift @@ -13,13 +13,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { - func applicationDidFinishLaunching(aNotification: NSNotification) { - // Insert code here to initialize your application - } + func applicationDidFinishLaunching(aNotification: NSNotification) { + // Insert code here to initialize your application + } - func applicationWillTerminate(aNotification: NSNotification) { - // Insert code here to tear down your application - } + func applicationWillTerminate(aNotification: NSNotification) { + // Insert code here to tear down your application + } } diff --git a/Projects/MacSSE/MacSSE/ViewController.swift b/Projects/MacSSE/MacSSE/ViewController.swift index 3d920a1..00fad4b 100644 --- a/Projects/MacSSE/MacSSE/ViewController.swift +++ b/Projects/MacSSE/MacSSE/ViewController.swift @@ -10,24 +10,24 @@ import Cocoa class ViewController: NSViewController { - @IBOutlet weak var logviewer: NSScrollView! - @IBOutlet weak var ipAddress: NSTextField! + @IBOutlet weak var logviewer: NSScrollView! + @IBOutlet weak var ipAddress: NSTextField! - override func viewDidLoad() { - super.viewDidLoad() + override func viewDidLoad() { + super.viewDidLoad() - // Do any additional setup after loading the view. - } + // Do any additional setup after loading the view. + } - override var representedObject: AnyObject? { - didSet { - // Update the view, if already loaded. - } - } + override var representedObject: AnyObject? { + didSet { + // Update the view, if already loaded. + } + } - @IBAction func onConnect(sender: NSButton) { + @IBAction func onConnect(sender: NSButton) { - } + } } diff --git a/Projects/SSEKit/SSEKit/EventSource.swift b/Projects/SSEKit/SSEKit/EventSource.swift index e1c5939..2d0b580 100644 --- a/Projects/SSEKit/SSEKit/EventSource.swift +++ b/Projects/SSEKit/SSEKit/EventSource.swift @@ -11,36 +11,36 @@ import Foundation // TODO: Split out into multiple files public enum ReadyState: Int { - case connecting = 0 - case open = 1 - case closed = 2 + case connecting = 0 + case open = 1 + case closed = 2 } public enum EventSourceError: Error { - case badEvent - case sourceConnectionTimeout - case sourceNotFound(Int?) //HTTP Status code - case unknown + case badEvent + case sourceConnectionTimeout + case sourceNotFound(Int?) //HTTP Status code + case unknown } open class EventSource: NSObject { internal var queue:DispatchQueue - open var name: String? { - return self.configuration.name - } + open var name: String? { + return self.configuration.name + } - open var readyState: ReadyState = .closed - open var configuration: EventSourceConfiguration - weak open var delegate: EventSourceDelegate? + open var readyState: ReadyState = .closed + open var configuration: EventSourceConfiguration + weak open var delegate: EventSourceDelegate? - public required init(configuration: EventSourceConfiguration, delegate: EventSourceDelegate, queue:DispatchQueue) { - self.queue = queue - self.configuration = configuration //copy - self.delegate = delegate - } + public required init(configuration: EventSourceConfiguration, delegate: EventSourceDelegate, queue:DispatchQueue) { + self.queue = queue + self.configuration = configuration //copy + self.delegate = delegate + } deinit { @@ -57,33 +57,33 @@ open class EventSource: NSObject { public struct Event: CustomDebugStringConvertible { - struct Metadata { + struct Metadata { - let timestamp: Date - let hostUri: String - } + let timestamp: Date + let hostUri: String + } - let metadata: Metadata - let configuration: EventSourceConfiguration + let metadata: Metadata + let configuration: EventSourceConfiguration - let identifier: String? - let event: String? - let data: Data? + let identifier: String? + let event: String? + let data: Data? let jsonData: Dictionary? - init?(withEventSource eventSource: EventSource, identifier: String?, event: String?, data: Data?) { + init?(withEventSource eventSource: EventSource, identifier: String?, event: String?, data: Data?) { - guard identifier != nil else { + guard identifier != nil else { - return nil - } + return nil + } - configuration = eventSource.configuration - self.metadata = Metadata(timestamp: Date(), hostUri: configuration.uri) + configuration = eventSource.configuration + self.metadata = Metadata(timestamp: Date(), hostUri: configuration.uri) - self.identifier = identifier - self.event = event - self.data = data + self.identifier = identifier + self.event = event + self.data = data if (data != nil) { let jsonData = try? JSONSerialization.jsonObject(with: data!, options:[]) self.jsonData = jsonData as? Dictionary @@ -91,39 +91,39 @@ public struct Event: CustomDebugStringConvertible { else { self.jsonData = nil; } - } + } - public var debugDescription: String { + public var debugDescription: String { - return "Event {\(self.identifier != nil ? self.identifier! : "nil"), \(self.event != nil ? self.event! : "nil"), Data length: \(self.data != nil ? self.data!.count : 0)}" - } + return "Event {\(self.identifier != nil ? self.identifier! : "nil"), \(self.event != nil ? self.event! : "nil"), Data length: \(self.data != nil ? self.data!.count : 0)}" + } } @objc public final class PrimaryEventSource: EventSource { - fileprivate var task: URLSessionDataTask? - fileprivate var children = Set() - fileprivate var retries = 0 - fileprivate let maxRetries = 3 + fileprivate var task: URLSessionDataTask? + fileprivate var children = Set() + fileprivate var retries = 0 + fileprivate let maxRetries = 3 var session:URLSession? - internal func add(child: ChildEventSource) { + internal func add(child: ChildEventSource) { - _ = self.queue.async { - self.children.insert(child) - } - } + _ = self.queue.async { + self.children.insert(child) + } + } - internal func remove(child: ChildEventSource) { + internal func remove(child: ChildEventSource) { - _ = self.queue.async { - self.children.remove(child) - } - } + _ = self.queue.async { + self.children.remove(child) + } + } - public override func connect() { - _ = self.queue.async { + public override func connect() { + _ = self.queue.async { self.readyState = .connecting let sessionConfig = URLSessionConfiguration.default @@ -157,7 +157,7 @@ public final class PrimaryEventSource: EventSource { //error } } - } + } public override func disconnect(allowRetry:Bool = true, completion:@escaping ()->() = {}) { _ = self.queue.async { @@ -195,49 +195,49 @@ public final class PrimaryEventSource: EventSource { extension PrimaryEventSource: URLSessionDataDelegate { - public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - disconnect() - } + disconnect() + } - public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { - guard self.readyState != .closed else { + guard self.readyState != .closed else { - //Discard any data from here on in - return - } + //Discard any data from here on in + return + } - guard let response = dataTask.response as? HTTPURLResponse else { + guard let response = dataTask.response as? HTTPURLResponse else { - return //not connected yet - } + return //not connected yet + } - if self.readyState == .connecting { + if self.readyState == .connecting { - switch response.statusCode { + switch response.statusCode { - case 200...299: - fallthrough - case 300...399: - self.delegate?.eventSourceDidConnect(self) - self.readyState = .open - self.retries = 0 - break + case 200...299: + fallthrough + case 300...399: + self.delegate?.eventSourceDidConnect(self) + self.readyState = .open + self.retries = 0 + break - case 400...499: - fallthrough - default: - self.delegate?.eventSource(self, didEncounterError: .sourceNotFound(response.statusCode)) - disconnect() - return - } - } + case 400...499: + fallthrough + default: + self.delegate?.eventSource(self, didEncounterError: .sourceNotFound(response.statusCode)) + disconnect() + return + } + } - inline_URLSession(session, dataTask: dataTask, didReceiveData: data) - } + inline_URLSession(session, dataTask: dataTask, didReceiveData: data) + } - public func inline_URLSession(_ session: Foundation.URLSession, dataTask: URLSessionDataTask, didReceiveData data: Data) { + public func inline_URLSession(_ session: Foundation.URLSession, dataTask: URLSessionDataTask, didReceiveData data: Data) { func scan(_ scanner: Scanner, field:String) -> (String?) { @@ -281,136 +281,136 @@ extension PrimaryEventSource: URLSessionDataDelegate { scanner.scanLocation = loc // reset, as this is optional... // is this actually optional? -// guard eventName != nil else { // finished -// NSLog("SSEKit SSE - No event name!") -// return -// } +// guard eventName != nil else { // finished +// NSLog("SSEKit SSE - No event name!") +// return +// } - eventData = scan(scanner, field:"data") + eventData = scan(scanner, field:"data") - guard eventData != nil else { // finished - NSLog("SSEKit SSE - No event data!") - return - } + guard eventData != nil else { // finished + NSLog("SSEKit SSE - No event data!") + return + } - // Send all events to children - for child in self.children { - self.queue.async { + // Send all events to children + for child in self.children { + self.queue.async { - if let event = Event(withEventSource: child, identifier: eventId, event: eventName, data: eventData?.data(using: String.Encoding.utf8)) { - child.eventSource(self, didReceiveEvent: event) - } - } - } + if let event = Event(withEventSource: child, identifier: eventId, event: eventName, data: eventData?.data(using: String.Encoding.utf8)) { + child.eventSource(self, didReceiveEvent: event) + } + } + } - // Don't create events if nobody is listerning - if let evnArray = self.configuration.events, let evn = eventName, evnArray.contains(evn) { + // Don't create events if nobody is listerning + if let evnArray = self.configuration.events, let evn = eventName, evnArray.contains(evn) { - self.queue.async { + self.queue.async { - if let event = Event(withEventSource: self, identifier: eventId, event: evn, data: eventData?.data(using: String.Encoding.utf8)) { - self.delegate?.eventSource(self, didReceiveEvent: event) - } - } - } - else if self.configuration.events == nil { + if let event = Event(withEventSource: self, identifier: eventId, event: evn, data: eventData?.data(using: String.Encoding.utf8)) { + self.delegate?.eventSource(self, didReceiveEvent: event) + } + } + } + else if self.configuration.events == nil { - self.queue.async { + self.queue.async { - if let event = Event(withEventSource: self, identifier: eventId, event: eventName, data: eventData?.data(using: String.Encoding.utf8)) { - self.delegate?.eventSource(self, didReceiveEvent: event) - } - } - } + if let event = Event(withEventSource: self, identifier: eventId, event: eventName, data: eventData?.data(using: String.Encoding.utf8)) { + self.delegate?.eventSource(self, didReceiveEvent: event) + } + } + } - } while(!scanner.isAtEnd) - } - } + } while(!scanner.isAtEnd) + } + } } @objc public final class ChildEventSource: EventSource { - weak public var primaryEventSource: PrimaryEventSource? + weak public var primaryEventSource: PrimaryEventSource? public required init(configuration: EventSourceConfiguration, delegate: EventSourceDelegate, queue:DispatchQueue) { super.init(configuration: configuration, delegate: delegate, queue:queue) - } + } - internal convenience init(withConfiguration config: EventSourceConfiguration, primaryEventSource: PrimaryEventSource, delegate: EventSourceDelegate, queue:DispatchQueue) { - self.init(configuration: config, delegate: delegate, queue:queue) - self.primaryEventSource = primaryEventSource - } + internal convenience init(withConfiguration config: EventSourceConfiguration, primaryEventSource: PrimaryEventSource, delegate: EventSourceDelegate, queue:DispatchQueue) { + self.init(configuration: config, delegate: delegate, queue:queue) + self.primaryEventSource = primaryEventSource + } public required init(configuration: EventSourceConfiguration, delegate: EventSourceDelegate) { fatalError("init(configuration:delegate:) has not been implemented") } - public override func connect() { + public override func connect() { - // TODO: Return an error if there is a probelm with `primaryEventSource` + // TODO: Return an error if there is a probelm with `primaryEventSource` - //print("CHILD CONNECTED") - self.primaryEventSource?.add(child: self) - self.delegate?.eventSourceDidConnect(self) - } + //print("CHILD CONNECTED") + self.primaryEventSource?.add(child: self) + self.delegate?.eventSourceDidConnect(self) + } - public override func disconnect(allowRetry:Bool = true, completion:@escaping ()->() = {}) { + public override func disconnect(allowRetry:Bool = true, completion:@escaping ()->() = {}) { - delegate?.eventSourceWillDisconnect(self) - self.readyState = .closed - delegate?.eventSourceDidDisconnect(self) + delegate?.eventSourceWillDisconnect(self) + self.readyState = .closed + delegate?.eventSourceDidDisconnect(self) completion() - } + } } extension ChildEventSource: EventSourceDelegate { - public func eventSource(_ eventSource: EventSource, didChangeState state: ReadyState) { /* Ignore */ } + public func eventSource(_ eventSource: EventSource, didChangeState state: ReadyState) { /* Ignore */ } - public func eventSourceDidConnect(_ eventSource: EventSource) { /* Ignore */ } + public func eventSourceDidConnect(_ eventSource: EventSource) { /* Ignore */ } - public func eventSourceWillDisconnect(_ eventSource: EventSource) { /* Ignore */ } + public func eventSourceWillDisconnect(_ eventSource: EventSource) { /* Ignore */ } - public func eventSourceDidDisconnect(_ eventSource: EventSource) { - self.disconnect() - } + public func eventSourceDidDisconnect(_ eventSource: EventSource) { + self.disconnect() + } - public func eventSource(_ eventSource: EventSource, didReceiveEvent event: Event) { + public func eventSource(_ eventSource: EventSource, didReceiveEvent event: Event) { - if let evnArray = self.configuration.events, let evn = event.event, evnArray.contains(evn) { + if let evnArray = self.configuration.events, let evn = event.event, evnArray.contains(evn) { - self.queue.async { + self.queue.async { - if let event = Event(withEventSource: self, identifier: event.identifier, event: event.event, data: event.data) { - self.delegate?.eventSource(self, didReceiveEvent: event) - } - } - } - else if self.configuration.events == nil { + if let event = Event(withEventSource: self, identifier: event.identifier, event: event.event, data: event.data) { + self.delegate?.eventSource(self, didReceiveEvent: event) + } + } + } + else if self.configuration.events == nil { - self.queue.async { + self.queue.async { - if let event = Event(withEventSource: self, identifier: event.identifier, event: event.event, data: event.data) { - self.delegate?.eventSource(self, didReceiveEvent: event) - } - } - } - } + if let event = Event(withEventSource: self, identifier: event.identifier, event: event.event, data: event.data) { + self.delegate?.eventSource(self, didReceiveEvent: event) + } + } + } + } - public func eventSource(_ eventSource: EventSource, didEncounterError error: EventSourceError) { /* Ignore */ } + public func eventSource(_ eventSource: EventSource, didEncounterError error: EventSourceError) { /* Ignore */ } } public protocol EventSourceDelegate: class { - func eventSource(_ eventSource: EventSource, didChangeState state: ReadyState) + func eventSource(_ eventSource: EventSource, didChangeState state: ReadyState) - func eventSourceDidConnect(_ eventSource: EventSource) - func eventSourceWillDisconnect(_ eventSource: EventSource) - func eventSourceDidDisconnect(_ eventSource: EventSource) + func eventSourceDidConnect(_ eventSource: EventSource) + func eventSourceWillDisconnect(_ eventSource: EventSource) + func eventSourceDidDisconnect(_ eventSource: EventSource) - func eventSource(_ eventSource: EventSource, didReceiveEvent event: Event) - func eventSource(_ eventSource: EventSource, didEncounterError error: EventSourceError) + func eventSource(_ eventSource: EventSource, didReceiveEvent event: Event) + func eventSource(_ eventSource: EventSource, didEncounterError error: EventSourceError) } diff --git a/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift b/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift index 8fd950e..6c22382 100644 --- a/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift +++ b/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift @@ -10,33 +10,33 @@ import Foundation public struct EventSourceConfiguration { - internal let name: String? + internal let name: String? - internal let hostAddress: String - internal let port: Int - internal let endpoint: String + internal let hostAddress: String + internal let port: Int + internal let endpoint: String - internal let timeout: TimeInterval + internal let timeout: TimeInterval - internal let events: [String]? + internal let events: [String]? - internal var uri: String { - return "\(self.hostAddress):\(self.port)\(self.endpoint)" - } + internal var uri: String { + return "\(self.hostAddress):\(self.port)\(self.endpoint)" + } - //options? + //options? - public init(withHost host: String, port: Int = 80, endpoint: String, timeout: TimeInterval = 5, events: [String]? = nil, name: String? = nil) { + public init(withHost host: String, port: Int = 80, endpoint: String, timeout: TimeInterval = 5, events: [String]? = nil, name: String? = nil) { - precondition(endpoint.first == "/", "Endpoint does not begin with a /") + precondition(endpoint.first == "/", "Endpoint does not begin with a /") - self.hostAddress = host - self.port = port - self.endpoint = endpoint - self.timeout = timeout + self.hostAddress = host + self.port = port + self.endpoint = endpoint + self.timeout = timeout - self.events = events + self.events = events - self.name = name - } + self.name = name + } } diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index 02de4ab..110ac67 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -11,42 +11,42 @@ import Foundation // MARK: Notifications public extension SSEManager { - public enum Notification: String { + public enum Notification: String { - case Connected - case Event - case Disconnected + case Connected + case Event + case Disconnected - public enum Key: String { + public enum Key: String { - // Event - case Source - case Identifier - case Name - case Data + // Event + case Source + case Identifier + case Name + case Data case JSONData - case Timestamp - } - } + case Timestamp + } + } } // MARK: - SSEManager open class SSEManager { - private static var instanceCount = 0 // debug! + private static var instanceCount = 0 // debug! - fileprivate var primaryEventSource: PrimaryEventSource? // TODO: remove - 1 connection per manager - adds too much complexity (and certainly the cause of a few bugs - as order of add/remove source matters) - fileprivate var eventSources = Set() + fileprivate var primaryEventSource: PrimaryEventSource? // TODO: remove - 1 connection per manager - adds too much complexity (and certainly the cause of a few bugs - as order of add/remove source matters) + fileprivate var eventSources = Set() fileprivate var queue = DispatchQueue(label: "com.naim.ssekit") - public init(sources: [EventSourceConfiguration]) { + public init(sources: [EventSourceConfiguration]) { - for config in sources { - _ = addEventSource(config) - } + for config in sources { + _ = addEventSource(config) + } SSEManager.instanceCount = SSEManager.instanceCount + 1 NSLog("SSEManager init - instances \(SSEManager.instanceCount)") - } + } deinit { self.primaryEventSource = nil @@ -54,36 +54,36 @@ open class SSEManager { NSLog("SSEManager dealloc - deinit \(SSEManager.instanceCount)") } - /** - Add an EventSource to the manager. - */ - open func addEventSource(_ eventSourceConfig: EventSourceConfiguration) -> EventSource { + /** + Add an EventSource to the manager. + */ + open func addEventSource(_ eventSourceConfig: EventSourceConfiguration) -> EventSource { - var eventSource: EventSource! + var eventSource: EventSource! _ = self.queue.sync { // must be sync, need to check there is a primary source - if self.primaryEventSource == nil { - eventSource = PrimaryEventSource(configuration: eventSourceConfig, delegate: self, queue: self.queue) - self.primaryEventSource = eventSource as? PrimaryEventSource + if self.primaryEventSource == nil { + eventSource = PrimaryEventSource(configuration: eventSourceConfig, delegate: self, queue: self.queue) + self.primaryEventSource = eventSource as? PrimaryEventSource precondition(self.eventSources.count == 0, "primary event source created AFTER other sources...bugs will arise...") - } - else { + } + else { eventSource = ChildEventSource(withConfiguration: eventSourceConfig, primaryEventSource: self.primaryEventSource!, delegate: self, queue:self.queue) - } - } + } + } - precondition(eventSource != nil, "Cannot be nil.") + precondition(eventSource != nil, "Cannot be nil.") _ = self.queue.async { - self.eventSources.insert(eventSource) - } + self.eventSources.insert(eventSource) + } - eventSource?.connect() + eventSource?.connect() - return eventSource - } + return eventSource + } public func reconnect() { self.queue.async { @@ -101,10 +101,10 @@ open class SSEManager { } } - /** - Disconnect and remove EventSource from manager. - */ - open func removeEventSource(_ eventSource: EventSource, _ completion:@escaping ()->() = {}) { + /** + Disconnect and remove EventSource from manager. + */ + open func removeEventSource(_ eventSource: EventSource, _ completion:@escaping ()->() = {}) { eventSource.disconnect(allowRetry: false, completion: { @@ -112,7 +112,7 @@ open class SSEManager { if (eventSource == self.primaryEventSource) { NSLog("destroy primary event source") - self.primaryEventSource = nil + self.primaryEventSource = nil } @@ -123,7 +123,7 @@ open class SSEManager { } } }) - } + } open func removeAllEventSources(_ completion:@escaping ()->()) { @@ -141,7 +141,7 @@ open class SSEManager { eventSource.disconnect(allowRetry: false) { count = count - 1 if count == 0 { - self.primaryEventSource = nil + self.primaryEventSource = nil self.eventSources.removeAll() DispatchQueue.main.async { completion() @@ -156,23 +156,23 @@ open class SSEManager { // MARK: - EventSourceDelegate extension SSEManager: EventSourceDelegate { - public func eventSource(_ eventSource: EventSource, didChangeState state: ReadyState) { + public func eventSource(_ eventSource: EventSource, didChangeState state: ReadyState) { - //TODO: Logging - print("State -> \(eventSource) -> \(state)") - } + //TODO: Logging + print("State -> \(eventSource) -> \(state)") + } - public func eventSourceDidConnect(_ eventSource: EventSource) { + public func eventSourceDidConnect(_ eventSource: EventSource) { DispatchQueue.main.async { NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Connected.rawValue), object: eventSource, userInfo: [ Notification.Key.Source.rawValue : eventSource.configuration.uri ]) } - } + } - public func eventSourceWillDisconnect(_ eventSource: EventSource) {} + public func eventSourceWillDisconnect(_ eventSource: EventSource) {} - public func eventSourceDidDisconnect(_ eventSource: EventSource) { + public func eventSourceDidDisconnect(_ eventSource: EventSource) { - //Remove disconnected EventSource objects from the array + //Remove disconnected EventSource objects from the array // self.queue.async { //TODO // if let esIndex = self.eventSources.indexOf(eventSource) { @@ -180,27 +180,27 @@ extension SSEManager: EventSourceDelegate { // } // } DispatchQueue.main.async { - NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Disconnected.rawValue), object: eventSource, userInfo: [ Notification.Key.Source.rawValue : eventSource.configuration.uri ]) - } - } + NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Disconnected.rawValue), object: eventSource, userInfo: [ Notification.Key.Source.rawValue : eventSource.configuration.uri ]) + } + } - public func eventSource(_ eventSource: EventSource, didReceiveEvent event: Event) { + public func eventSource(_ eventSource: EventSource, didReceiveEvent event: Event) { - //print("[ES#: \(eventSources.count)] \(eventSource) -> \(event)") + //print("[ES#: \(eventSources.count)] \(eventSource) -> \(event)") - var userInfo: [String: AnyObject] = [Notification.Key.Source.rawValue : eventSource.configuration.uri as AnyObject, Notification.Key.Timestamp.rawValue : event.metadata.timestamp as AnyObject] + var userInfo: [String: AnyObject] = [Notification.Key.Source.rawValue : eventSource.configuration.uri as AnyObject, Notification.Key.Timestamp.rawValue : event.metadata.timestamp as AnyObject] - if let identifier = event.identifier { - userInfo[Notification.Key.Identifier.rawValue] = identifier as AnyObject - } + if let identifier = event.identifier { + userInfo[Notification.Key.Identifier.rawValue] = identifier as AnyObject + } - if let name = event.event { - userInfo[Notification.Key.Name.rawValue] = name as AnyObject - } + if let name = event.event { + userInfo[Notification.Key.Name.rawValue] = name as AnyObject + } - if let data = event.data { - userInfo[Notification.Key.Data.rawValue] = data as AnyObject - } + if let data = event.data { + userInfo[Notification.Key.Data.rawValue] = data as AnyObject + } if let jsonData = event.jsonData { userInfo[Notification.Key.JSONData.rawValue] = jsonData as AnyObject @@ -209,11 +209,11 @@ extension SSEManager: EventSourceDelegate { DispatchQueue.main.async { NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Event.rawValue), object: eventSource, userInfo: userInfo) } - } + } - public func eventSource(_ eventSource: EventSource, didEncounterError error: EventSourceError) { + public func eventSource(_ eventSource: EventSource, didEncounterError error: EventSourceError) { - //TODO: Send error notification - //print("Error -> \(eventSource) -> \(error)") - } + //TODO: Send error notification + //print("Error -> \(eventSource) -> \(error)") + } } diff --git a/Projects/SSEKit/SSEKitTests/SSEKitTests.swift b/Projects/SSEKit/SSEKitTests/SSEKitTests.swift index cc0ac93..0356650 100644 --- a/Projects/SSEKit/SSEKitTests/SSEKitTests.swift +++ b/Projects/SSEKit/SSEKitTests/SSEKitTests.swift @@ -11,26 +11,26 @@ import XCTest class SSEKitTests: XCTestCase { - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } - func testPerformanceExample() { - // This is an example of a performance test case. - self.measureBlock { - // Put the code you want to measure the time of here. - } - } + func testPerformanceExample() { + // This is an example of a performance test case. + self.measureBlock { + // Put the code you want to measure the time of here. + } + } } diff --git a/Projects/SSEKitExample/SSEKitExample/AppDelegate.swift b/Projects/SSEKitExample/SSEKitExample/AppDelegate.swift index b80976d..52ddfdd 100644 --- a/Projects/SSEKitExample/SSEKitExample/AppDelegate.swift +++ b/Projects/SSEKitExample/SSEKitExample/AppDelegate.swift @@ -11,35 +11,35 @@ import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? + var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - return true - } + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } - func applicationWillResignActive(_ application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. - } + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } - func applicationDidEnterBackground(_ application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - } + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } - func applicationWillEnterForeground(_ application: UIApplication) { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. - } + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + } - func applicationDidBecomeActive(_ application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } - func applicationWillTerminate(_ application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. - } + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } } diff --git a/Projects/SSEKitExample/SSEKitExample/ViewController.swift b/Projects/SSEKitExample/SSEKitExample/ViewController.swift index ccf2e02..d544155 100644 --- a/Projects/SSEKitExample/SSEKitExample/ViewController.swift +++ b/Projects/SSEKitExample/SSEKitExample/ViewController.swift @@ -11,41 +11,41 @@ import SSEKit class ViewController: UIViewController { - var manager: SSEManager? + var manager: SSEManager? - @IBOutlet weak internal var logViewer: UITextView! + @IBOutlet weak internal var logViewer: UITextView! - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view, typically from a nib. + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. - NotificationCenter.default.addObserver(forName: nil, object: nil, queue: nil) { + NotificationCenter.default.addObserver(forName: nil, object: nil, queue: nil) { - //SSEManager.Notification.Key.Name.rawValue + //SSEManager.Notification.Key.Name.rawValue - guard let name = $0.userInfo?[SSEManager.Notification.Key.Name.rawValue], - let data = $0.userInfo?[SSEManager.Notification.Key.Data.rawValue] as? Data, - let identifier = $0.userInfo?[SSEManager.Notification.Key.Identifier.rawValue] as? String, - let timestamp = $0.userInfo?[SSEManager.Notification.Key.Timestamp.rawValue] as? Date - else { + guard let name = $0.userInfo?[SSEManager.Notification.Key.Name.rawValue], + let data = $0.userInfo?[SSEManager.Notification.Key.Data.rawValue] as? Data, + let identifier = $0.userInfo?[SSEManager.Notification.Key.Identifier.rawValue] as? String, + let timestamp = $0.userInfo?[SSEManager.Notification.Key.Timestamp.rawValue] as? Date + else { - return - } + return + } - let dataStr = String(data: data, encoding: String.Encoding(rawValue: 4))! + let dataStr = String(data: data, encoding: String.Encoding(rawValue: 4))! - print("\(timestamp): [\(identifier)] \(name) -> \(dataStr)") - } + print("\(timestamp): [\(identifier)] \(name) -> \(dataStr)") + } - //let config = EventSourceConfiguration(withHost: "192.168.37.123", port: 15081, endpoint: "/notify", timeout: 10, events: nil) - //let config2 = EventSourceConfiguration(withHost: "192.168.37.123", port: 15081, endpoint: "/notify", events: ["nowplaying"]) - //let config4 = EventSourceConfiguration(withHost: "192.168.37.76", port: 8080, endpoint: "/sse", events: ["bad-event"]) + //let config = EventSourceConfiguration(withHost: "192.168.37.123", port: 15081, endpoint: "/notify", timeout: 10, events: nil) + //let config2 = EventSourceConfiguration(withHost: "192.168.37.123", port: 15081, endpoint: "/notify", events: ["nowplaying"]) + //let config4 = EventSourceConfiguration(withHost: "192.168.37.76", port: 8080, endpoint: "/sse", events: ["bad-event"]) - //let config2 = EventSourceConfiguration(withHost: "localhost", port: 8080, endpoint: "/sse2") - //let config3 = EventSourceConfiguration(withHost: "localhost", port: 8081, endpoint: "/sse") + //let config2 = EventSourceConfiguration(withHost: "localhost", port: 8080, endpoint: "/sse2") + //let config3 = EventSourceConfiguration(withHost: "localhost", port: 8081, endpoint: "/sse") - manager = SSEManager(sources: []) + manager = SSEManager(sources: []) /* for i in 0...10 { @@ -78,62 +78,62 @@ class ViewController: UIViewController { // let es2 = manager?.addEventSource(config2) // // NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(onEvent2(_:)), name: SSEManager.Notification.Event.rawValue, object: es2) - } + } - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } - @objc - func onEvent(_ note: Notification) { + @objc + func onEvent(_ note: Notification) { - if let identifier = note.userInfo?["Identifier"], - let name = note.userInfo?["Name"], - let source = note.userInfo?["Source"], - let timestamp = note.userInfo?["Timestamp"] { + if let identifier = note.userInfo?["Identifier"], + let name = note.userInfo?["Name"], + let source = note.userInfo?["Source"], + let timestamp = note.userInfo?["Timestamp"] { - guard let eventSource = note.object as? EventSource else { - return - } + guard let eventSource = note.object as? EventSource else { + return + } - let event = "\(eventSource.name!) [\(identifier)]\t\(name)\t\(source)\t\(timestamp)\n" + let event = "\(eventSource.name!) [\(identifier)]\t\(name)\t\(source)\t\(timestamp)\n" - DispatchQueue.main.async { - self.logViewer.text = (event + self.logViewer.text) - } - } - } + DispatchQueue.main.async { + self.logViewer.text = (event + self.logViewer.text) + } + } + } - func onEvent2(_ note: Notification) { + func onEvent2(_ note: Notification) { - if let identifier = note.userInfo?["Identifier"], - let name = note.userInfo?["Name"] { + if let identifier = note.userInfo?["Identifier"], + let name = note.userInfo?["Name"] { - let event = "2️⃣[\(identifier)]\t\(name)\n" + let event = "2️⃣[\(identifier)]\t\(name)\n" - DispatchQueue.main.async { - self.logViewer.text = (event + self.logViewer.text) - } + DispatchQueue.main.async { + self.logViewer.text = (event + self.logViewer.text) + } - } - } + } + } - func onConnected(_ note: Notification) { + func onConnected(_ note: Notification) { - DispatchQueue.main.async { - self.logViewer.text = (" **** CONNECTED ****" + self.logViewer.text) - } - } + DispatchQueue.main.async { + self.logViewer.text = (" **** CONNECTED ****" + self.logViewer.text) + } + } - func onDisconnected(_ note: Notification) { + func onDisconnected(_ note: Notification) { - DispatchQueue.main.async { - self.logViewer.text = (" ==== DISCONNECTED ====" + self.logViewer.text) - } - } + DispatchQueue.main.async { + self.logViewer.text = (" ==== DISCONNECTED ====" + self.logViewer.text) + } + } - override var prefersStatusBarHidden : Bool { - return true - } + override var prefersStatusBarHidden : Bool { + return true + } } diff --git a/Projects/SSEKitExample/SSEKitExampleTests/SSEKitExampleTests.swift b/Projects/SSEKitExample/SSEKitExampleTests/SSEKitExampleTests.swift index 68789b0..bc6aa2c 100644 --- a/Projects/SSEKitExample/SSEKitExampleTests/SSEKitExampleTests.swift +++ b/Projects/SSEKitExample/SSEKitExampleTests/SSEKitExampleTests.swift @@ -11,26 +11,26 @@ import XCTest class SSEKitExampleTests: XCTestCase { - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } - func testPerformanceExample() { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } } diff --git a/Prototypes/MyPlayground.playground/Contents.swift b/Prototypes/MyPlayground.playground/Contents.swift index 2d9e29d..cff89b5 100644 --- a/Prototypes/MyPlayground.playground/Contents.swift +++ b/Prototypes/MyPlayground.playground/Contents.swift @@ -16,31 +16,31 @@ private let underlyingQueue: dispatch_queue_t! = dispatch_queue_create("com.naim class hold { - init(queue: dispatch_queue_t!) { - print("Git queue: \(queue)") - } + init(queue: dispatch_queue_t!) { + print("Git queue: \(queue)") + } } class holdNumber { - init(holdNumber: Int) { - print("Got number: \(holdNumber)") - } + init(holdNumber: Int) { + print("Got number: \(holdNumber)") + } } public class test { - lazy var underlyingQueue: dispatch_queue_t! = dispatch_queue_create("com.naim.DuleQueue", DISPATCH_QUEUE_SERIAL) - internal var number: Int = 2 + lazy var underlyingQueue: dispatch_queue_t! = dispatch_queue_create("com.naim.DuleQueue", DISPATCH_QUEUE_SERIAL) + internal var number: Int = 2 - //let myHold = hold(queue: underlyingQueue) - private lazy var myNumber : holdNumber = holdNumber(holdNumber: self.number) + //let myHold = hold(queue: underlyingQueue) + private lazy var myNumber : holdNumber = holdNumber(holdNumber: self.number) - func out() { - print("Test: \(underlyingQueue)") - } + func out() { + print("Test: \(underlyingQueue)") + } } var myTest = test() diff --git a/Prototypes/Operation Queue.playground/Contents.swift b/Prototypes/Operation Queue.playground/Contents.swift index fba3e22..10434e9 100644 --- a/Prototypes/Operation Queue.playground/Contents.swift +++ b/Prototypes/Operation Queue.playground/Contents.swift @@ -31,37 +31,37 @@ XCPSetExecutionShouldContinueIndefinitely(true) class DuleQueue { - //dispatch_queue_attr_make_with_qos_class - private let underlyingQueue: dispatch_queue_t! // = dispatch_queue_create("com.naim.DuleQueue", DISPATCH_QUEUE_SERIAL) + //dispatch_queue_attr_make_with_qos_class + private let underlyingQueue: dispatch_queue_t! // = dispatch_queue_create("com.naim.DuleQueue", DISPATCH_QUEUE_SERIAL) - //private let testQueue: NSOperationQueue = NSOperationQueue( + //private let testQueue: NSOperationQueue = NSOperationQueue( - private let firstQueue = NSOperationQueue(qualityOfService: .Utility) + private let firstQueue = NSOperationQueue(qualityOfService: .Utility) - private let secondQueue = NSOperationQueue(qualityOfService: .Utility) + private let secondQueue = NSOperationQueue(qualityOfService: .Utility) - init() { + init() { - let attrs = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0) - underlyingQueue = dispatch_queue_create("com.naim.DuleQueue", attrs) + let attrs = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0) + underlyingQueue = dispatch_queue_create("com.naim.DuleQueue", attrs) - firstQueue.underlyingQueue = underlyingQueue - secondQueue.underlyingQueue = underlyingQueue - } + firstQueue.underlyingQueue = underlyingQueue + secondQueue.underlyingQueue = underlyingQueue + } - func dispachOnFirstQueue(block: dispatch_block_t) { + func dispachOnFirstQueue(block: dispatch_block_t) { - let operation = NSBlockOperation(block: block) + let operation = NSBlockOperation(block: block) - firstQueue.addOperations([operation], waitUntilFinished: true) - } + firstQueue.addOperations([operation], waitUntilFinished: true) + } - func dispachOnSecondQueue(block: dispatch_block_t) { + func dispachOnSecondQueue(block: dispatch_block_t) { - let operation = NSBlockOperation(block: block) + let operation = NSBlockOperation(block: block) - secondQueue.addOperations([operation], waitUntilFinished: true) - } + secondQueue.addOperations([operation], waitUntilFinished: true) + } } let queue = DuleQueue() @@ -71,19 +71,19 @@ var data = [Int]() for i in 0..<10 { - queue.dispachOnFirstQueue { - data.append(1) - sleep(3) - } + queue.dispachOnFirstQueue { + data.append(1) + sleep(3) + } - queue.dispachOnSecondQueue { - data.append(8) - sleep(1) - } + queue.dispachOnSecondQueue { + data.append(8) + sleep(1) + } } queue.dispachOnSecondQueue { - print(" --> \(data)") + print(" --> \(data)") } //for i in 0..<10 { diff --git a/Prototypes/Operation Queue.playground/Sources/OperationQueue.swift b/Prototypes/Operation Queue.playground/Sources/OperationQueue.swift index 2a3d09f..9357696 100644 --- a/Prototypes/Operation Queue.playground/Sources/OperationQueue.swift +++ b/Prototypes/Operation Queue.playground/Sources/OperationQueue.swift @@ -10,25 +10,25 @@ import Foundation extension NSOperationQueue { - //private let underlyingQueue: dispatch_queue_t! + //private let underlyingQueue: dispatch_queue_t! - convenience init(qualityOfService: NSQualityOfService, maxConcurrentOperationCount: Int = 1, underlyingQueue: dispatch_queue_t?) { + convenience init(qualityOfService: NSQualityOfService, maxConcurrentOperationCount: Int = 1, underlyingQueue: dispatch_queue_t?) { - self.init() + self.init() - self.qualityOfService = qualityOfService - self.maxConcurrentOperationCount = maxConcurrentOperationCount + self.qualityOfService = qualityOfService + self.maxConcurrentOperationCount = maxConcurrentOperationCount - if let queue = underlyingQueue { - self.underlyingQueue = queue - } + if let queue = underlyingQueue { + self.underlyingQueue = queue + } // else { // let attrs = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0) // underlyingQueue = dispatch_queue_create("com.naim.DuleQueue", attrs) // // self.underlyingQueue = underlyingQueue // } - } + } // convenience init(qualityOfService: NSQualityOfService, maxConcurrentOperationCount: Int = 1) { // self.init() diff --git a/Prototypes/ProducerConsumer.playground/Contents.swift b/Prototypes/ProducerConsumer.playground/Contents.swift index 904b051..0b0bf8d 100644 --- a/Prototypes/ProducerConsumer.playground/Contents.swift +++ b/Prototypes/ProducerConsumer.playground/Contents.swift @@ -8,54 +8,54 @@ XCPSetExecutionShouldContinueIndefinitely(true) extension NSOperationQueue { - convenience init(qualityOfService: NSQualityOfService, maxConcurrentOperationCount: Int = 1) { - self.init() + convenience init(qualityOfService: NSQualityOfService, maxConcurrentOperationCount: Int = 1) { + self.init() - self.qualityOfService = qualityOfService - self.maxConcurrentOperationCount = maxConcurrentOperationCount - } + self.qualityOfService = qualityOfService + self.maxConcurrentOperationCount = maxConcurrentOperationCount + } } class Producer { - //private unowned let dataWrite: NSMutableData - private var dataWriteString: DataContainer + //private unowned let dataWrite: NSMutableData + private var dataWriteString: DataContainer - //private let dataToWrite = "abcdefghijklmnopqrstuvwxyz+0123456789+ABCDEFGHIJKLMNOPQRSTUVWXYZ+" - private let dataToWrite = "abcd123+efgh456+abcd123+efgh456+abcd123+efgh456+abcd123+efgh456+abcd123+efgh456+a+b+c+d+f+g+h+i+j+k+l+aa+bb+cc+dd+aaa+bbb+ccc+efgh456abcd123efgh456efgh456abcd123efgh456+" + //private let dataToWrite = "abcdefghijklmnopqrstuvwxyz+0123456789+ABCDEFGHIJKLMNOPQRSTUVWXYZ+" + private let dataToWrite = "abcd123+efgh456+abcd123+efgh456+abcd123+efgh456+abcd123+efgh456+abcd123+efgh456+a+b+c+d+f+g+h+i+j+k+l+aa+bb+cc+dd+aaa+bbb+ccc+efgh456abcd123efgh456efgh456abcd123efgh456+" - var clock: NSTimer! - //lazy var clock: NSTimer! = NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: "produceSomeData", userInfo: nil, repeats: true) + var clock: NSTimer! + //lazy var clock: NSTimer! = NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: "produceSomeData", userInfo: nil, repeats: true) - var count = 0 + var count = 0 - private unowned let consumer: Consumer + private unowned let consumer: Consumer - init(data: DataContainer, consumer: Consumer) { - dataWriteString = data - self.consumer = consumer + init(data: DataContainer, consumer: Consumer) { + dataWriteString = data + self.consumer = consumer - clock = NSTimer.scheduledTimerWithTimeInterval(0.0125, target: self, selector: "produceSomeData:", userInfo: nil, repeats: true) - clock.fire() + clock = NSTimer.scheduledTimerWithTimeInterval(0.0125, target: self, selector: "produceSomeData:", userInfo: nil, repeats: true) + clock.fire() - print("Created Producer...") - } + print("Created Producer...") + } - @objc func produceSomeData(timer: NSTimer) { - //print("- tick") + @objc func produceSomeData(timer: NSTimer) { + //print("- tick") - let length = dataToWrite.characters.count - let randomInt = Int(arc4random_uniform(UInt32(length))) + 1 //no zeros - let index = dataToWrite.startIndex.advancedBy(randomInt) - let string = dataToWrite.substringToIndex(index) + let length = dataToWrite.characters.count + let randomInt = Int(arc4random_uniform(UInt32(length))) + 1 //no zeros + let index = dataToWrite.startIndex.advancedBy(randomInt) + let string = dataToWrite.substringToIndex(index) - //print("Adding string \"\(string)\" to data container") + //print("Adding string \"\(string)\" to data container") - //let data: NSData! = string.dataUsingEncoding(NSUTF8StringEncoding) + //let data: NSData! = string.dataUsingEncoding(NSUTF8StringEncoding) - //print("Adding string \"\(string)\" to data container") - //print("Adding data \"\(data)\" to data container") + //print("Adding string \"\(string)\" to data container") + //print("Adding data \"\(data)\" to data container") // if(data != nil) { // dataWrite.appendData(data) @@ -74,35 +74,35 @@ class Producer { if (count % 50) == 0 { //(Int(arc4random_uniform(200))) + 10 clock.fireDate = NSDate().dateByAddingTimeInterval(Double(arc4random_uniform(5))) } - } + } } class Consumer { - //private unowned let dataRead: NSMutableData - private var dataReadString: DataContainer + //private unowned let dataRead: NSMutableData + private var dataReadString: DataContainer - var clock: NSTimer! + var clock: NSTimer! - var location: String.Index! - var distance: String.Index.Distance = 0 - var foundChuncks = 0 + var location: String.Index! + var distance: String.Index.Distance = 0 + var foundChuncks = 0 - var progressArray: [Float] = [] + var progressArray: [Float] = [] - let opsQueue = NSOperationQueue(qualityOfService: .Background) + let opsQueue = NSOperationQueue(qualityOfService: .Background) - init(data: DataContainer) { - dataReadString = data - location = dataReadString.startIndex(); + init(data: DataContainer) { + dataReadString = data + location = dataReadString.startIndex(); - clock = NSTimer.scheduledTimerWithTimeInterval(0.25, target: self, selector: "consumeSomeData:", userInfo: nil, repeats: true) + clock = NSTimer.scheduledTimerWithTimeInterval(0.25, target: self, selector: "consumeSomeData:", userInfo: nil, repeats: true) - print("Created Consumer...") - } + print("Created Consumer...") + } - func scheduleDrain() { + func scheduleDrain() { // let dataDelta = dataReadString.characters.count - location // XCPCaptureValue("Delta", value: dataDelta) @@ -113,93 +113,93 @@ class Consumer { } } - } + } - var count: Int = 0 + var count: Int = 0 - func drain() { + func drain() { - count++ + count++ - //location in NSData is not the same as location in String - //DATA->String->string buffer->consumer - //let nextRange = Range(start: location, end: dataReadString.endIndex) + //location in NSData is not the same as location in String + //DATA->String->string buffer->consumer + //let nextRange = Range(start: location, end: dataReadString.endIndex) - //let nextRange: Range = NSMakeRange(location, (dataReadString.characters.count - location)) //range from location to end of data container - //let subData = dataRead.subdataWithRange(nextRange) - let newLocation = dataReadString.startIndex(); //This gets the Index for the current string size - let newNewLoc = newLocation.advancedBy(distance) + //let nextRange: Range = NSMakeRange(location, (dataReadString.characters.count - location)) //range from location to end of data container + //let subData = dataRead.subdataWithRange(nextRange) + let newLocation = dataReadString.startIndex(); //This gets the Index for the current string size + let newNewLoc = newLocation.advancedBy(distance) - //let range = Range(start: newNewLoc, end: dataReadString.endIndex()) - // print("distance: \(distance) range: \(range)") + //let range = Range(start: newNewLoc, end: dataReadString.endIndex()) + // print("distance: \(distance) range: \(range)") - let string = dataReadString.chunk(fromIndex: newNewLoc) + let string = dataReadString.chunk(fromIndex: newNewLoc) - //print("Data: \(subData)") + //print("Data: \(subData)") - //let string: String! = String(data: subData, encoding: NSUTF8StringEncoding) + //let string: String! = String(data: subData, encoding: NSUTF8StringEncoding) - if (string.characters.count > 0) { + if (string.characters.count > 0) { - let scanner = NSScanner(string: string) + let scanner = NSScanner(string: string) - while(!scanner.atEnd) { + while(!scanner.atEnd) { - var foundStr: NSString? = nil + var foundStr: NSString? = nil - if scanner.scanUpToString("+", intoString: &foundStr) { //capture string in nil + if scanner.scanUpToString("+", intoString: &foundStr) { //capture string in nil - if(scanner.scanString("+", intoString: nil)) { //scan past +) + if(scanner.scanString("+", intoString: nil)) { //scan past +) - //GET NEW LOCATION EVERY TIME + //GET NEW LOCATION EVERY TIME - //location.advancedBy(foundStr!.length) + //location.advancedBy(foundStr!.length) - //print("Length: NS->\(foundStr!.length), S->\(foundString.characters.count)") - //let dis: String.Index.Distance = foundString.characters.count as String.Index.Distance - // print("DISTANCE: \(dis)") - //let newLocation = location.advancedBy(foundString.characters.count) - // print("NEW LOCATION: \(newLocation)") + //print("Length: NS->\(foundStr!.length), S->\(foundString.characters.count)") + //let dis: String.Index.Distance = foundString.characters.count as String.Index.Distance + // print("DISTANCE: \(dis)") + //let newLocation = location.advancedBy(foundString.characters.count) + // print("NEW LOCATION: \(newLocation)") - let foundString: String = foundStr as! String + let foundString: String = foundStr as! String - let distanceToAdvance = 1 + (foundString.characters.count as String.Index.Distance) //+1 for the '+' - distance += distanceToAdvance + let distanceToAdvance = 1 + (foundString.characters.count as String.Index.Distance) //+1 for the '+' + distance += distanceToAdvance - //print("location: \(location)") + //print("location: \(location)") - foundChuncks++ - } - } - } - - if (dataReadString.length >= 512) || ((foundChuncks % 64) == 0) { - //TODO: only add one at a time - let op = NSBlockOperation { - let index = self.dataReadString.startIndex().advancedBy(self.distance) - self.dataReadString.trim(fromIndex: index) - self.distance = 0 - } + foundChuncks++ + } + } + } + + if (dataReadString.length >= 512) || ((foundChuncks % 64) == 0) { + //TODO: only add one at a time + let op = NSBlockOperation { + let index = self.dataReadString.startIndex().advancedBy(self.distance) + self.dataReadString.trim(fromIndex: index) + self.distance = 0 + } - op.queuePriority = .VeryHigh + op.queuePriority = .VeryHigh - opsQueue.addOperation(op) - } - } - else { - print("data string: \(dataReadString)") - print("Bad data: \(string)") - } - } + opsQueue.addOperation(op) + } + } + else { + print("data string: \(dataReadString)") + print("Bad data: \(string)") + } + } - @objc func consumeSomeData(timer: NSTimer) { + @objc func consumeSomeData(timer: NSTimer) { - //print("Consumer storage length: \(dataReadString.length)") + //print("Consumer storage length: \(dataReadString.length)") - _ = dataReadString.length - //XCPCaptureValue("Length", value: storageLength) + _ = dataReadString.length + //XCPCaptureValue("Length", value: storageLength) - // print("Location index: \(location)") + // print("Location index: \(location)") // let progress = 1.0 - (Float(location) / Float(dataRead.length)) @@ -207,24 +207,24 @@ class Consumer { // // let queueLength = opsQueue.operationCount // XCPCaptureValue("Queue Length", value: queueLength) - } + } } class DataContainer { - init() { + init() { - } + } - //location? + //location? - var length: Int { + var length: Int { - get { - return internalStringStorage.characters.count //TODO - } - } + get { + return internalStringStorage.characters.count //TODO + } + } // func distanceToEnd(fromIndex index: Range) { // @@ -232,41 +232,41 @@ class DataContainer { // // } - private var internalStringStorage = String() + private var internalStringStorage = String() - func append(data: String) { + func append(data: String) { - internalStringStorage += data //TODO: Thread safe? + internalStringStorage += data //TODO: Thread safe? - } + } - func chunk(range: Range) -> String { + func chunk(range: Range) -> String { - return internalStringStorage.substringWithRange(range) - } + return internalStringStorage.substringWithRange(range) + } - func chunk(fromIndex index: String.Index) -> String { + func chunk(fromIndex index: String.Index) -> String { - let range = Range(start: index, end: internalStringStorage.endIndex) + let range = Range(start: index, end: internalStringStorage.endIndex) - //print("\tchunk :: \(range) -> internalStringStorage") + //print("\tchunk :: \(range) -> internalStringStorage") - return internalStringStorage.substringWithRange(range) - } + return internalStringStorage.substringWithRange(range) + } - func startIndex() -> String.Index { - return internalStringStorage.startIndex - } + func startIndex() -> String.Index { + return internalStringStorage.startIndex + } - func endIndex() -> String.Index { - return internalStringStorage.endIndex - } + func endIndex() -> String.Index { + return internalStringStorage.endIndex + } - func trim(fromIndex index: String.Index) { + func trim(fromIndex index: String.Index) { - let trimmedString = chunk(fromIndex: index) - internalStringStorage = trimmedString - } + let trimmedString = chunk(fromIndex: index) + internalStringStorage = trimmedString + } } //let dataContainer: NSMutableData! = NSMutableData(capacity: 512) diff --git a/Prototypes/SSEKitExample/SSEKitExample/AppDelegate.swift b/Prototypes/SSEKitExample/SSEKitExample/AppDelegate.swift index 4fd15af..4349b7e 100644 --- a/Prototypes/SSEKitExample/SSEKitExample/AppDelegate.swift +++ b/Prototypes/SSEKitExample/SSEKitExample/AppDelegate.swift @@ -11,35 +11,35 @@ import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? + var window: UIWindow? - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - // Override point for customization after application launch. - return true - } + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + // Override point for customization after application launch. + return true + } - func applicationWillResignActive(application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. - } + func applicationWillResignActive(application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } - func applicationDidEnterBackground(application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - } + func applicationDidEnterBackground(application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } - func applicationWillEnterForeground(application: UIApplication) { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. - } + func applicationWillEnterForeground(application: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + } - func applicationDidBecomeActive(application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } + func applicationDidBecomeActive(application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } - func applicationWillTerminate(application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. - } + func applicationWillTerminate(application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } } diff --git a/Prototypes/SSEKitExample/SSEKitExample/ViewController.swift b/Prototypes/SSEKitExample/SSEKitExample/ViewController.swift index b1828e9..2fdbeb6 100644 --- a/Prototypes/SSEKitExample/SSEKitExample/ViewController.swift +++ b/Prototypes/SSEKitExample/SSEKitExample/ViewController.swift @@ -11,58 +11,58 @@ import SSEKit class ViewController: UIViewController { - let sse = EventSource(host: "lamp.private", path: "/sse.php") + let sse = EventSource(host: "lamp.private", path: "/sse.php") - override func viewDidLoad() { - super.viewDidLoad() + override func viewDidLoad() { + super.viewDidLoad() - print("SSEKit version: \(sse.versionString)") + print("SSEKit version: \(sse.versionString)") - } + } - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } } /* class ViewController: UIViewController, StateDelegate { - enum ExampleState { - case Initial - } + enum ExampleState { + case Initial + } - typealias StateType = ExampleState + typealias StateType = ExampleState - override func viewDidLoad() { - super.viewDidLoad() + override func viewDidLoad() { + super.viewDidLoad() - let stateMachine = State(initialState:.Initial, delegate:self) + let stateMachine = State(initialState:.Initial, delegate:self) - print("State: \(stateMachine.state)") - print("State Machine version: \(stateMachine.version)") + print("State: \(stateMachine.state)") + print("State Machine version: \(stateMachine.version)") - } + } - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } - // MARK: StateDelegate + // MARK: StateDelegate - func shouldTransitionFrom(from:StateType, to:StateType) -> Bool { - return true - } + func shouldTransitionFrom(from:StateType, to:StateType) -> Bool { + return true + } - func didTransitionFrom(from:StateType, to:StateType) { + func didTransitionFrom(from:StateType, to:StateType) { - } + } - func failedTransitionFrom(from:StateType, to:StateType) { + func failedTransitionFrom(from:StateType, to:StateType) { - } + } } */ \ No newline at end of file From cb8603901c82b55d80d9e9ca47ee911d000014e7 Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Tue, 7 May 2019 11:50:13 +0100 Subject: [PATCH 05/22] APP-2720 fix compiler warnings --- Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj | 7 +++++-- .../xcshareddata/xcschemes/SSEKit iOS.xcscheme | 2 +- .../xcshareddata/xcschemes/SSEKit macOS.xcscheme | 2 +- Projects/SSEKit/SSEKit/SSEManager.swift | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj index 4f35f1d..de8001c 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj +++ b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj @@ -193,7 +193,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "Naim Audio Ltd"; TargetAttributes = { 2AA0D22F1C7C9A3B006E83D0 = { @@ -212,10 +212,11 @@ }; buildConfigurationList = 2AA0D22A1C7C9A3B006E83D0 /* Build configuration list for PBXProject "SSEKit" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 2AA0D2261C7C9A3B006E83D0; productRefGroup = 2AA0D2311C7C9A3B006E83D0 /* Products */; @@ -297,6 +298,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -355,6 +357,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; diff --git a/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme b/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme index 1f2eb8a..f562e96 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme +++ b/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme @@ -1,6 +1,6 @@ Date: Thu, 9 May 2019 18:48:48 +0100 Subject: [PATCH 06/22] APP-2737 remove primary event source --- .../SSEKit/SSEKit.xcodeproj/project.pbxproj | 8 +- .../xcschemes/SSEKit iOS.xcscheme | 1 + Projects/SSEKit/SSEKit/EventSource.swift | 393 ++-------------- .../SSEKit/EventSourceConfiguration.swift | 7 +- Projects/SSEKit/SSEKit/SSEManager.swift | 419 ++++++++++++------ Projects/SSEKit/SSEKitTests/SSEKitTests.swift | 112 ++++- 6 files changed, 448 insertions(+), 492 deletions(-) diff --git a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj index de8001c..d495e42 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj +++ b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj @@ -450,11 +450,11 @@ isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = SSEKitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -462,11 +462,11 @@ isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = SSEKitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme b/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme index f562e96..59fcabf 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme +++ b/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + codeCoverageEnabled = "YES" shouldUseLaunchSchemeArgsEnv = "YES"> () = {}) { - // don't do anything - virtual? + deinit { + } } @@ -64,7 +65,6 @@ public struct Event: CustomDebugStringConvertible { } let metadata: Metadata - let configuration: EventSourceConfiguration let identifier: String? let event: String? @@ -77,9 +77,8 @@ public struct Event: CustomDebugStringConvertible { return nil } - - configuration = eventSource.configuration - self.metadata = Metadata(timestamp: Date(), hostUri: configuration.uri) + + self.metadata = Metadata(timestamp: Date(), hostUri: eventSource.manager!.connectionURL!.absoluteString) self.identifier = identifier self.event = event @@ -98,319 +97,3 @@ public struct Event: CustomDebugStringConvertible { return "Event {\(self.identifier != nil ? self.identifier! : "nil"), \(self.event != nil ? self.event! : "nil"), Data length: \(self.data != nil ? self.data!.count : 0)}" } } - -@objc -public final class PrimaryEventSource: EventSource { - - fileprivate var task: URLSessionDataTask? - fileprivate var children = Set() - fileprivate var retries = 0 - fileprivate let maxRetries = 3 - var session:URLSession? - - internal func add(child: ChildEventSource) { - - _ = self.queue.async { - self.children.insert(child) - } - } - - internal func remove(child: ChildEventSource) { - - _ = self.queue.async { - self.children.remove(child) - } - } - - public override func connect() { - _ = self.queue.async { - self.readyState = .connecting - - let sessionConfig = URLSessionConfiguration.default - sessionConfig.requestCachePolicy = .reloadIgnoringLocalCacheData - sessionConfig.timeoutIntervalForRequest = TimeInterval(5) - sessionConfig.timeoutIntervalForResource = TimeInterval(INT_MAX) - sessionConfig.httpAdditionalHeaders = ["Accept" : "text/event-stream", "Cache-Control" : "no-cache"] - - if self.session != nil { - self.session!.invalidateAndCancel() - } - - let session = Foundation.URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil) //This requires self be marked as @objc - - self.session = session - - var urlComponents = URLComponents() - urlComponents.host = self.configuration.hostAddress - urlComponents.path = self.configuration.endpoint - urlComponents.port = self.configuration.port - urlComponents.scheme = "http" //FIXME: This should be settable in config - - if let url = urlComponents.url { - - //print("URL: \(url)") - - self.task = session.dataTask(with: url) - self.task?.resume() - } - else { - //error - } - } - } - - public override func disconnect(allowRetry:Bool = true, completion:@escaping ()->() = {}) { - _ = self.queue.async { - self.session?.invalidateAndCancel() - self.session = nil - - guard let t = self.task, t.state != .canceling else { - completion() - return - } - - self.task?.cancel() - self.task = nil - self.readyState = .closed - - if allowRetry && self.retries < self.maxRetries { - self.retries = self.retries + 1 - self.connect() - } else { - self.delegate?.eventSourceWillDisconnect(self) - - self.delegate?.eventSourceDidDisconnect(self) - - for child in self.children { - child.delegate?.eventSourceWillDisconnect(child) - - child.delegate?.eventSourceDidDisconnect(child) - } - - completion() - } - } - } -} - -extension PrimaryEventSource: URLSessionDataDelegate { - - public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - - disconnect() - } - - public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { - - guard self.readyState != .closed else { - - //Discard any data from here on in - return - } - - guard let response = dataTask.response as? HTTPURLResponse else { - - return //not connected yet - } - - if self.readyState == .connecting { - - switch response.statusCode { - - case 200...299: - fallthrough - case 300...399: - self.delegate?.eventSourceDidConnect(self) - self.readyState = .open - self.retries = 0 - break - - case 400...499: - fallthrough - default: - self.delegate?.eventSource(self, didEncounterError: .sourceNotFound(response.statusCode)) - disconnect() - return - } - } - - inline_URLSession(session, dataTask: dataTask, didReceiveData: data) - } - - public func inline_URLSession(_ session: Foundation.URLSession, dataTask: URLSessionDataTask, didReceiveData data: Data) { - - func scan(_ scanner: Scanner, field:String) -> (String?) { - - let originalLocation = scanner.scanLocation - - scanner.scanUpTo("\(field):", into: nil) - - guard scanner.scanString("\(field):", into:nil) else { // not found, reset and return nil - scanner.scanLocation = originalLocation - return nil - } - - var value: NSString? - scanner.scanUpTo("\n", into: &value) - - // get rid of any newlines - scanner.scanCharacters(from: CharacterSet.whitespacesAndNewlines, into: nil) - - return value as String?; - } - - if let eventString = String(data: data, encoding: .utf8) { //NSString(bytes: data.withUnsafeBytes length: data.count, encoding: 4) { - - - let scanner = Scanner(string: eventString as String) - scanner.charactersToBeSkipped = CharacterSet.whitespaces - - repeat { - - var eventId: String?, eventName: String?, eventData: String? - - eventId = scan(scanner, field:"id") - - guard eventId != nil else { // finished - NSLog("SSEKit SSE - No id!") - return - } - - let loc = scanner.scanLocation - eventName = scan(scanner, field:"event") - scanner.scanLocation = loc // reset, as this is optional... - - // is this actually optional? -// guard eventName != nil else { // finished -// NSLog("SSEKit SSE - No event name!") -// return -// } - - eventData = scan(scanner, field:"data") - - guard eventData != nil else { // finished - NSLog("SSEKit SSE - No event data!") - return - } - - - - // Send all events to children - for child in self.children { - self.queue.async { - - if let event = Event(withEventSource: child, identifier: eventId, event: eventName, data: eventData?.data(using: String.Encoding.utf8)) { - child.eventSource(self, didReceiveEvent: event) - } - } - } - - // Don't create events if nobody is listerning - if let evnArray = self.configuration.events, let evn = eventName, evnArray.contains(evn) { - - self.queue.async { - - if let event = Event(withEventSource: self, identifier: eventId, event: evn, data: eventData?.data(using: String.Encoding.utf8)) { - self.delegate?.eventSource(self, didReceiveEvent: event) - } - } - } - else if self.configuration.events == nil { - - self.queue.async { - - if let event = Event(withEventSource: self, identifier: eventId, event: eventName, data: eventData?.data(using: String.Encoding.utf8)) { - self.delegate?.eventSource(self, didReceiveEvent: event) - } - } - } - - } while(!scanner.isAtEnd) - } - } -} - -@objc -public final class ChildEventSource: EventSource { - - weak public var primaryEventSource: PrimaryEventSource? - public required init(configuration: EventSourceConfiguration, delegate: EventSourceDelegate, queue:DispatchQueue) { - - super.init(configuration: configuration, delegate: delegate, queue:queue) - } - - internal convenience init(withConfiguration config: EventSourceConfiguration, primaryEventSource: PrimaryEventSource, delegate: EventSourceDelegate, queue:DispatchQueue) { - self.init(configuration: config, delegate: delegate, queue:queue) - self.primaryEventSource = primaryEventSource - } - - public required init(configuration: EventSourceConfiguration, delegate: EventSourceDelegate) { - fatalError("init(configuration:delegate:) has not been implemented") - } - - public override func connect() { - - // TODO: Return an error if there is a probelm with `primaryEventSource` - - //print("CHILD CONNECTED") - self.primaryEventSource?.add(child: self) - self.delegate?.eventSourceDidConnect(self) - } - - public override func disconnect(allowRetry:Bool = true, completion:@escaping ()->() = {}) { - - delegate?.eventSourceWillDisconnect(self) - self.readyState = .closed - delegate?.eventSourceDidDisconnect(self) - completion() - } -} - -extension ChildEventSource: EventSourceDelegate { - - public func eventSource(_ eventSource: EventSource, didChangeState state: ReadyState) { /* Ignore */ } - - public func eventSourceDidConnect(_ eventSource: EventSource) { /* Ignore */ } - - public func eventSourceWillDisconnect(_ eventSource: EventSource) { /* Ignore */ } - - public func eventSourceDidDisconnect(_ eventSource: EventSource) { - self.disconnect() - } - - public func eventSource(_ eventSource: EventSource, didReceiveEvent event: Event) { - - if let evnArray = self.configuration.events, let evn = event.event, evnArray.contains(evn) { - - self.queue.async { - - if let event = Event(withEventSource: self, identifier: event.identifier, event: event.event, data: event.data) { - self.delegate?.eventSource(self, didReceiveEvent: event) - } - } - } - else if self.configuration.events == nil { - - self.queue.async { - - if let event = Event(withEventSource: self, identifier: event.identifier, event: event.event, data: event.data) { - self.delegate?.eventSource(self, didReceiveEvent: event) - } - } - } - } - - public func eventSource(_ eventSource: EventSource, didEncounterError error: EventSourceError) { /* Ignore */ } -} - -public protocol EventSourceDelegate: class { - - func eventSource(_ eventSource: EventSource, didChangeState state: ReadyState) - - func eventSourceDidConnect(_ eventSource: EventSource) - func eventSourceWillDisconnect(_ eventSource: EventSource) - func eventSourceDidDisconnect(_ eventSource: EventSource) - - func eventSource(_ eventSource: EventSource, didReceiveEvent event: Event) - func eventSource(_ eventSource: EventSource, didEncounterError error: EventSourceError) -} diff --git a/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift b/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift index 6c22382..81f705a 100644 --- a/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift +++ b/Projects/SSEKit/SSEKit/EventSourceConfiguration.swift @@ -3,11 +3,14 @@ // SSEKit // // Created by Richard Stelling on 23/02/2016. -// Copyright © 2016 Richard Stelling All rights reserved. +// Copyright © 2016 Naim Audio All rights reserved. // import Foundation +// TODO: delete this file! +// leaving in for now in case we want to patch 5.13 + public struct EventSourceConfiguration { internal let name: String? @@ -21,7 +24,7 @@ public struct EventSourceConfiguration { internal let events: [String]? internal var uri: String { - return "\(self.hostAddress):\(self.port)\(self.endpoint)" + return "http:\(self.hostAddress):\(self.port)\(self.endpoint)" } //options? diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index 0a242c2..e036903 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -3,7 +3,7 @@ // SSEKit // // Created by Richard Stelling on 23/02/2016. -// Copyright © 2016 Richard Stelling All rights reserved. +// Copyright © 2016 Naim Audio All rights reserved. // import Foundation @@ -31,189 +31,356 @@ public extension SSEManager { } // MARK: - SSEManager -open class SSEManager { - private static var instanceCount = 0 // debug! +open class SSEManager : NSObject, URLSessionDelegate { + + public typealias CompletionClosure = (_ error: NSError?) -> (); + + public enum ConnectionState: Int { + case idle = 0 + case connecting = 1 + case connected = 2 + case disconnecting = 3 + } + + public internal(set) var connectionState:ConnectionState = .idle - fileprivate var primaryEventSource: PrimaryEventSource? // TODO: remove - 1 connection per manager - adds too much complexity (and certainly the cause of a few bugs - as order of add/remove source matters) + internal var session:URLSession? = nil + internal var sessionTask: URLSessionDataTask? = nil + internal var connectionURL: URL? = nil + + private static var instanceCount = 0 // debug! fileprivate var eventSources = Set() - fileprivate var queue = DispatchQueue(label: "com.naim.ssekit") + internal var queue = DispatchQueue(label: "com.naim.ssekit") + internal var connectionRetries = 0 + public var maxConnectionRetries = 3 + + fileprivate var shouldDisconnectOnLastSource = false // TODO: just used for old EventSourceConfiguration usage + + private var connectCompletionClosure: CompletionClosure? = nil - public init(sources: [EventSourceConfiguration]) { - - for config in sources { - _ = addEventSource(config) - } + public override init() { SSEManager.instanceCount = SSEManager.instanceCount + 1 NSLog("SSEManager init - instances \(SSEManager.instanceCount)") } + @available(*, deprecated, message: "EventSourceConfiguration to be removed") + public convenience init(sources: [EventSourceConfiguration]) { + self.init() + for eventSourceConfig in sources { + _ = self.addEventSource(eventSourceConfig) + } + } + deinit { - self.primaryEventSource = nil SSEManager.instanceCount = SSEManager.instanceCount - 1 NSLog("SSEManager dealloc - deinit \(SSEManager.instanceCount)") } - - /** - Add an EventSource to the manager. - */ - open func addEventSource(_ eventSourceConfig: EventSourceConfiguration) -> EventSource { - - var eventSource: EventSource! - - _ = self.queue.sync { // must be sync, need to check there is a primary source + + /// connect to the given SSE endpoint + /// + /// - Parameter url: url of the product probably http://:15081/notify + func connect(toURL url: URL, completion: CompletionClosure? = nil ) { + _ = self.queue.async { - if self.primaryEventSource == nil { - eventSource = PrimaryEventSource(configuration: eventSourceConfig, delegate: self, queue: self.queue) - self.primaryEventSource = eventSource as? PrimaryEventSource - precondition(self.eventSources.count == 0, "primary event source created AFTER other sources...bugs will arise...") + guard self.connectionState == .idle else { + completion?(NSError(domain:"com.naim.ssekit", code:1, userInfo:[NSLocalizedDescriptionKey: "Already connected/connecting"])) + return } - else { - eventSource = ChildEventSource(withConfiguration: eventSourceConfig, primaryEventSource: self.primaryEventSource!, delegate: self, queue:self.queue) + + self.connectCompletionClosure = completion + + self.connectionState = .connecting + + let sessionConfig = URLSessionConfiguration.default + sessionConfig.requestCachePolicy = .reloadIgnoringLocalCacheData + sessionConfig.timeoutIntervalForRequest = TimeInterval(5) // configuration + sessionConfig.timeoutIntervalForResource = TimeInterval(INT_MAX) + sessionConfig.httpAdditionalHeaders = ["Accept" : "text/event-stream", "Cache-Control" : "no-cache"] + + if self.session != nil { + self.session!.invalidateAndCancel() } + + let session = Foundation.URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil) //This requires self be marked as @objc + + self.session = session + + self.connectionURL = url + self.sessionTask = session.dataTask(with: url) + + self.sessionTask?.resume() + } + } + + /// disconnect the SSE connection socket + /// + /// - Parameter completion: completion closure + public func disconnect(completion:@escaping ()->() = {}) { - precondition(eventSource != nil, "Cannot be nil.") - _ = self.queue.async { - self.eventSources.insert(eventSource) + guard self.connectionState != .disconnecting && self.connectionState != .idle else { + completion() + return + } + + self.connectionState = .disconnecting + self.session?.invalidateAndCancel() + self.session = nil + + guard let t = self.sessionTask, t.state != .canceling else { + completion() + return + } + + self.sessionTask?.cancel() + self.sessionTask = nil + self.connectionState = .idle + + DispatchQueue.main.sync { + self.eventSources.forEach({ (eventSource) in + NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Disconnected.rawValue), object: eventSource, userInfo: [ Notification.Key.Source.rawValue : self.connectionURL!.absoluteString ]) + }) + } + + NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Disconnected.rawValue), object: self, userInfo: [ Notification.Key.Source.rawValue : self.connectionURL!.absoluteString ]) + + completion() } - - - eventSource?.connect() - - return eventSource } + - public func reconnect() { + /// create a new event source given the config. + /// + /// - Parameter eventSourceConfig: a populated EventSourceConfig instance + /// - Returns: the new EventSource instance + @available(*, deprecated, message: "EventSourceConfiguration to be removed, use connect and the addEventSource that takes strings") + open func addEventSource(_ eventSourceConfig: EventSourceConfiguration) -> EventSource { + self.queue.async { - if (self.eventSources.count > 0 && self.primaryEventSource?.readyState != .open && self.primaryEventSource?.readyState != .connecting) { - precondition(self.primaryEventSource != nil, "no primary event source!Dump other event sources: \(self.eventSources)") - - self.primaryEventSource?.connect() - for eventSource in self.eventSources { - if (eventSource != self.primaryEventSource) { - eventSource.connect() - } - } + if (self.connectionState == .idle) { + self.shouldDisconnectOnLastSource = true + self.connect(toURL: URL(string: eventSourceConfig.uri)!) } + } + return self.addEventSource(forEvents: eventSourceConfig.events ?? []) + } + + /// create a new event source that listens to the provided events + /// + /// - Parameter events: array of event name strings, if empty array [] listen to everything + /// - Returns: the EventSource instance + open func addEventSource(forEvents events:[String]) -> EventSource { + var eventSource: EventSource! + + eventSource = EventSource(withManager: self, events: events) + + _ = self.queue.async { + + precondition(eventSource != nil, "Cannot be nil.") + + self.eventSources.insert(eventSource) } + + return eventSource } - /** - Disconnect and remove EventSource from manager. - */ + + /// remove the event source from listening to events + /// + /// - Parameter eventSource: the EventSource instance to remove + /// - Parameter completion: completion closure open func removeEventSource(_ eventSource: EventSource, _ completion:@escaping ()->() = {}) { - - eventSource.disconnect(allowRetry: false, completion: { - self.queue.async { + self.queue.async { + + self.eventSources.remove(eventSource) - if (eventSource == self.primaryEventSource) { - NSLog("destroy primary event source") - self.primaryEventSource = nil - + if self.shouldDisconnectOnLastSource && self.eventSources.count == 0 { + self.disconnect() { + DispatchQueue.main.async { + completion() + } } - - self.eventSources.remove(eventSource) - + } + else { DispatchQueue.main.async { completion() } } - }) + + } } + /// remove all the listening event sources + /// + /// - Parameter completion: completion closure open func removeAllEventSources(_ completion:@escaping ()->()) { self.queue.async { - var count = self.eventSources.count - guard count != 0 else { - DispatchQueue.main.async { - completion() - } - return - } + self.eventSources.removeAll() - for eventSource in self.eventSources { - eventSource.disconnect(allowRetry: false) { - count = count - 1 - if count == 0 { - self.primaryEventSource = nil - self.eventSources.removeAll() - DispatchQueue.main.async { - completion() - } + if self.shouldDisconnectOnLastSource { + self.disconnect() { + DispatchQueue.main.async { + completion() } } } + else { + DispatchQueue.main.async { + completion() + } + } } } } -// MARK: - EventSourceDelegate -extension SSEManager: EventSourceDelegate { + +// MARK: - URLSessionDataDelegate +extension SSEManager: URLSessionDataDelegate { - public func eventSource(_ eventSource: EventSource, didChangeState state: ReadyState) { - - //TODO: Logging - print("State -> \(eventSource) -> \(state)") - } - - public func eventSourceDidConnect(_ eventSource: EventSource) { - DispatchQueue.main.async { - NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Connected.rawValue), object: eventSource, userInfo: [ Notification.Key.Source.rawValue : eventSource.configuration.uri ]) + public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + self.queue.async { + if let url = self.connectionURL, session == self.session { + guard self.connectionState != .disconnecting else { + self.connectionState = .idle + return + } + + self.connectionState = .idle + // session ended... + self.connectionRetries = self.connectionRetries + 1 + if (self.connectionRetries < self.maxConnectionRetries) { + self.connect(toURL: url) + } + else { + self.disconnect() { + } + } + } } } - - public func eventSourceWillDisconnect(_ eventSource: EventSource) {} - - public func eventSourceDidDisconnect(_ eventSource: EventSource) { - - //Remove disconnected EventSource objects from the array -// self.queue.async { - //TODO -// if let esIndex = self.eventSources.indexOf(eventSource) { -// self.eventSources.removeAtIndex(esIndex) -// } -// } - DispatchQueue.main.async { - NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Disconnected.rawValue), object: eventSource, userInfo: [ Notification.Key.Source.rawValue : eventSource.configuration.uri ]) - } - } - - public func eventSource(_ eventSource: EventSource, didReceiveEvent event: Event) { - - //print("[ES#: \(eventSources.count)] \(eventSource) -> \(event)") - - var userInfo: [String: AnyObject] = [Notification.Key.Source.rawValue : eventSource.configuration.uri as AnyObject, Notification.Key.Timestamp.rawValue : event.metadata.timestamp as AnyObject] - - if let identifier = event.identifier { - userInfo[Notification.Key.Identifier.rawValue] = identifier as AnyObject + + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + + guard self.connectionState != .idle, + self.connectionState != .disconnecting, + session == self.session else { + + //Discard any data from here on in + return } - - if let name = event.event { - userInfo[Notification.Key.Name.rawValue] = name as AnyObject + + guard let response = dataTask.response as? HTTPURLResponse else { + + return //not connected yet } - - if let data = event.data { - userInfo[Notification.Key.Data.rawValue] = data as AnyObject + + self.queue.async { + + if self.connectionState == .connecting { + + NSLog("\(response)") + switch response.statusCode { + + case 200...299: + fallthrough + case 300...399: + self.connectionState = .connected + self.connectionRetries = 0 + DispatchQueue.main.sync { + self.eventSources.forEach({ (eventSource) in + NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Connected.rawValue), object: eventSource, userInfo: [ Notification.Key.Source.rawValue : self.connectionURL!.absoluteString ]) + }) + self.connectCompletionClosure?(nil) + self.connectCompletionClosure = nil + NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Connected.rawValue), object: self, userInfo: [ Notification.Key.Source.rawValue : self.connectionURL!.absoluteString ]) + } + break + + case 400...499: + fallthrough + default: + self.disconnect { + } + return + } + } + + self.parseEventData(data) } + } + + /// parse the event data, create Event instances, and tell all EventSources about them + /// + /// - Parameter data: the event data received + /// + /// TODO - this should be fileprivate - just enabling for test, put back when mocked URLSession properly + internal func parseEventData(_ data: Data) { - if let jsonData = event.jsonData { - userInfo[Notification.Key.JSONData.rawValue] = jsonData as AnyObject + func scan(_ scanner: Scanner, field:String) -> (String?) { + + let originalLocation = scanner.scanLocation + + scanner.scanUpTo("\(field):", into: nil) + + guard scanner.scanString("\(field):", into:nil) else { // not found, reset and return nil + scanner.scanLocation = originalLocation + return nil + } + + var value: NSString? + scanner.scanUpTo("\n", into: &value) + + // get rid of any newlines + scanner.scanCharacters(from: CharacterSet.whitespacesAndNewlines, into: nil) + + return value as String?; } - DispatchQueue.main.async { - NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Event.rawValue), object: eventSource, userInfo: userInfo) + if let eventString = String(data: data, encoding: .utf8) { //NSString(bytes: data.withUnsafeBytes length: data.count, encoding: 4) { + + + let scanner = Scanner(string: eventString as String) + scanner.charactersToBeSkipped = CharacterSet.whitespaces + + repeat { + + var eventId: String?, eventName: String?, eventData: String? + + eventId = scan(scanner, field:"id") + + guard eventId != nil else { // finished + NSLog("SSEKit SSE - No id!") + return + } + + let loc = scanner.scanLocation + eventName = scan(scanner, field:"event") + scanner.scanLocation = loc // reset, as this is optional... + + eventData = scan(scanner, field:"data") + + guard eventData != nil else { // finished + NSLog("SSEKit SSE - No event data!") + return + } + + // Send events to all event sources + for eventSource in self.eventSources { + self.queue.async { + + if let event = Event(withEventSource: eventSource, identifier: eventId, event: eventName ?? "", data: eventData?.data(using: String.Encoding.utf8)) { + eventSource.handleEvent(event) + } + } + } + + } while(!scanner.isAtEnd) } } - - public func eventSource(_ eventSource: EventSource, didEncounterError error: EventSourceError) { - - //TODO: Send error notification - //print("Error -> \(eventSource) -> \(error)") - } } diff --git a/Projects/SSEKit/SSEKitTests/SSEKitTests.swift b/Projects/SSEKit/SSEKitTests/SSEKitTests.swift index 0356650..523e1ac 100644 --- a/Projects/SSEKit/SSEKitTests/SSEKitTests.swift +++ b/Projects/SSEKit/SSEKitTests/SSEKitTests.swift @@ -3,12 +3,17 @@ // SSEKitTests // // Created by Richard Stelling on 23/02/2016. -// Copyright © 2016 Richard Stelling All rights reserved. +// Copyright © 2016 Naim Audio All rights reserved. // import XCTest @testable import SSEKit +let DUT_SSE_Endpoint = URL(string: "http://192.168.0.20:15081/notify")! + +// TODO: mock/stub URLSession & Data task + + class SSEKitTests: XCTestCase { override func setUp() { @@ -21,14 +26,111 @@ class SSEKitTests: XCTestCase { super.tearDown() } - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. + func testConnectSendDisconnectOK() { + + let connectExpectation = self.expectation(description: "should connect ok") + let connectEventExpectation = self.expectation(description: "should get connect event") + let disconnectEventExpectation = self.expectation(description: "has sent disconnection event") + let npEventExpectation = self.expectation(description: "has sent now playing event") + let disconnectExpectation = self.expectation(description: "should disconnect ok") + + let manager = SSEManager() + manager.connect(toURL: DUT_SSE_Endpoint) { (error) in + XCTAssert(error == nil) + XCTAssert(Thread.isMainThread, "closure should be on main thread") + connectExpectation.fulfill() + } + + let eventSource = manager.addEventSource(forEvents: []) // listen to all events + let cheeseSource = manager.addEventSource(forEvents: ["cheese"]) // listen for cheese + + let observer = NotificationCenter.default.addObserver(forName: Notification.Name(SSEManager.Notification.Connected.rawValue), object:eventSource, queue: nil) { (notification) in + + XCTAssert(Thread.isMainThread, "Notifications should be on main thread") + XCTAssert(notification.object as! EventSource == eventSource, "should be eventSource") + + // simulate cheese event + manager.parseEventData(Data("id:11713\nevent:cheese\ndata:{\"version\":\"1\",\"ussi\":\"cheese\",\"id\":\"1\",\"parameters\":{\"transportPosition\":\"8872\"}}".utf8)) + + connectEventExpectation.fulfill() + } + + + let npObserver = NotificationCenter.default.addObserver(forName: Notification.Name(SSEManager.Notification.Event.rawValue), object:cheeseSource, queue: nil) { (notification) in + + XCTAssert(Thread.isMainThread, "Notifications should be on main thread") + XCTAssert(notification.object as! EventSource == cheeseSource, "should be cheeseSource") + + // we are done, lets diconnect + manager.disconnect() { + XCTAssert(manager.connectionState == .idle, "should be in idle state at the end") + disconnectExpectation.fulfill() + } + + npEventExpectation.fulfill() + } + + + let disconnectObserver = NotificationCenter.default.addObserver(forName: Notification.Name(SSEManager.Notification.Disconnected.rawValue), object:eventSource, queue: nil) { (notification) in + XCTAssert(Thread.isMainThread, "Notifications should be on main thread") + disconnectEventExpectation.fulfill() + } + + self.waitForExpectations(timeout: 50) { (error) in + XCTAssertNil(error) + + NotificationCenter.default.removeObserver(observer) + NotificationCenter.default.removeObserver(disconnectObserver) + NotificationCenter.default.removeObserver(npObserver) + } + } + + func testConnectSendOK_depricated() { // use the old way + + let connectExpectation = self.expectation(description: "should connect ok") + let cheeseEventExpectation = self.expectation(description: "has sent cheese event") + let disconnectExpectation = self.expectation(description: "should disconnect ok") + + let manager = SSEManager(sources: []) + + let eventSource = manager.addEventSource(EventSourceConfiguration(withHost: DUT_SSE_Endpoint.host!, port: DUT_SSE_Endpoint.port!, endpoint: DUT_SSE_Endpoint.path, timeout: 5, events: nil, name: "everything!")) + let cheeseSource = manager.addEventSource(EventSourceConfiguration(withHost: DUT_SSE_Endpoint.host!, port: DUT_SSE_Endpoint.port!, endpoint: DUT_SSE_Endpoint.path, timeout: 5, events: ["cheese"], name: "cheese!")) + + let observer = NotificationCenter.default.addObserver(forName: Notification.Name(SSEManager.Notification.Connected.rawValue), object:eventSource, queue: nil) { (notification) in + + XCTAssert(Thread.isMainThread, "Notifications should be on main thread") + + connectExpectation.fulfill() + + // simulate cheese event + manager.parseEventData(Data("id:11713\nevent:cheese\ndata:{\"version\":\"1\",\"ussi\":\"cheese\",\"id\":\"1\",\"parameters\":{\"transportPosition\":\"8872\"}}".utf8)) + + } + + let cheeseObserver = NotificationCenter.default.addObserver(forName: Notification.Name(SSEManager.Notification.Event.rawValue), object:cheeseSource, queue: nil) { (notification) in + + XCTAssert(Thread.isMainThread, "Notifications should be on main thread") + XCTAssert(notification.object as! EventSource == cheeseSource, "should be cheeseSource") + + manager.removeAllEventSources { + XCTAssert(manager.connectionState == .idle, "should be in idle state at the end") + disconnectExpectation.fulfill() + } + + cheeseEventExpectation.fulfill() + } + + self.waitForExpectations(timeout: 5) { (error) in + XCTAssertNil(error) + + NotificationCenter.default.removeObserver(observer) + NotificationCenter.default.removeObserver(cheeseObserver) + } } func testPerformanceExample() { // This is an example of a performance test case. - self.measureBlock { + self.measure { // Put the code you want to measure the time of here. } } From 0062e121021d23a964253d040413a21263b10d3d Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 10 May 2019 10:26:30 +0100 Subject: [PATCH 07/22] APP-2737 make connect method public --- Projects/SSEKit/SSEKit/SSEManager.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index e036903..833accb 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -80,7 +80,7 @@ open class SSEManager : NSObject, URLSessionDelegate { /// connect to the given SSE endpoint /// /// - Parameter url: url of the product probably http://:15081/notify - func connect(toURL url: URL, completion: CompletionClosure? = nil ) { + open func connect(toURL url: URL, completion: CompletionClosure? = nil ) { _ = self.queue.async { guard self.connectionState == .idle else { @@ -117,7 +117,7 @@ open class SSEManager : NSObject, URLSessionDelegate { /// disconnect the SSE connection socket /// /// - Parameter completion: completion closure - public func disconnect(completion:@escaping ()->() = {}) { + open func disconnect(completion:@escaping ()->() = {}) { _ = self.queue.async { guard self.connectionState != .disconnecting && self.connectionState != .idle else { From d1c8174c1ac4b47e957ebbb7c383c0232900c6d2 Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 10 May 2019 11:43:14 +0100 Subject: [PATCH 08/22] APP-2737 dont require closure on removeAllEventSources --- Projects/SSEKit/SSEKit/SSEManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index 833accb..bfe7918 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -217,7 +217,7 @@ open class SSEManager : NSObject, URLSessionDelegate { /// remove all the listening event sources /// /// - Parameter completion: completion closure - open func removeAllEventSources(_ completion:@escaping ()->()) { + open func removeAllEventSources(_ completion:@escaping ()->() = {}) { self.queue.async { From 7573c577eba7e626e2118ff1432472513ca14260 Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 10 May 2019 14:40:28 +0100 Subject: [PATCH 09/22] APP-2737 fix issues with error states and add test cases --- Projects/SSEKit/SSEKit/SSEManager.swift | 14 +++-- Projects/SSEKit/SSEKitTests/SSEKitTests.swift | 52 ++++++++++++++++++- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index bfe7918..43743d8 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -119,7 +119,7 @@ open class SSEManager : NSObject, URLSessionDelegate { /// - Parameter completion: completion closure open func disconnect(completion:@escaping ()->() = {}) { - _ = self.queue.async { + self.queue.async { guard self.connectionState != .disconnecting && self.connectionState != .idle else { completion() return @@ -142,10 +142,10 @@ open class SSEManager : NSObject, URLSessionDelegate { self.eventSources.forEach({ (eventSource) in NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Disconnected.rawValue), object: eventSource, userInfo: [ Notification.Key.Source.rawValue : self.connectionURL!.absoluteString ]) }) + + NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Disconnected.rawValue), object: self, userInfo: [ Notification.Key.Source.rawValue : self.connectionURL!.absoluteString ]) } - NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: Notification.Disconnected.rawValue), object: self, userInfo: [ Notification.Key.Source.rawValue : self.connectionURL!.absoluteString ]) - completion() } } @@ -251,13 +251,17 @@ extension SSEManager: URLSessionDataDelegate { return } - self.connectionState = .idle // session ended... self.connectionRetries = self.connectionRetries + 1 if (self.connectionRetries < self.maxConnectionRetries) { - self.connect(toURL: url) + self.connectionState = .idle + self.connect(toURL: url, completion: self.connectCompletionClosure) } else { + DispatchQueue.main.sync { + self.connectCompletionClosure?(error as NSError?) + self.connectCompletionClosure = nil + } self.disconnect() { } } diff --git a/Projects/SSEKit/SSEKitTests/SSEKitTests.swift b/Projects/SSEKit/SSEKitTests/SSEKitTests.swift index 523e1ac..f6b24de 100644 --- a/Projects/SSEKit/SSEKitTests/SSEKitTests.swift +++ b/Projects/SSEKit/SSEKitTests/SSEKitTests.swift @@ -76,7 +76,7 @@ class SSEKitTests: XCTestCase { disconnectEventExpectation.fulfill() } - self.waitForExpectations(timeout: 50) { (error) in + self.waitForExpectations(timeout: 15) { (error) in XCTAssertNil(error) NotificationCenter.default.removeObserver(observer) @@ -127,6 +127,56 @@ class SSEKitTests: XCTestCase { NotificationCenter.default.removeObserver(cheeseObserver) } } + + func testConnectNOK() { + let connectResponseExpectation = self.expectation(description: "should call connect closure") + + + let manager = SSEManager() + + manager.maxConnectionRetries = 2 // or test takes too long! + + // connect to an invalid ip (get a request timed out) + manager.connect(toURL: URL(string: "http://999.999.999.999:15081/notify")!, completion: { (error) in + XCTAssert(error != nil) + XCTAssert(Thread.isMainThread, "closure should be on main thread") + connectResponseExpectation.fulfill() + }) + + self.waitForExpectations(timeout: 15) { (error) in + XCTAssertNil(error) + } + } + + + func testConnectOKThenFail() { + let connectResponseExpectation = self.expectation(description: "should call connect closure") + let disconnectEventExpectation = self.expectation(description: "should disconnect") + + let manager = SSEManager() + + manager.maxConnectionRetries = 0 // stop retries + + manager.connect(toURL: DUT_SSE_Endpoint, completion: { (error) in + XCTAssert(error == nil) + XCTAssert(Thread.isMainThread, "closure should be on main thread") + connectResponseExpectation.fulfill() + + // force an error + manager.urlSession(manager.session!, task: manager.sessionTask!, didCompleteWithError: NSError(domain: "com.naim.ssekittest", code: 1, userInfo: [:]) as Error) + }) + + let disconnectObserver = NotificationCenter.default.addObserver(forName: Notification.Name(SSEManager.Notification.Disconnected.rawValue), object:manager, queue: nil) { (notification) in + XCTAssert(Thread.isMainThread, "Notifications should be on main thread") + disconnectEventExpectation.fulfill() + } + + self.waitForExpectations(timeout: 15) { (error) in + XCTAssertNil(error) + XCTAssert(manager.connectionState == SSEManager.ConnectionState.idle, "should be idle") + NotificationCenter.default.removeObserver(disconnectObserver) + } + } func testPerformanceExample() { // This is an example of a performance test case. From 10b6c8a6890e4cf20087dc455d59204e788fafee Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Mon, 10 Jun 2019 14:30:18 +0100 Subject: [PATCH 10/22] APP-2788 support swift5 --- Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj index d495e42..e7fdc91 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj +++ b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj @@ -199,7 +199,7 @@ 2AA0D22F1C7C9A3B006E83D0 = { CreatedOnToolsVersion = 7.3; DevelopmentTeam = 99935HA2E3; - LastSwiftMigration = 0900; + LastSwiftMigration = 1020; }; 2AA0D2391C7C9A3B006E83D0 = { CreatedOnToolsVersion = 7.3; @@ -422,8 +422,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKit; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -441,8 +440,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKit; SKIP_INSTALL = YES; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Release; }; From 4aacec9bf0ad0502463b3e342e31f1b2a4831ebd Mon Sep 17 00:00:00 2001 From: Tom Johnson <> Date: Thu, 13 Jun 2019 14:26:28 +0100 Subject: [PATCH 11/22] Reduced amount of default logging --- Projects/SSEKit/SSEKit/SSEManager.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index 43743d8..6045667 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -61,7 +61,7 @@ open class SSEManager : NSObject, URLSessionDelegate { public override init() { SSEManager.instanceCount = SSEManager.instanceCount + 1 - NSLog("SSEManager init - instances \(SSEManager.instanceCount)") +// NSLog("SSEManager init - instances \(SSEManager.instanceCount)") } @available(*, deprecated, message: "EventSourceConfiguration to be removed") @@ -74,7 +74,7 @@ open class SSEManager : NSObject, URLSessionDelegate { deinit { SSEManager.instanceCount = SSEManager.instanceCount - 1 - NSLog("SSEManager dealloc - deinit \(SSEManager.instanceCount)") +// NSLog("SSEManager dealloc - deinit \(SSEManager.instanceCount)") } /// connect to the given SSE endpoint @@ -288,7 +288,7 @@ extension SSEManager: URLSessionDataDelegate { if self.connectionState == .connecting { - NSLog("\(response)") +// NSLog("\(response)") switch response.statusCode { case 200...299: From b2affd9d753687e554f3bea9c1a30ec9962e3e47 Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Tue, 27 Aug 2019 14:23:56 +0100 Subject: [PATCH 12/22] APP-3077 SSE event logging support --- Projects/SSEKit/SSEKit/SSEManager.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index 6045667..4701297 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -54,6 +54,7 @@ open class SSEManager : NSObject, URLSessionDelegate { internal var queue = DispatchQueue(label: "com.naim.ssekit") internal var connectionRetries = 0 public var maxConnectionRetries = 3 + public var logEvents: Set? = nil // log all events on empty set, no events on nil fileprivate var shouldDisconnectOnLastSource = false // TODO: just used for old EventSourceConfiguration usage @@ -374,6 +375,12 @@ extension SSEManager: URLSessionDataDelegate { return } + // SSE EVENT LOGGING + // On if empty set, or specific event matches name + if let logEvents = self.logEvents, logEvents.count == 0 || (eventName != nil && logEvents.contains(eventName!)) { + NSLog("🔵 SSE EVENT:\(self.connectionURL!.host!) \(eventName ?? "nil") - \(eventData!)") + } + // Send events to all event sources for eventSource in self.eventSources { self.queue.async { From 535da5b03f6c3c3e9056db0bc1fb839c73c0446a Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Wed, 18 Nov 2020 16:15:48 +0000 Subject: [PATCH 13/22] APP-4585 fix xcode 12 compiler warnings --- .../SSEKit/SSEKit.xcodeproj/project.pbxproj | 16 ++++++----- .../xcschemes/SSEKit iOS.xcscheme | 28 ++++++++----------- .../xcschemes/SSEKit macOS.xcscheme | 6 +--- Projects/SSEKit/SSEKit/SSEManager.swift | 4 +-- 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj index e7fdc91..1016b12 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj +++ b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj @@ -193,7 +193,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1220; ORGANIZATIONNAME = "Naim Audio Ltd"; TargetAttributes = { 2AA0D22F1C7C9A3B006E83D0 = { @@ -318,6 +318,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -341,7 +342,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = SSEKit; @@ -377,6 +378,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -394,7 +396,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = SSEKit; SDKROOT = iphoneos; @@ -417,7 +419,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "SSEKit/Info-iOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKit; SKIP_INSTALL = YES; @@ -436,7 +438,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "SSEKit/Info-iOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKit; SKIP_INSTALL = YES; @@ -448,7 +450,7 @@ isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = SSEKitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -460,7 +462,7 @@ isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = SSEKitTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme b/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme index 59fcabf..695a324 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme +++ b/Projects/SSEKit/SSEKit.xcodeproj/xcshareddata/xcschemes/SSEKit iOS.xcscheme @@ -1,6 +1,6 @@ + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> @@ -47,6 +47,15 @@ + + + + @@ -59,17 +68,6 @@ - - - - - - - - - - - - :15081/notify open func connect(toURL url: URL, completion: CompletionClosure? = nil ) { - _ = self.queue.async { + self.queue.async { guard self.connectionState == .idle else { completion?(NSError(domain:"com.naim.ssekit", code:1, userInfo:[NSLocalizedDescriptionKey: "Already connected/connecting"])) @@ -177,7 +177,7 @@ open class SSEManager : NSObject, URLSessionDelegate { eventSource = EventSource(withManager: self, events: events) - _ = self.queue.async { + self.queue.async { precondition(eventSource != nil, "Cannot be nil.") From d7bbb105f51c81ebf76de8fdfce90b758c02b332 Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 12 Feb 2021 12:13:10 +0000 Subject: [PATCH 14/22] APP-5030 fix potential of SSEKit getting stuck in wrong state --- Projects/SSEKit/SSEKit/SSEManager.swift | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index 309a201..330ffdb 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -42,7 +42,17 @@ open class SSEManager : NSObject, URLSessionDelegate { case disconnecting = 3 } - public internal(set) var connectionState:ConnectionState = .idle + // TODO: remove + public var _connectionState = ConnectionState.idle + public internal(set) var connectionState:ConnectionState { + get { + return _connectionState + } + set { + NSLog("\(self) SSEKit state \(_connectionState.rawValue) -> \(newValue.rawValue)") + _connectionState = newValue + } + } internal var session:URLSession? = nil internal var sessionTask: URLSessionDataTask? = nil @@ -130,11 +140,6 @@ open class SSEManager : NSObject, URLSessionDelegate { self.session?.invalidateAndCancel() self.session = nil - guard let t = self.sessionTask, t.state != .canceling else { - completion() - return - } - self.sessionTask?.cancel() self.sessionTask = nil self.connectionState = .idle From 6f880e6637bdf81863dccea2466a6fc5fca3fbef Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 12 Feb 2021 14:57:54 +0000 Subject: [PATCH 15/22] APP-5030 remove extra console logs --- Projects/SSEKit/SSEKit/SSEManager.swift | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index 330ffdb..de43e67 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -42,17 +42,7 @@ open class SSEManager : NSObject, URLSessionDelegate { case disconnecting = 3 } - // TODO: remove - public var _connectionState = ConnectionState.idle - public internal(set) var connectionState:ConnectionState { - get { - return _connectionState - } - set { - NSLog("\(self) SSEKit state \(_connectionState.rawValue) -> \(newValue.rawValue)") - _connectionState = newValue - } - } + public internal(set) var connectionState:ConnectionState = ConnectionState.idle internal var session:URLSession? = nil internal var sessionTask: URLSessionDataTask? = nil From bb5f31c06cac476af6218b64232760212de3e1bb Mon Sep 17 00:00:00 2001 From: JoshParadroid Date: Thu, 28 Oct 2021 17:17:10 +0100 Subject: [PATCH 16/22] APP-5893: SSEKit logging working. --- Projects/SSEKit/SSEKit/SSEManager.swift | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index de43e67..67f4b6b 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -7,6 +7,7 @@ // import Foundation +import NaimKit // MARK: Notifications public extension SSEManager { @@ -62,7 +63,6 @@ open class SSEManager : NSObject, URLSessionDelegate { public override init() { SSEManager.instanceCount = SSEManager.instanceCount + 1 -// NSLog("SSEManager init - instances \(SSEManager.instanceCount)") } @available(*, deprecated, message: "EventSourceConfiguration to be removed") @@ -75,7 +75,6 @@ open class SSEManager : NSObject, URLSessionDelegate { deinit { SSEManager.instanceCount = SSEManager.instanceCount - 1 -// NSLog("SSEManager dealloc - deinit \(SSEManager.instanceCount)") } /// connect to the given SSE endpoint @@ -284,7 +283,6 @@ extension SSEManager: URLSessionDataDelegate { if self.connectionState == .connecting { -// NSLog("\(response)") switch response.statusCode { case 200...299: @@ -353,34 +351,41 @@ extension SSEManager: URLSessionDataDelegate { var eventId: String?, eventName: String?, eventData: String? eventId = scan(scanner, field:"id") + var logger = Logger(subsystem: "SSEKit", category: "SSEManager") - guard eventId != nil else { // finished - NSLog("SSEKit SSE - No id!") + guard let eventId = eventId else { // finished + logger.error("No id!") return } + logger = logger.with(properties: ["eventId" : eventId]) + let loc = scanner.scanLocation eventName = scan(scanner, field:"event") + logger = logger.with(properties: ["eventName" : eventName ?? "UNKNOWN EVENT NAME"]) + scanner.scanLocation = loc // reset, as this is optional... eventData = scan(scanner, field:"data") - guard eventData != nil else { // finished - NSLog("SSEKit SSE - No event data!") + guard let eventData = eventData else { // finished + logger.error("No event data!") return } + logger = logger.with(properties: ["eventData" : eventData]) + // SSE EVENT LOGGING // On if empty set, or specific event matches name if let logEvents = self.logEvents, logEvents.count == 0 || (eventName != nil && logEvents.contains(eventName!)) { - NSLog("🔵 SSE EVENT:\(self.connectionURL!.host!) \(eventName ?? "nil") - \(eventData!)") + logger.info("EVENT:\(self.connectionURL!.host!) \(eventName ?? "nil")") } // Send events to all event sources for eventSource in self.eventSources { self.queue.async { - if let event = Event(withEventSource: eventSource, identifier: eventId, event: eventName ?? "", data: eventData?.data(using: String.Encoding.utf8)) { + if let event = Event(withEventSource: eventSource, identifier: eventId, event: eventName ?? "", data: eventData.data(using: String.Encoding.utf8)) { eventSource.handleEvent(event) } } From ba43c0319f39613d5377c9c828a704e95566bd57 Mon Sep 17 00:00:00 2001 From: JoshParadroid Date: Mon, 1 Nov 2021 16:41:23 +0000 Subject: [PATCH 17/22] APP-5893: Added IP address into the log object. --- Projects/SSEKit/SSEKit/SSEManager.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index 67f4b6b..6eedc4a 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -351,7 +351,7 @@ extension SSEManager: URLSessionDataDelegate { var eventId: String?, eventName: String?, eventData: String? eventId = scan(scanner, field:"id") - var logger = Logger(subsystem: "SSEKit", category: "SSEManager") + var logger = Logger(subsystem: "SSEKit", category: "SSEManager", ipAddress: self.connectionURL.host) guard let eventId = eventId else { // finished logger.error("No id!") @@ -378,7 +378,7 @@ extension SSEManager: URLSessionDataDelegate { // SSE EVENT LOGGING // On if empty set, or specific event matches name if let logEvents = self.logEvents, logEvents.count == 0 || (eventName != nil && logEvents.contains(eventName!)) { - logger.info("EVENT:\(self.connectionURL!.host!) \(eventName ?? "nil")") + logger.info("EVENT: \(eventName ?? "nil")") } // Send events to all event sources From 5e97cc49eb3f7db52d9049d7b43d0df7b7d3b910 Mon Sep 17 00:00:00 2001 From: JoshParadroid Date: Tue, 2 Nov 2021 15:15:58 +0000 Subject: [PATCH 18/22] APP-5893: Fixed missing unwrap. --- Projects/SSEKit/SSEKit/SSEManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index 6eedc4a..f4cc4f6 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -351,7 +351,7 @@ extension SSEManager: URLSessionDataDelegate { var eventId: String?, eventName: String?, eventData: String? eventId = scan(scanner, field:"id") - var logger = Logger(subsystem: "SSEKit", category: "SSEManager", ipAddress: self.connectionURL.host) + var logger = Logger(subsystem: "SSEKit", category: "SSEManager", ipAddress: self.connectionURL?.host) guard let eventId = eventId else { // finished logger.error("No id!") From dceebbe870a9daf603b93a95a6e6eeb6fc52dda5 Mon Sep 17 00:00:00 2001 From: JoshParadroid Date: Tue, 2 Nov 2021 16:20:02 +0000 Subject: [PATCH 19/22] APP-5893: Changed shadowing issue and added dependency of NaimKit. --- .../SSEKit/SSEKit.xcodeproj/project.pbxproj | 28 +++++++++++++++++++ Projects/SSEKit/SSEKit/SSEManager.swift | 10 +++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj index 1016b12..cb84edd 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj +++ b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 12EC047E2731990700FA5116 /* NaimKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12EC047D2731990700FA5116 /* NaimKit.framework */; }; + 12EC047F2731990700FA5116 /* NaimKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12EC047D2731990700FA5116 /* NaimKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 2A18FBC41C7CB8A80052DFCE /* EventSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC31C7CB8A80052DFCE /* EventSource.swift */; }; 2A18FBC61C7CB8C40052DFCE /* EventSourceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC51C7CB8C40052DFCE /* EventSourceConfiguration.swift */; }; 2A18FBC81C7CB8EF0052DFCE /* SSEManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC71C7CB8EF0052DFCE /* SSEManager.swift */; }; @@ -29,7 +31,22 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 12EC04802731990700FA5116 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 12EC047F2731990700FA5116 /* NaimKit.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ + 12EC047D2731990700FA5116 /* NaimKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = NaimKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2A18FBC31C7CB8A80052DFCE /* EventSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventSource.swift; sourceTree = ""; }; 2A18FBC51C7CB8C40052DFCE /* EventSourceConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventSourceConfiguration.swift; sourceTree = ""; }; 2A18FBC71C7CB8EF0052DFCE /* SSEManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSEManager.swift; sourceTree = ""; }; @@ -48,6 +65,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 12EC047E2731990700FA5116 /* NaimKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -69,12 +87,21 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 12EC047C2731990700FA5116 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 12EC047D2731990700FA5116 /* NaimKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 2AA0D2261C7C9A3B006E83D0 = { isa = PBXGroup; children = ( 2AA0D2321C7C9A3B006E83D0 /* SSEKit */, 2AA0D23E1C7C9A3B006E83D0 /* SSEKitTests */, 2AA0D2311C7C9A3B006E83D0 /* Products */, + 12EC047C2731990700FA5116 /* Frameworks */, ); sourceTree = ""; }; @@ -140,6 +167,7 @@ 2AA0D22C1C7C9A3B006E83D0 /* Frameworks */, 2AA0D22D1C7C9A3B006E83D0 /* Headers */, 2AA0D22E1C7C9A3B006E83D0 /* Resources */, + 12EC04802731990700FA5116 /* Embed Frameworks */, ); buildRules = ( ); diff --git a/Projects/SSEKit/SSEKit/SSEManager.swift b/Projects/SSEKit/SSEKit/SSEManager.swift index f4cc4f6..6f7ecf0 100644 --- a/Projects/SSEKit/SSEKit/SSEManager.swift +++ b/Projects/SSEKit/SSEKit/SSEManager.swift @@ -353,12 +353,12 @@ extension SSEManager: URLSessionDataDelegate { eventId = scan(scanner, field:"id") var logger = Logger(subsystem: "SSEKit", category: "SSEManager", ipAddress: self.connectionURL?.host) - guard let eventId = eventId else { // finished + guard let unwrappedEventId = eventId else { // finished logger.error("No id!") return } - logger = logger.with(properties: ["eventId" : eventId]) + logger = logger.with(properties: ["eventId" : unwrappedEventId]) let loc = scanner.scanLocation eventName = scan(scanner, field:"event") @@ -368,12 +368,12 @@ extension SSEManager: URLSessionDataDelegate { eventData = scan(scanner, field:"data") - guard let eventData = eventData else { // finished + guard let unwrappedEventData = eventData else { // finished logger.error("No event data!") return } - logger = logger.with(properties: ["eventData" : eventData]) + logger = logger.with(properties: ["eventData" : unwrappedEventData]) // SSE EVENT LOGGING // On if empty set, or specific event matches name @@ -385,7 +385,7 @@ extension SSEManager: URLSessionDataDelegate { for eventSource in self.eventSources { self.queue.async { - if let event = Event(withEventSource: eventSource, identifier: eventId, event: eventName ?? "", data: eventData.data(using: String.Encoding.utf8)) { + if let event = Event(withEventSource: eventSource, identifier: unwrappedEventId, event: eventName ?? "", data: unwrappedEventData.data(using: String.Encoding.utf8)) { eventSource.handleEvent(event) } } From 42a703af74f09aa66e53799df57aaf1a59188d21 Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 12 Nov 2021 13:28:49 +0000 Subject: [PATCH 20/22] APP-6004 remove embedded NaimKit --- Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj index cb84edd..65d6408 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj +++ b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 12EC047E2731990700FA5116 /* NaimKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12EC047D2731990700FA5116 /* NaimKit.framework */; }; - 12EC047F2731990700FA5116 /* NaimKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 12EC047D2731990700FA5116 /* NaimKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 2A18FBC41C7CB8A80052DFCE /* EventSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC31C7CB8A80052DFCE /* EventSource.swift */; }; 2A18FBC61C7CB8C40052DFCE /* EventSourceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC51C7CB8C40052DFCE /* EventSourceConfiguration.swift */; }; 2A18FBC81C7CB8EF0052DFCE /* SSEManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC71C7CB8EF0052DFCE /* SSEManager.swift */; }; @@ -38,7 +37,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 12EC047F2731990700FA5116 /* NaimKit.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; From a514716270d6c9e0cd57e24095ac62b06058963d Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 10 Dec 2021 11:03:54 +0000 Subject: [PATCH 21/22] APP-6121 builds with carthage --use-xcframeworks --- .../SSEKit/SSEKit.xcodeproj/project.pbxproj | 150 ++++-------------- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + 2 files changed, 37 insertions(+), 121 deletions(-) create mode 100644 SSEKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj index 65d6408..c6a110b 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj +++ b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj @@ -3,21 +3,18 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ - 12EC047E2731990700FA5116 /* NaimKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12EC047D2731990700FA5116 /* NaimKit.framework */; }; + 011CBDF32762597A00712220 /* NaimKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 011CBDF22762597A00712220 /* NaimKit.xcframework */; }; + 011CBDF42762597A00712220 /* NaimKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 011CBDF22762597A00712220 /* NaimKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 2A18FBC41C7CB8A80052DFCE /* EventSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC31C7CB8A80052DFCE /* EventSource.swift */; }; 2A18FBC61C7CB8C40052DFCE /* EventSourceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC51C7CB8C40052DFCE /* EventSourceConfiguration.swift */; }; 2A18FBC81C7CB8EF0052DFCE /* SSEManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC71C7CB8EF0052DFCE /* SSEManager.swift */; }; 2AA0D2341C7C9A3B006E83D0 /* SSEKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2AA0D2331C7C9A3B006E83D0 /* SSEKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2AA0D23B1C7C9A3B006E83D0 /* SSEKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AA0D2301C7C9A3B006E83D0 /* SSEKit.framework */; }; 2AA0D2401C7C9A3B006E83D0 /* SSEKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA0D23F1C7C9A3B006E83D0 /* SSEKitTests.swift */; }; - 2AB7AD181D53743600A5F64A /* SSEKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2AA0D2331C7C9A3B006E83D0 /* SSEKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2AB7AD761D53835E00A5F64A /* SSEManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC71C7CB8EF0052DFCE /* SSEManager.swift */; }; - 2AB7AD771D53835E00A5F64A /* EventSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC31C7CB8A80052DFCE /* EventSource.swift */; }; - 2AB7AD781D53835E00A5F64A /* EventSourceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC51C7CB8C40052DFCE /* EventSourceConfiguration.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -37,6 +34,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 011CBDF42762597A00712220 /* NaimKit.xcframework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -44,6 +42,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 011CBDF22762597A00712220 /* NaimKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = NaimKit.xcframework; path = ../../../../Build/NaimKit.xcframework; sourceTree = ""; }; 12EC047D2731990700FA5116 /* NaimKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = NaimKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2A18FBC31C7CB8A80052DFCE /* EventSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventSource.swift; sourceTree = ""; }; 2A18FBC51C7CB8C40052DFCE /* EventSourceConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventSourceConfiguration.swift; sourceTree = ""; }; @@ -54,7 +53,6 @@ 2AA0D23A1C7C9A3B006E83D0 /* SSEKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SSEKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 2AA0D23F1C7C9A3B006E83D0 /* SSEKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSEKitTests.swift; sourceTree = ""; }; 2AA0D2411C7C9A3B006E83D0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 2AB7AD101D53740E00A5F64A /* SSEKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SSEKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2AB7AD191D53744400A5F64A /* Info-macOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-macOS.plist"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -63,7 +61,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 12EC047E2731990700FA5116 /* NaimKit.framework in Frameworks */, + 011CBDF32762597A00712220 /* NaimKit.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -75,19 +73,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2AB7AD0C1D53740E00A5F64A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 12EC047C2731990700FA5116 /* Frameworks */ = { isa = PBXGroup; children = ( + 011CBDF22762597A00712220 /* NaimKit.xcframework */, 12EC047D2731990700FA5116 /* NaimKit.framework */, ); name = Frameworks; @@ -108,7 +100,6 @@ children = ( 2AA0D2301C7C9A3B006E83D0 /* SSEKit.framework */, 2AA0D23A1C7C9A3B006E83D0 /* SSEKitTests.xctest */, - 2AB7AD101D53740E00A5F64A /* SSEKit.framework */, ); name = Products; sourceTree = ""; @@ -146,14 +137,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2AB7AD0D1D53740E00A5F64A /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 2AB7AD181D53743600A5F64A /* SSEKit.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -194,24 +177,6 @@ productReference = 2AA0D23A1C7C9A3B006E83D0 /* SSEKitTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - 2AB7AD0F1D53740E00A5F64A /* SSEKit macOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 2AB7AD151D53740E00A5F64A /* Build configuration list for PBXNativeTarget "SSEKit macOS" */; - buildPhases = ( - 2AB7AD0B1D53740E00A5F64A /* Sources */, - 2AB7AD0C1D53740E00A5F64A /* Frameworks */, - 2AB7AD0D1D53740E00A5F64A /* Headers */, - 2AB7AD0E1D53740E00A5F64A /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "SSEKit macOS"; - productName = "SSEKit macOS"; - productReference = 2AB7AD101D53740E00A5F64A /* SSEKit.framework */; - productType = "com.apple.product-type.framework"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -231,9 +196,6 @@ CreatedOnToolsVersion = 7.3; LastSwiftMigration = 0800; }; - 2AB7AD0F1D53740E00A5F64A = { - CreatedOnToolsVersion = 7.3.1; - }; }; }; buildConfigurationList = 2AA0D22A1C7C9A3B006E83D0 /* Build configuration list for PBXProject "SSEKit" */; @@ -250,7 +212,6 @@ projectRoot = ""; targets = ( 2AA0D22F1C7C9A3B006E83D0 /* SSEKit iOS */, - 2AB7AD0F1D53740E00A5F64A /* SSEKit macOS */, 2AA0D2391C7C9A3B006E83D0 /* SSEKitTests */, ); }; @@ -271,13 +232,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2AB7AD0E1D53740E00A5F64A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -299,16 +253,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2AB7AD0B1D53740E00A5F64A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 2AB7AD761D53835E00A5F64A /* SSEManager.swift in Sources */, - 2AB7AD771D53835E00A5F64A /* EventSource.swift in Sources */, - 2AB7AD781D53835E00A5F64A /* EventSourceConfiguration.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -426,7 +370,8 @@ MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = SSEKit; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -446,7 +391,11 @@ INFOPLIST_FILE = "SSEKit/Info-iOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKit; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -465,7 +414,11 @@ INFOPLIST_FILE = "SSEKit/Info-iOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKit; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; @@ -477,7 +430,11 @@ buildSettings = { INFOPLIST_FILE = SSEKitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.2; @@ -489,57 +446,17 @@ buildSettings = { INFOPLIST_FILE = SSEKitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.naimaudio.SSEKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.2; }; name = Release; }; - 2AB7AD161D53740E00A5F64A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = ""; - COMBINE_HIDPI_IMAGES = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = "SSEKit/Info-macOS.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = "com.empiricalmagic.SSEKit-macOS"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; - }; - name = Debug; - }; - 2AB7AD171D53740E00A5F64A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = ""; - COMBINE_HIDPI_IMAGES = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = "SSEKit/Info-macOS.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = "com.empiricalmagic.SSEKit-macOS"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -570,15 +487,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 2AB7AD151D53740E00A5F64A /* Build configuration list for PBXNativeTarget "SSEKit macOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 2AB7AD161D53740E00A5F64A /* Debug */, - 2AB7AD171D53740E00A5F64A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = 2AA0D2271C7C9A3B006E83D0 /* Project object */; diff --git a/SSEKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SSEKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/SSEKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + From 6544c66fc67a916f2ba0a5c8232d859032aa3b5d Mon Sep 17 00:00:00 2001 From: Nick Hingston Date: Fri, 10 Dec 2021 17:40:36 +0000 Subject: [PATCH 22/22] APP-6121 don't embed NaimKit --- Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj index c6a110b..61687c7 100644 --- a/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj +++ b/Projects/SSEKit/SSEKit.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 011CBDF32762597A00712220 /* NaimKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 011CBDF22762597A00712220 /* NaimKit.xcframework */; }; - 011CBDF42762597A00712220 /* NaimKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 011CBDF22762597A00712220 /* NaimKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 2A18FBC41C7CB8A80052DFCE /* EventSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC31C7CB8A80052DFCE /* EventSource.swift */; }; 2A18FBC61C7CB8C40052DFCE /* EventSourceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC51C7CB8C40052DFCE /* EventSourceConfiguration.swift */; }; 2A18FBC81C7CB8EF0052DFCE /* SSEManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A18FBC71C7CB8EF0052DFCE /* SSEManager.swift */; }; @@ -27,20 +26,6 @@ }; /* End PBXContainerItemProxy section */ -/* Begin PBXCopyFilesBuildPhase section */ - 12EC04802731990700FA5116 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 011CBDF42762597A00712220 /* NaimKit.xcframework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 011CBDF22762597A00712220 /* NaimKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = NaimKit.xcframework; path = ../../../../Build/NaimKit.xcframework; sourceTree = ""; }; 12EC047D2731990700FA5116 /* NaimKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = NaimKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -148,7 +133,6 @@ 2AA0D22C1C7C9A3B006E83D0 /* Frameworks */, 2AA0D22D1C7C9A3B006E83D0 /* Headers */, 2AA0D22E1C7C9A3B006E83D0 /* Resources */, - 12EC04802731990700FA5116 /* Embed Frameworks */, ); buildRules = ( );