diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/iOS/OSCTest/OSCTest.xcodeproj/project.pbxproj b/Examples/iOS/OSCTest/OSCTest.xcodeproj/project.pbxproj index 9e0a000..a7ba1f9 100644 --- a/Examples/iOS/OSCTest/OSCTest.xcodeproj/project.pbxproj +++ b/Examples/iOS/OSCTest/OSCTest.xcodeproj/project.pbxproj @@ -316,7 +316,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = M4VH59A389; + DEVELOPMENT_TEAM = FNLF39495D; INFOPLIST_FILE = "$(SRCROOT)/OSCTest/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -334,7 +334,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = M4VH59A389; + DEVELOPMENT_TEAM = FNLF39495D; INFOPLIST_FILE = "$(SRCROOT)/OSCTest/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/Examples/macOS/OSCMonitor/OSCMonitor.xcodeproj/project.pbxproj b/Examples/macOS/OSCMonitor/OSCMonitor.xcodeproj/project.pbxproj index 51270d1..97ff1bb 100644 --- a/Examples/macOS/OSCMonitor/OSCMonitor.xcodeproj/project.pbxproj +++ b/Examples/macOS/OSCMonitor/OSCMonitor.xcodeproj/project.pbxproj @@ -108,6 +108,8 @@ dependencies = ( ); name = OSCMonitor; + packageProductDependencies = ( + ); productName = OSCMonitor; productReference = 033827B21D3999590065A3A7 /* OSCMonitor.app */; productType = "com.apple.product-type.application"; @@ -124,7 +126,7 @@ TargetAttributes = { 033827B11D3999590065A3A7 = { CreatedOnToolsVersion = 8.0; - DevelopmentTeam = 638F7EDPVY; + DevelopmentTeam = FNLF39495D; DevelopmentTeamName = "Devin Roth (Personal Team)"; LastSwiftMigration = 0910; ProvisioningStyle = Automatic; @@ -133,13 +135,15 @@ }; buildConfigurationList = 033827AD1D3999590065A3A7 /* Build configuration list for PBXProject "OSCMonitor" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 033827A91D3999590065A3A7; + packageReferences = ( + ); productRefGroup = 033827B31D3999590065A3A7 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -298,6 +302,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = FNLF39495D; INFOPLIST_FILE = OSCMonitor/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.14; @@ -314,6 +319,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = FNLF39495D; INFOPLIST_FILE = OSCMonitor/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.14; diff --git a/Examples/macOS/OSCMonitor/OSCMonitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/macOS/OSCMonitor/OSCMonitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Examples/macOS/OSCMonitor/OSCMonitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Framework/SwiftOSC/Addresses/OSCAddressPattern.swift b/Framework/SwiftOSC/Addresses/OSCAddressPattern.swift index d760983..2cf8a80 100644 --- a/Framework/SwiftOSC/Addresses/OSCAddressPattern.swift +++ b/Framework/SwiftOSC/Addresses/OSCAddressPattern.swift @@ -180,20 +180,24 @@ public struct OSCAddressPattern { // Returns True if the address matches the address pattern. public func matches(_ address: OSCAddress)->Bool{ - if address.string.range(of: self.regex, options: .regularExpression) == nil { - return false - } else { - return true + var maches = true + autoreleasepool { + if address.string.range(of: self.regex, options: .regularExpression) == nil { + maches = false + } } + return maches } // Returns True if the address is along the path of the address pattern public func matches(path: OSCAddress)->Bool{ - if path.string.range(of: self.regexPath, options: .regularExpression) == nil { - return false - } else { - return true + var maches = true + autoreleasepool { + if path.string.range(of: self.regexPath, options: .regularExpression) == nil { + maches = false + } } + return maches } } diff --git a/Framework/SwiftOSC/Network/OSCClient.swift b/Framework/SwiftOSC/Network/OSCClient.swift index 6287434..33cca78 100644 --- a/Framework/SwiftOSC/Network/OSCClient.swift +++ b/Framework/SwiftOSC/Network/OSCClient.swift @@ -10,39 +10,39 @@ import Foundation import Network public class OSCClient { - - var connection: NWConnection? + + public var connection: NWConnection? // Access Control: changed 'var connection: NWConnection?' to public. var queue: DispatchQueue - - var host: NWEndpoint.Host - var port: NWEndpoint.Port - + + public private(set) var host: NWEndpoint.Host + public private(set) var port: NWEndpoint.Port + public init?(host: String, port: Int) { - + // check if string is empty if host == "" { - + NSLog("Invalid Hostname: No empty strings allowed.") return nil - + } - if port > 65535 && port >= 0{ + if port > 65535 || port <= 0 { NSLog("Invalid Port: Out of range.") return nil } - + self.host = NWEndpoint.Host(host) self.port = NWEndpoint.Port(integerLiteral: UInt16(port)) - + queue = DispatchQueue(label: "SwiftOSC Client") setupConnection() } - + func setupConnection(){ - + // create the connection connection = NWConnection(host: host, port: port, using: .udp) - + // setup state update handler connection?.stateUpdateHandler = { [weak self] (newState) in switch newState { @@ -53,22 +53,27 @@ public class OSCClient { NSLog("SWiftOSC Client is restarting.") self?.setupConnection() case .cancelled: + NSLog("SWiftOSC Client cancelled.") break case .waiting(let error): NSLog("SwiftOSC Client waiting with error \(error)") case .preparing: + NSLog("SWiftOSC Client is preparing.") break case .setup: + NSLog("SWiftOSC Client is setting up.") break + @unknown default: + fatalError() } } - + // start the connection connection?.start(queue: queue) } - + public func send(_ element: OSCElement){ - + let data = element.oscData connection?.send(content: data, completion: .contentProcessed({ (error) in if let error = error { @@ -80,7 +85,7 @@ public class OSCClient { // destroy connection and listener connection?.forceCancel() - + // setup new listener setupConnection() } diff --git a/Framework/SwiftOSC/Network/OSCDelegate.swift b/Framework/SwiftOSC/Network/OSCDelegate.swift index bcb9bf2..75d7db5 100644 --- a/Framework/SwiftOSC/Network/OSCDelegate.swift +++ b/Framework/SwiftOSC/Network/OSCDelegate.swift @@ -8,7 +8,7 @@ import Foundation -public protocol OSCDelegate { +public protocol OSCDelegate: AnyObject { func didReceive(_ data: Data) func didReceive(_ bundle: OSCBundle) func didReceive(_ message: OSCMessage) diff --git a/Framework/SwiftOSC/Network/OSCServer.swift b/Framework/SwiftOSC/Network/OSCServer.swift index 1d1de0d..9c59c9a 100644 --- a/Framework/SwiftOSC/Network/OSCServer.swift +++ b/Framework/SwiftOSC/Network/OSCServer.swift @@ -11,56 +11,80 @@ import Network public class OSCServer { - public var delegate: OSCDelegate? + public weak var delegate: OSCDelegate? - var listener: NWListener? - var port: NWEndpoint.Port + public var listener: NWListener? + public private(set) var port: NWEndpoint.Port + public private(set) var name: String? + public private(set) var domain: String? var queue: DispatchQueue - var connection: NWConnection? + public var connection: NWConnection? - public init?(port: Int) { + var bonjour: Bool = false + + public init?(port: Int, bonjourName: String? = nil, domain: String? = nil) { + + self.domain = domain // check port range - if port > 65535 && port >= 0{ + if port > 65535 || port <= 0 { NSLog("Invalid Port: Out of range.") return nil } + if let bonjourName = bonjourName { + bonjour = true + self.name = bonjourName + } self.port = NWEndpoint.Port(integerLiteral: UInt16(port)) - queue = DispatchQueue(label: "SwiftOSC Server") + queue = DispatchQueue(label: "SwiftOSC Server") // or .main setupListener() } func setupListener() { + + // advertise Bonjour + let udpOption = NWProtocolUDP.Options() + let params = NWParameters(dtls: nil, udp: udpOption) + //if bonjour { params.includePeerToPeer = true } + // create the listener - listener = try! NWListener(using: .udp, on: port) + listener = try! NWListener(using: params, on: port) + + // Bonjour service + if bonjour { listener?.service = NWListener.Service(name: name, + type: "_osc._udp", + domain: domain) + } // handle incoming connections server will only respond to the latest connection listener?.newConnectionHandler = { [weak self] (newConnection) in + guard let self = self else { print("SwiftOSC Server newConnectionHandler error"); return } + NSLog("\(self.name ?? "SwiftOSC server"): New Connection from \(String(describing: newConnection))") - NSLog("New Connection from \(String(describing: newConnection))") - - // cancel previous connection - if self?.connection != nil { - NSLog("Cancelling connection: \(String(describing: newConnection))") - self?.connection?.cancel() + // cancel previous connection // check if it's own port + if self.connection != nil { + NSLog("\(self.name ?? "SwiftOSC server"): Cancelling connection: \(String(describing: newConnection))") + self.connection?.cancel() } - self?.connection = newConnection - self?.connection?.start(queue: (self?.queue)!) - self?.receive() + self.connection = newConnection + self.connection?.start(queue: (self.queue)) + self.receive() } - + // Handle listener state changes listener?.stateUpdateHandler = { [weak self] (newState) in + guard let self = self else { print("SwiftOSC Server stateUpdateHandler error"); return } switch newState { case .ready: - NSLog("Listening on port \(String(describing: self?.listener?.port))") + NSLog("\(self.name ?? "SwiftOSC server"): Listening on port \(String(describing: self.listener?.port)), delegate: \(String(describing: self.delegate))") case .failed(let error): - NSLog("Listener failed with error \(error)") + NSLog("\(self.name ?? "SwiftOSC server"): Listener failed with error \(error)") + self.restart() case .cancelled: - NSLog("Listener cancelled") + NSLog("\(self.name ?? "SwiftOSC server"): Listener cancelled") default: break } @@ -74,7 +98,7 @@ public class OSCServer { func receive() { connection?.receiveMessage { [weak self] (content, context, isCompleted, error) in if let data = content { - data.printHexString() + // data.printHexString() self?.decodePacket(data) } @@ -86,13 +110,14 @@ public class OSCServer { func decodePacket(_ data: Data){ - DispatchQueue.main.async { - self.delegate?.didReceive(data) + DispatchQueue.main.async { [weak self] in + guard let strongSelf = self else { return } + strongSelf.delegate?.didReceive(data) } if data[0] == 0x2f { // check if first character is "/" if let message = decodeMessage(data){ - print(message) + // print(message) self.sendToDelegate(message) } @@ -118,23 +143,25 @@ public class OSCServer { let length = Int(bundleData.subdata(in: Range(0...3)).toInt32()) let nextData = bundleData.subdata(in: 4.. OSCMessage { + let customMessage: OSCMessage = OSCMessage(OSCAddressPattern("/OSCError")!) + customMessage.add(String("SwiftOSCServer \(name ?? String(describing: self.listener?.port)) OSC exception_ *** dirty fix 'type end found nil' *** (TotalMix bundle data with missing type end)")) + return customMessage + } + let customMessage = buildCustomMessage() + NSLog("SwiftOSCServer exception_ *** dirty fix 'type end found nil' ***") + return customMessage } let type = messageData.subdata(in: 1.. 2.0' +```` + +### [Swift Package Manager](https://swift.org/package-manager/) + +```swift +dependencies: [ + .package(url: "https://github.com/soundflix/SwiftOSC.git", from: "2.0") +] ``` -OR +Alternatively, you can add the package [directly via Xcode](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app). + +OR install locally: ### Step 1 @@ -22,7 +34,7 @@ Clone or download repository from Github. ### Step 2 -Open SwiftOSC.xcworkspace and build SwiftOSC frameworks. +Open SwiftOSC.xcworkspace and build SwiftOSC frameworks. ### Step 3 @@ -47,7 +59,7 @@ var server = OSCServer(port: 8080) Setup server delegate to handle incoming OSC Data ```swift class OSCHandler: OSCServerDelegate { - + func didReceive(_ message: OSCMessage){ if let integer = message.arguments[0] as? Int { print("Received int \(integer)") @@ -93,7 +105,7 @@ client.send(message) ``` ## Known Issues - OSCClient loses connection following returning from being in the background. Call client.restart() in this situation.\ - + ## About