├── Example_Projects ├── SwiftCoAPClientExample │ ├── SwiftCoAPClientExample │ │ ├── SwiftCoAPClientExample-Bridging-Header.h │ │ ├── Images.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.xib │ │ │ └── Main.storyboard │ │ ├── ExampleViewController.swift │ │ └── SCClient.swift │ ├── SwiftCoAPClientExample.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── xcshareddata │ │ │ └── xcschemes │ │ │ │ └── SwiftCoAPClientExample.xcscheme │ │ └── project.pbxproj │ └── SwiftCoAPClientExampleTests │ │ ├── Info.plist │ │ └── SwiftCoAPClientExampleTests.swift └── SwiftCoAPServerExample │ ├── SwiftCoAPServerExample │ ├── SwiftCoAPServerExample-Bridging-Header.h │ ├── DefaultTableViewCell.swift │ ├── Info.plist │ ├── TimeResourceModel.swift │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── TestResourceModel.swift │ ├── SeparateResourceModel.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ └── ExampleViewController.swift │ ├── SwiftCoAPServerExample.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── SwiftCoAPServerExample.xcscheme │ └── project.pbxproj │ └── SwiftCoAPServerExampleTests │ ├── Info.plist │ └── SwiftCoAPServerExampleTests.swift ├── SwiftCoAP.xcworkspace ├── xcshareddata │ └── IDEWorkspaceChecks.plist └── contents.xcworkspacedata ├── .gitignore ├── LICENSE ├── README.md └── SwiftCoAP_Library ├── SCClient.swift └── SCServer.swift /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample/SwiftCoAPClientExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "GCDAsyncUdpSocket.h" -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample/SwiftCoAPServerExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "GCDAsyncUdpSocket.h" -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftCoAP.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftCoAP.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 16 | 18 | 19 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Xcode 4 | # 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | *.moved-aside 17 | DerivedData 18 | *.hmap 19 | *.ipa 20 | *.xcuserstate 21 | 22 | # CocoaPods 23 | # 24 | # We recommend against adding the Pods directory to your .gitignore. However 25 | # you should judge for yourself, the pros and cons are mentioned at: 26 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 27 | # 28 | # Pods/ 29 | 30 | # Carthage 31 | # 32 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 33 | # Carthage/Checkouts 34 | 35 | Carthage/Build 36 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample/DefaultTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultTableViewCell.swift 3 | // SwiftCoAPServerExample 4 | // 5 | // Created by Wojtek Kordylewski on 13.06.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DefaultTableViewCell: UITableViewCell { 12 | 13 | @IBOutlet weak var nameLabel: UILabel! 14 | @IBOutlet weak var detailLabel: UILabel! 15 | 16 | 17 | override func awakeFromNib() { 18 | super.awakeFromNib() 19 | // Initialization code 20 | } 21 | 22 | override func setSelected(_ selected: Bool, animated: Bool) { 23 | super.setSelected(selected, animated: animated) 24 | 25 | // Configure the view for the selected state 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Wojtek Kordylewski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExampleTests/SwiftCoAPClientExampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftCoAPClientExampleTests.swift 3 | // SwiftCoAPClientExampleTests 4 | // 5 | // Created by Wojtek Kordylewski on 03.06.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class SwiftCoAPClientExampleTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExampleTests/SwiftCoAPServerExampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftCoAPServerExampleTests.swift 3 | // SwiftCoAPServerExampleTests 4 | // 5 | // Created by Wojtek Kordylewski on 12.06.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class SwiftCoAPServerExampleTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample/TimeResourceModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimeResourceModel.swift 3 | // SwiftCoAPServerExample 4 | // 5 | // Created by Wojtek Kordylewski on 23.06.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class TimeResourceModel: SCResourceModel { 12 | //Individual Properties 13 | var myText: String { 14 | didSet { 15 | self.dataRepresentation = myText.data(using: String.Encoding.utf8) //update observable Data anytime myText is changed 16 | } 17 | } 18 | weak var server: SCServer! 19 | var observeTimer: Timer! 20 | // 21 | 22 | init(name: String, allowedRoutes: UInt, text: String, server: SCServer!) { 23 | self.myText = text 24 | self.server = server 25 | super.init(name: name, allowedRoutes: allowedRoutes) 26 | //Starting Updates for Observe 27 | self.observeTimer = Timer(timeInterval: 5.0, target: self, selector: #selector(TimeResourceModel.updateObservableData), userInfo: nil, repeats: true) 28 | RunLoop.current.add(self.observeTimer, forMode: RunLoop.Mode.common) 29 | self.observable = true 30 | self.dataRepresentation = myText.data(using: String.Encoding.utf8) 31 | } 32 | 33 | @objc func updateObservableData() { 34 | myText = "Observe Time: \(Date())" 35 | server.updateRegisteredObserversForResource(self) 36 | } 37 | 38 | override func dataForGet(queryDictionary: [String : String], options: [Int : [Data]]) -> (statusCode: SCCodeValue, payloadData: Data?, contentFormat: SCContentFormat?)? { 39 | return (SCCodeValue(classValue: 2, detailValue: 05)!, myText.data(using: String.Encoding.utf8), .plain) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftCoAPClientExample 4 | // 5 | // Created by Wojtek Kordylewski on 03.06.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // 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. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // 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. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftCoAPServerExample 4 | // 5 | // Created by Wojtek Kordylewski on 12.06.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // 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. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // 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. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample/TestResourceModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestResourceModel.swift 3 | // SwiftCoAPServerExample 4 | // 5 | // Created by Wojtek Kordylewski on 25.06.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class TestResourceModel: SCResourceModel { 12 | //Individual Properties 13 | var myText: String { 14 | didSet { 15 | self.dataRepresentation = myText.data(using: String.Encoding.utf8) //update observable Data anytime myText is changed 16 | } 17 | } 18 | // 19 | 20 | init(name: String, allowedRoutes: UInt, text: String) { 21 | self.myText = text 22 | super.init(name: name, allowedRoutes: allowedRoutes) 23 | self.dataRepresentation = myText.data(using: String.Encoding.utf8) 24 | } 25 | 26 | override func dataForGet(queryDictionary: [String : String], options: [Int : [Data]]) -> (statusCode: SCCodeValue, payloadData: Data?, contentFormat: SCContentFormat?)? { 27 | return (SCCodeValue(classValue: 2, detailValue: 05)!, myText.data(using: String.Encoding.utf8), .plain) 28 | } 29 | 30 | override func dataForPost(queryDictionary: [String : String], options: [Int : [Data]], requestData: Data?) -> (statusCode: SCCodeValue, payloadData: Data?, contentFormat: SCContentFormat?, locationUri: String?)? { 31 | if let data = requestData, let string = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as String?{ 32 | myText = string 33 | return (SCCodeSample.created.codeValue(), "Data created successfully".data(using: String.Encoding.utf8), .plain, self.name) 34 | } 35 | return (SCCodeSample.forbidden.codeValue(), "Invalid Data sent".data(using: String.Encoding.utf8), .plain, nil) 36 | } 37 | 38 | override func dataForPut(queryDictionary: [String : String], options: [Int : [Data]], requestData: Data?) -> (statusCode: SCCodeValue, payloadData: Data?, contentFormat: SCContentFormat?, locationUri: String?)? { 39 | if let data = requestData, let string = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as String?{ 40 | myText += string 41 | return (SCCodeSample.changed.codeValue(), "Update Successful".data(using: String.Encoding.utf8), .plain, self.name) 42 | } 43 | return (SCCodeSample.forbidden.codeValue(), "Invalid Data sent".data(using: String.Encoding.utf8), .plain, nil) 44 | } 45 | 46 | override func dataForDelete(queryDictionary: [String : String], options: [Int : [Data]]) -> (statusCode: SCCodeValue, payloadData: Data?, contentFormat: SCContentFormat?)? { 47 | myText = "" 48 | return (SCCodeSample.deleted.codeValue(), "Deleted".data(using: String.Encoding.utf8), .plain) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample/SeparateResourceModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SeparateResourceModel.swift 3 | // SwiftCoAPServerExample 4 | // 5 | // Created by Wojtek Kordylewski on 23.06.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SeparateResourceModel: SCResourceModel { 12 | //Individual Properties 13 | var myText: String { 14 | didSet { 15 | self.dataRepresentation = myText.data(using: String.Encoding.utf8) //update observable Data anytime myText is changed 16 | } 17 | } 18 | weak var server: SCServer! 19 | // 20 | 21 | init(name: String, allowedRoutes: UInt, text: String, server: SCServer!) { 22 | self.myText = text 23 | self.server = server 24 | super.init(name: name, allowedRoutes: allowedRoutes) 25 | self.dataRepresentation = myText.data(using: String.Encoding.utf8) 26 | } 27 | 28 | func delay(_ delay:Double, closure:@escaping ()->()) { 29 | DispatchQueue.main.asyncAfter( 30 | deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), 31 | execute: closure 32 | ) 33 | } 34 | 35 | override func willHandleDataAsynchronouslyForRoute(_ route: SCAllowedRoute, queryDictionary: [String : String], options: [Int : [Data]], originalMessage: SCMessage) -> Bool { 36 | switch route { 37 | case .get: 38 | delay(6.0) { 39 | self.server.didCompleteAsynchronousRequestForOriginalMessage(originalMessage, resource: self, values: (SCCodeValue(classValue: 2, detailValue: 05)!, self.myText.data(using: String.Encoding.utf8), .plain, nil)) 40 | } 41 | case .post: 42 | delay(6.0) { 43 | if let data = originalMessage.payload, let string = NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue) as String? { 44 | self.myText = string 45 | self.server.didCompleteAsynchronousRequestForOriginalMessage(originalMessage, resource: self, values: (SCCodeSample.created.codeValue(), "Data created successfully".data(using: String.Encoding.utf8), .plain, self.name)) 46 | 47 | } 48 | else { 49 | self.server.didCompleteAsynchronousRequestForOriginalMessage(originalMessage, resource: self, values: (SCCodeSample.forbidden.codeValue(), "Invalid Data sent".data(using: String.Encoding.utf8), .plain, nil)) 50 | } 51 | } 52 | case .put, .delete: 53 | return false 54 | } 55 | return true 56 | } 57 | 58 | override func dataForDelete(queryDictionary: [String : String], options: [Int : [Data]]) -> (statusCode: SCCodeValue, payloadData: Data?, contentFormat: SCContentFormat?)? { 59 | myText = "<>" 60 | return (SCCodeSample.deleted.codeValue(), "Deleted".data(using: String.Encoding.utf8), .plain) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample.xcodeproj/xcshareddata/xcschemes/SwiftCoAPServerExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample/ExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleViewController.swift 3 | // SwiftCoAPServerExample 4 | // 5 | // Created by Wojtek Kordylewski on 12.06.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ExampleViewController: UIViewController { 12 | 13 | var myServer: SCServer! 14 | let kDefaultCellIdentifier = "DefaultCell" 15 | 16 | @IBOutlet weak var tableView: UITableView! 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | if let server = SCServer(delegate: self) { 21 | server.resources.append(TestResourceModel(name: "test", allowedRoutes: SCAllowedRoute.get.rawValue | SCAllowedRoute.post.rawValue | SCAllowedRoute.put.rawValue | SCAllowedRoute.delete.rawValue, text: "This is a very long description text, I hope that all of you will like it. It should be transmitted via the block2 option by default")) 22 | server.resources.append(TimeResourceModel(name: "time", allowedRoutes: SCAllowedRoute.get.rawValue, text: "Current Date Time: \(Date())", server: server)) 23 | server.resources.append(SeparateResourceModel(name: "separate", allowedRoutes: SCAllowedRoute.get.rawValue | SCAllowedRoute.post.rawValue | SCAllowedRoute.delete.rawValue, text: "Delayed answer...", server: server)) 24 | 25 | server.autoBlock2SZX = 1 26 | myServer = server 27 | } 28 | 29 | 30 | tableView.contentInset = UIEdgeInsets.init(top: 64.0, left: 0, bottom: 0, right: 0) 31 | tableView.estimatedRowHeight = 44.0 32 | tableView.rowHeight = UITableView.automaticDimension 33 | } 34 | 35 | override func viewWillDisappear(_ animated: Bool) { 36 | super.viewWillDisappear(animated) 37 | myServer?.close() 38 | } 39 | } 40 | 41 | extension ExampleViewController: UITableViewDataSource { 42 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 43 | return myServer?.resources.count ?? 0 44 | } 45 | 46 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 47 | let cell = tableView.dequeueReusableCell(withIdentifier: kDefaultCellIdentifier) as! DefaultTableViewCell 48 | let textResource = myServer!.resources[indexPath.row] 49 | cell.nameLabel.text = textResource.name 50 | cell.detailLabel.text = NSString(data: textResource.dataRepresentation as Data, encoding: String.Encoding.utf8.rawValue) as String? 51 | return cell 52 | } 53 | } 54 | 55 | extension ExampleViewController: SCServerDelegate { 56 | func swiftCoapServer(_ server: SCServer, didFailWithError error: NSError) { 57 | print("Failed with Error \(error.localizedDescription)") 58 | } 59 | 60 | func swiftCoapServer(_ server: SCServer, didHandleRequestWithCode requestCode: SCCodeValue, forResource resource: SCResourceModel, withResponseCode responseCode: SCCodeValue) { 61 | tableView.reloadData() 62 | print("Did Handle Request with request code: \(requestCode.toString()) for resource \(resource.name) with response code: \(responseCode.toString())") 63 | } 64 | 65 | func swiftCoapServer(_ server: SCServer, didRejectRequestWithCode requestCode: SCCodeValue, forPath path: String, withResponseCode responseCode: SCCodeValue) { 66 | print("Did Reject Request with request code: \(requestCode.toString()) for resource path \(path) with response code: \(responseCode.toString())") 67 | } 68 | 69 | func swiftCoapServer(_ server: SCServer, didSendSeparateResponseMessage: SCMessage, number: Int) { 70 | print("Server sent separate Response message)") 71 | } 72 | 73 | func swiftCoapServer(_ server: SCServer, willUpdatedObserversForResource resource: SCResourceModel) { 74 | tableView.reloadData() 75 | // print("Attempting to Update Observers for resource \(resource.name)") 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample/ExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleViewController.swift 3 | // SwiftCoAP 4 | // 5 | // Created by Wojtek Kordylewski on 04.05.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ExampleViewController: UIViewController { 12 | @IBOutlet weak var textView: UITextView! 13 | @IBOutlet weak var hostTextField: UITextField! 14 | @IBOutlet weak var uriPathTextField: UITextField! 15 | @IBOutlet weak var portTextField: UITextField! 16 | 17 | let separatorLine = "\n-----------------\n" 18 | 19 | var coapClient: SCClient! 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | let client = SCClient(delegate: self) 24 | client.sendToken = true 25 | client.autoBlock1SZX = 2 26 | 27 | //Advanced Settings 28 | 29 | //client.cachingActive = true 30 | //client.httpProxyingData = ("localhost", 5683) 31 | 32 | self.coapClient = client 33 | 34 | //Default values, change if you want 35 | hostTextField.text = "coap.me" 36 | portTextField.text = "5683" 37 | } 38 | 39 | // MARK: Actions 40 | 41 | @IBAction func onClickDelete(_ sender: AnyObject) { 42 | textView.text = "" 43 | } 44 | 45 | @IBAction func onClickSendMessage(_ sender: AnyObject) { 46 | if sender is UIButton { 47 | view.endEditing(true) 48 | } 49 | let m = SCMessage(code: SCCodeValue(classValue: 0, detailValue: 01)!, type: .confirmable, payload: "test".data(using: String.Encoding.utf8)) 50 | 51 | if let stringData = uriPathTextField.text?.data(using: String.Encoding.utf8) { 52 | m.addOption(SCOption.uriPath.rawValue, data: stringData) 53 | } 54 | 55 | if let portString = portTextField.text, let hostString = hostTextField.text, let port = UInt16(portString) { 56 | coapClient.sendCoAPMessage(m, hostName:hostString, port: port) 57 | } 58 | else { 59 | textView.text = "\(String(describing: textView.text))\nInvalid PORT" 60 | } 61 | } 62 | } 63 | 64 | extension ExampleViewController: UITextFieldDelegate { 65 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 66 | switch textField { 67 | case hostTextField: 68 | portTextField.becomeFirstResponder() 69 | case portTextField: 70 | uriPathTextField.becomeFirstResponder() 71 | default: 72 | uriPathTextField.resignFirstResponder() 73 | onClickSendMessage(textField) 74 | } 75 | return true 76 | } 77 | } 78 | 79 | extension ExampleViewController: SCClientDelegate { 80 | func swiftCoapClient(_ client: SCClient, didReceiveMessage message: SCMessage) { 81 | var payloadstring = "" 82 | if let pay = message.payload { 83 | if let string = NSString(data: pay as Data, encoding:String.Encoding.utf8.rawValue) { 84 | payloadstring = String(string) 85 | } 86 | } 87 | let firstPartString = "Message received from \(message.hostName ?? "") with type: \(message.type.shortString())\nwith code: \(message.code.toString()) \nwith id: \(message.messageId ?? 0)\nPayload: \(payloadstring)\n" 88 | var optString = "Options:\n" 89 | for (key, _) in message.options { 90 | var optName = "Unknown" 91 | 92 | if let knownOpt = SCOption(rawValue: key) { 93 | optName = knownOpt.toString() 94 | } 95 | 96 | optString += "\(optName) (\(key))\n" 97 | } 98 | textView.text = separatorLine + firstPartString + optString + separatorLine + textView.text 99 | } 100 | 101 | func swiftCoapClient(_ client: SCClient, didFailWithError error: NSError) { 102 | textView.text = "Failed with Error \(error.localizedDescription)" + separatorLine + separatorLine + textView.text 103 | } 104 | 105 | func swiftCoapClient(_ client: SCClient, didSendMessage message: SCMessage, number: Int) { 106 | textView.text = "Message sent (\(number)) with type: \(message.type.shortString()) with id: \(String(describing: message.messageId))\n" + separatorLine + separatorLine + textView.text 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample.xcodeproj/xcshareddata/xcschemes/SwiftCoAPClientExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 40 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SwiftCoAP 2 | ===== 3 | **Updated for Swift 4.2** 4 | 5 | **NEW:** Download the Client-Implementation **myCoAP** for iOS/watchOS which builds upon this library: [AppStore-Link](https://itunes.apple.com/de/app/mycoap/id1048383045?mt=8) 6 | 7 | This project is an implementation of the "Constrained Application Protocol" (CoAP - RFC 7252) in Swift. It is intended for Clients and Servers. 8 | This implementation provides the standard CoAP features (including Caching) along with the extensions: 9 | 10 | * Observe 11 | * Block transfer (Block1 and Block2) 12 | 13 | A short manual is provided below. 14 | Feedback is highly appreciated! 15 | 16 | 17 | Want an Objective-C implementation? Checkout [iCoAP](https://github.com/stuffrabbit/iCoAP). 18 | 19 | Getting Started 20 | ===== 21 | 22 | ###The Files: 23 | * Copy all files included in the `SwiftCoAP_Library` folder to your Xcode project 24 | * Make sure to add `GCDAsyncUdpSocket.h` to your Objective-C Bridging-File, as this project uses the Objective-C-Library CocoaAsyncSocket for UDP communication 25 | 26 | ###The Code 27 | 28 | This section gives you an impression on how to use the provided data structures. 29 | 30 | #### SCMessage 31 | 32 | `SCMessage` represents a CoAP message in SwiftCoAP. You can initialize a message with help of the designated initializer as follows: `SCMessage()`. Alternatively, `SCMessage` provides a convenience initializer (`convenience init(code: SCCodeValue, type: SCType, payload: NSData?)`) that lets you create an instance the following way: 33 | 34 | ```swift 35 | SCMessage(code: SCCodeValue(classValue: 0, detailValue: 01)!, type: .Confirmable, payload: "test".dataUsingEncoding(NSUTF8StringEncoding)) 36 | ``` 37 | * The CoAP type is represented as `SCType` of type enum (refer to source code) 38 | * The CoAP code is represented as a struct named `SCCodeValue`. The struct lets you apply the CoAP code syntax c.dd (e.g. `SCCodeValue(classValue: 0, detailValue: 01)` equals `0.01` (note that this is a failable initializer, which fails when invalid class values (greater 7) or detail values (greater 31) are passed as arguments)). 39 | * The CoAP options are represented as Dictionary. The option number represents the key (as Int) and the respective value pair represents an Array with NSData objects (in case that the same option is present multiple times). To add an option safely, it is recommended to use the provided `addOption(option: Int, data: NSData)` method. 40 | 41 | * Checkout the source code and its comments for more information 42 | 43 | #### SCClient 44 | 45 | This class represents a CoAP client, which can be initialized with the given designated initializer: `init(delegate: SCClientDelegate?)`. 46 | 47 | ##### Properties 48 | 49 | You can modify the following properties of an `SCClient` object to alter its behavior: 50 | 51 | * `sendToken: Bool` (default `true`) If true, a randomized token with at least 4 bytes length is generated upon transmission 52 | * `autoBlock1SZX: UInt?` (default `2`) If not nil, Block1 transfer will be used automatically when the payload size exceeds the value 2^(autoBlock1SZX +4). Valid Values: 0-6 53 | * `httpProxyingData: (hostName: String, port: UInt16)?` (default `nil`) If not nil, all message will be sent via http to the given proxy address 54 | * `cachingActive: Bool` (default `false`) If true, caching is activiated 55 | 56 | Send a message by calling the method `sendCoAPMessage(message: SCMessage, hostName: String, port: UInt16)` and implement the provided `SCClientDelegate` protocol to receive callbacks. This should be it. 57 | 58 | ##### Example 59 | 60 | ```swift 61 | let m = SCMessage(code: SCCodeValue(classValue: 0, detailValue: 01), type: .Confirmable, payload: "test".dataUsingEncoding(NSUTF8StringEncoding)) 62 | m.addOption(SCOption.UriPath.rawValue, data: "test".dataUsingEncoding(NSUTF8StringEncoding)!) 63 | let coapClient = SCClient(delegate: self) 64 | coapClient.sendCoAPMessage(m, hostName: "coap.me", port: 5683) 65 | ``` 66 | ##### Other Methods 67 | 68 | * `cancelObserve()` Cancels observe directly, sending the previous message with an Observe-Option Value of 1. Only effective, if the previous message initiated a registration as observer with the respective server. 69 | * `closeTransmission()` Closes the transmission. It is recommended to call this method anytime you do not expect to receive a response any longer. 70 | 71 | ##### HTTP-Proxying 72 | 73 | The class `SCClient` gives you the opportunity to send a message as HTTP via a proxy. Just add the following line after initiating an `SCClient`object: 74 | ```swift 75 | coapClient.httpProxyingData = ("localhost", 5683) 76 | ``` 77 | The Options of the CoAP-Message are sent in the HTTP-Header. It is required that the Proxy returns the CoAP-Type in the Header of HTTP-Response as well. The respective Header-Field is `COAP_TYPE`. 78 | The Request-URI has the following Format: `http://proxyHost:proxyPort/coapHost:coapPort` 79 | An Example: Sending your message to the CoAP-Server `coap.me` with the Port `5683` via a HTTP-Proxy located at `localhost:9292`, lets the SwiftCoAP library compose the follwoing Request-URI: `http://localhost:9292/coap.me:5683` 80 | 81 | ##### Custom Transport Layer Functionality 82 | 83 | `SCClient` encapsulates the CoAP transport layer functionality into a separate object which implements the `SCCoAPTransportLayerProtocol` protocol. `SCClient` uses the provided `SCCoAPUDPTransportLayer` class by default, which uses UDP. However, if you want to replace it with your own class just do the following steps: 84 | 85 | * Create a custom class and adopt the SCCoAPTransportLayerProtocol 86 | * Pass an object of your class to the init method of SCClient: `init(delegate: SCClientDelegate?, transportLayerObject: SCCoAPTransportLayerProtocol)` 87 | * `SCClient` will set itself as a delegate of your class and notify you through the methods of `SCCoAPTransportLayerProtocol` when e.g. a data needs to be sent. 88 | * Whenever you receive a response to data you have sent, call methods of the protocol `SCCoAPTransportLayerDelegate` on your property `transportLayerDelegate` which will hold a weak reference to the resepective object of type `SCClient`(reference is automatically set through SCClient). 89 | * Checkout the source code and the implementation of the class `SCCoAPUDPTransportLayer`, to see the functionality in action. 90 | 91 | An (real) example where using a custom transport layer functionality would be helpful: 92 | You cannot use UDP, e.g. when you bring this library to WatchOS 2. As UDP communcation is not available on this OS you can use WatchConnectiviy as transport layer object for your `SCClient` and let the iPhone execute the UDP sendings. 93 | #### SCServer 94 | 95 | This class represents a CoAP server, which can be initialized with the standard designated initializer `init()`. The given convenience initializer `init?(port: UInt16)` initializes a server instance and automatically starts listening on the given port. This initialization can fail if a UDP-socket error occurs. 96 | 97 | ##### Properties 98 | 99 | You can modify the following properties of an `SCServer` object to alter its behavior: 100 | 101 | * `autoBlock2SZX: UInt?` (default `nil`) If not nil, Block2 transfer will be used automatically in responses when the payload size exceeds the value 2^(autoBlock1SZX +4). Valid Values: 0-6 102 | * `resources: [SCResourceModel]` Array of `SCResourceModel` objects which represent a resource of the server (see below) 103 | * `autoWellKnownCore: Bool` (default `true`) If set to `true`, the server will automatically provide responses for the resource `well-known/core` with its current resources. 104 | ##### Methods 105 | 106 | * `start(port: UInt16 = 5683) -> Bool` Starts the server manually on the given port 107 | * `close()` Closes Udp socket listening 108 | * `reset()` Resets the context of the server (including added resources, cached message contexts, registered observers for resources and data uploads for Block1) 109 | * `didCompleteAsynchronousRequestForOriginalMessage(message: SCMessage, resource: SCResourceModel, values:(statusCode: SCCodeValue, payloadData: NSData?, contentFormat: SCContentFormat!, locationUri: String!))` Call this method when your resource is ready to process a separate response. The concerned resource must return true for the method `willHandleDataAsynchronouslyForGet(...)`. It is necessary to pass the original message and the resource (both received in `willHandleDataAsynchronouslyForGet`) so that the server is able to retrieve the current context. Additionay, you have to pass the typical `values` tuple which form the response (as described in `SCResourceModel`) 110 | * `updateRegisteredObserversForResource(resource: SCResourceModel)` Call this method when the given resource has updated its data representation in order to notify all registered observers (and has `observable` set to `true`). 111 | 112 | #### Resource representation `SCResourceModel` 113 | 114 | SwiftCoAP provides the base class `SCResourceModel` to represent a CoAP resource in the server implementation. To create your own resources with custom behavior, you just have to subclass `SCResourceModel`. You must use the designated initializer `init(name: String, allowedRoutes: UInt)` which requires you to set the name and the routes (GET, POST, PUT, DELETE) which you want to support (see explanation below). 115 | 116 | ##### Resource properties 117 | `SCResourceModel` has the following properties which can be modified/set on initialization: 118 | * `name: String` The name of the resource 119 | * `allowedRoutes: UInt` Bitmask of allowed routes (see `SCAllowedRoutes` enum) (you can pass for example `SCAllowedRoute.Get.rawValue | SCAllowedRoute.Post.rawValue` to support GET and POST) 120 | * `maxAgeValue: UInt!` (default `nil`) If not nil, every response will contain the provided MaxAge value 121 | * `etag: NSData!` (default `nil` and read-only) If not nil, every response will contain the provided eTag. The etag is generated automatically whenever you update the value `dataRepresentation` of the resource (is represented as hashvalue of your data representation). 122 | * `dataRepresentation: NSData!` The current data representation of the resource. Needs to stay up to date 123 | * `observable: Bool` (default false) If true, a response will contain the Observe option, and endpoints will be able to register as observers in `SCServer`. Call `updateRegisteredObserversForResource(self)`, anytime the value of your `dataRepresentation` changes. 124 | 125 | ##### Resource methods 126 | The following methods are used for data reception of your allowed routes. SCServer will call the appropriate message upon the reception of a reqeuest. Override the respective methods, which match your allowedRoutes. 127 | 128 | SCServer passes a `queryDictionary` containing the URI query content (e.g `["user_id": "23"]`) and all options contained in the respective request. The POST and PUT methods provide the message's payload as well. 129 | Please, refer to the example resources in the `SwiftCoAPServerExample` project for implementation examples. 130 | 131 | * `willHandleDataAsynchronouslyForRoute(route: SCAllowedRoute, queryDictionary: [String : String], options: [Int : [NSData]], originalMessage: SCMessage) -> Bool` This method lets you decide whether the current request shall be processed asynchronously, i.e. if `true` will be returned, an empty ACK will be sent, and you can provide the actual content in a separate response by calling the servers `didCompleteAsynchronousRequestForOriginalMessage(...)`. Note: `dataForGet(...)`, `dataForPost(...)`, etc. will not be called additionally if you return `true`. 132 | 133 | The following methods require data for the given routes GET, POST, PUT, DELETE and must be overriden if needed. If you return `nil`, the server will respond with a *Method not allowed* error code (Make sure that you have set the allowed routes in the `allowedRoutes` bitmask property). 134 | You have to return a tuple with a statuscode, optional payload, optional content format for your provided payload and (in case of POST and PUT) an optional locationURI. 135 | * `dataForGet(#queryDictionary: [String : String], options: [Int : [NSData]]) -> (statusCode: SCCodeValue, payloadData: NSData?, contentFormat: SCContentFormat!)?` 136 | * `dataForPost(#queryDictionary: [String : String], options: [Int : [NSData]], requestData: NSData?) -> (statusCode: SCCodeValue, payloadData: NSData?, contentFormat: SCContentFormat!, locationUri: String!)?` 137 | * `dataForPut(#queryDictionary: [String : String], options: [Int : [NSData]], requestData: NSData?) -> (statusCode: SCCodeValue, payloadData: NSData?, contentFormat: SCContentFormat!, locationUri: String!)?` 138 | * `dataForDelete(#queryDictionary: [String : String], options: [Int : [NSData]]) -> (statusCode: SCCodeValue, payloadData: NSData?, contentFormat: SCContentFormat!)?` 139 | 140 | ##### Server with Resources Example 141 | ```swift 142 | let server = SCServer(port: 5683) 143 | 144 | let resource = TestResourceModel(name: "test", allowedRoutes: SCAllowedRoute.Get.rawValue | SCAllowedRoute.Post.rawValue | SCAllowedRoute.Put.rawValue | SCAllowedRoute.Delete.rawValue, 145 | text: "This is a very long description text, I hope that all of you will like it") 146 | 147 | server?.resources.append(resource) 148 | server?.delegate = self 149 | ``` 150 | 151 | **Don't hesitate to contact me if something is unclear!** 152 | 153 | Examples: 154 | ===== 155 | Make sure to take a look at the examples, which show the library in action. Let me know if you have questions, or other issues. 156 | 157 | 158 | Used Libraries: 159 | ===== 160 | This version uses the public domain licensed CocoaAsyncSocket library 161 | for UDP-socket networking. 162 | [Click here](https://github.com/robbiehanson/CocoaAsyncSocket) for more information. 163 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 71 | 81 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /SwiftCoAP_Library/SCClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SCClient.swift 3 | // SwiftCoAP 4 | // 5 | // Created by Wojtek Kordylewski on 03.05.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | //MARK: 12 | //MARK: SC Client Delegate Protocol declaration 13 | 14 | @objc protocol SCClientDelegate { 15 | 16 | //Tells the delegate that a valid CoAP message was received 17 | func swiftCoapClient(_ client: SCClient, didReceiveMessage message: SCMessage) 18 | 19 | //Tells the delegate that an error occured during or before transmission (refer to the "SCClientErrorCode" Enum) 20 | @objc optional func swiftCoapClient(_ client: SCClient, didFailWithError error: NSError) 21 | 22 | //Tells the delegate that the respective message was sent. The property "number" indicates the amount of (re-)transmission attempts 23 | @objc optional func swiftCoapClient(_ client: SCClient, didSendMessage message: SCMessage, number: Int) 24 | } 25 | 26 | 27 | //MARK: 28 | //MARK: SC Client Error Code Enumeration 29 | 30 | enum SCClientErrorCode: Int { 31 | case transportLayerSendError, messageInvalidForSendingError, receivedInvalidMessageError, noResponseExpectedError, proxyingError 32 | 33 | func descriptionString() -> String { 34 | switch self { 35 | case .transportLayerSendError: 36 | return "Failed to send data via the given Transport Layer" 37 | case .messageInvalidForSendingError: 38 | return "CoAP-Message is not valid" 39 | case .receivedInvalidMessageError: 40 | return "Data received was not a valid CoAP Message" 41 | case .noResponseExpectedError: 42 | return "The recipient does not respond" 43 | case .proxyingError: 44 | return "HTTP-URL Request could not be sent" 45 | } 46 | } 47 | } 48 | 49 | 50 | //MARK: 51 | //MARK: SC Client IMPLEMENTATION 52 | 53 | class SCClient: NSObject { 54 | 55 | //MARK: Constants and Properties 56 | 57 | //CONSTANTS 58 | let kMaxObserveOptionValue: UInt = 8388608 59 | 60 | //INTERNAL PROPERTIES (allowed to modify) 61 | 62 | weak var delegate: SCClientDelegate? 63 | var sendToken = true //If true, a token with 4-8 Bytes is sent 64 | var autoBlock1SZX: UInt? = 2 { didSet { if let newValue = autoBlock1SZX { autoBlock1SZX = min(6, newValue) } } } //If not nil, Block1 transfer will be used automatically when the payload size exceeds the value 2^(autoBlock1SZX + 4). Valid Values: 0-6. 65 | 66 | var httpProxyingData: (hostName: String, port: UInt16)? //If not nil, all messages will be sent via http to the given proxy address 67 | var cachingActive = false //Activates caching 68 | var disableRetransmissions = false 69 | 70 | //READ-ONLY PROPERTIES 71 | 72 | fileprivate (set) var isMessageInTransmission = false //Indicates whether a message is in transmission and/or responses are still expected (e.g. separate, block, observe) 73 | 74 | //PRIVATE PROPERTIES 75 | 76 | fileprivate var transportLayerObject: SCCoAPTransportLayerProtocol! 77 | fileprivate var transmissionTimer: Timer! 78 | fileprivate var messageInTransmission: SCMessage! 79 | fileprivate var currentMessageId: UInt16 = UInt16(arc4random_uniform(0xFFFF) &+ 1) 80 | fileprivate var retransmissionCounter = 0 81 | fileprivate var currentTransmitWait = 0.0 82 | fileprivate var recentNotificationInfo: (Date, UInt)! 83 | lazy fileprivate var cachedMessagePairs = [SCMessage : SCMessage]() 84 | 85 | 86 | //MARK: Internal Methods (allowed to use) 87 | 88 | init(delegate: SCClientDelegate?, transportLayerObject: SCCoAPTransportLayerProtocol = SCCoAPUDPTransportLayer()) { 89 | self.delegate = delegate 90 | super.init() 91 | self.transportLayerObject = transportLayerObject 92 | self.transportLayerObject.transportLayerDelegate = self 93 | } 94 | 95 | func sendCoAPMessage(_ message: SCMessage, hostName: String, port: UInt16) { 96 | currentMessageId = (currentMessageId % 0xFFFF) + 1 97 | 98 | message.hostName = hostName 99 | message.port = port 100 | message.messageId = currentMessageId 101 | message.timeStamp = Date() 102 | 103 | messageInTransmission = message 104 | 105 | if sendToken { 106 | message.token = UInt64(arc4random_uniform(0xFFFFFFFF) + 1) + (UInt64(arc4random_uniform(0xFFFFFFFF) + 1) << 32) 107 | } 108 | 109 | if cachingActive && message.code == SCCodeValue(classValue: 0, detailValue: 01) { 110 | for cachedMessage in cachedMessagePairs.keys { 111 | if cachedMessage.equalForCachingWithMessage(message) { 112 | if cachedMessage.isFresh() { 113 | if message.options[SCOption.observe.rawValue] == nil { cachedMessage.options[SCOption.observe.rawValue] = nil } 114 | delegate?.swiftCoapClient(self, didReceiveMessage: cachedMessagePairs[cachedMessage]!) 115 | handleBlock2WithMessage(cachedMessagePairs[cachedMessage]!) 116 | return 117 | } 118 | else { 119 | cachedMessagePairs[cachedMessage] = nil 120 | break 121 | } 122 | } 123 | } 124 | } 125 | 126 | if httpProxyingData != nil { 127 | sendHttpMessageFromCoAPMessage(message) 128 | } 129 | else { 130 | if message.blockBody == nil, let autoB1SZX = autoBlock1SZX { 131 | let fixedByteSize = pow(2, Double(autoB1SZX) + 4) 132 | if let payload = message.payload { 133 | let blocksCount = ceil(Double(payload.count) / fixedByteSize) 134 | if blocksCount > 1 { 135 | message.blockBody = payload 136 | let blockValue = 8 + UInt(autoB1SZX) 137 | sendBlock1MessageForCurrentContext(payload: payload.subdata(in: (0 ..< Int(fixedByteSize))), blockValue: blockValue) 138 | return 139 | } 140 | } 141 | } 142 | 143 | initiateSending() 144 | } 145 | } 146 | 147 | 148 | // Cancels observe directly, sending the previous message with an Observe-Option Value of 1. Only effective, if the previous message initiated a registration as observer with the respective server. To cancel observer indirectly (forget about the current state) call "closeTransmission()" or send another Message (this cleans up the old state automatically) 149 | func cancelObserve() { 150 | let cancelMessage = SCMessage(code: SCCodeValue(classValue: 0, detailValue: 01)!, type: .nonConfirmable, payload: nil) 151 | cancelMessage.token = messageInTransmission.token 152 | cancelMessage.options = messageInTransmission.options 153 | currentMessageId = (currentMessageId % 0xFFFF) + 1 154 | cancelMessage.messageId = currentMessageId 155 | cancelMessage.hostName = messageInTransmission.hostName 156 | cancelMessage.port = messageInTransmission.port 157 | var cancelByte: UInt8 = 1 158 | cancelMessage.options[SCOption.observe.rawValue] = [Data(bytes: &cancelByte, count: 1)] 159 | if let messageData = cancelMessage.toData() { 160 | sendCoAPMessageOverTransportLayerWithData(messageData, host: messageInTransmission.hostName!, port: messageInTransmission.port!) 161 | } 162 | } 163 | 164 | 165 | //Closes the transmission. It is recommended to call this method anytime you do not expect to receive a response any longer. 166 | 167 | func closeTransmission() { 168 | transportLayerObject.closeTransmission() 169 | messageInTransmission = nil 170 | isMessageInTransmission = false 171 | transmissionTimer?.invalidate() 172 | transmissionTimer = nil 173 | recentNotificationInfo = nil 174 | cachedMessagePairs = [:] 175 | } 176 | 177 | // MARK: Private Methods 178 | 179 | fileprivate func initiateSending() { 180 | isMessageInTransmission = true 181 | transmissionTimer?.invalidate() 182 | transmissionTimer = nil 183 | recentNotificationInfo = nil 184 | 185 | if messageInTransmission.type == .confirmable && !disableRetransmissions { 186 | retransmissionCounter = 0 187 | currentTransmitWait = 0 188 | sendWithRentransmissionHandling() 189 | } 190 | else { 191 | sendPendingMessage() 192 | } 193 | } 194 | 195 | //Actually PRIVATE! Do not call from outside. Has to be internally visible as NSTimer won't find it otherwise 196 | 197 | @objc func sendWithRentransmissionHandling() { 198 | sendPendingMessage() 199 | 200 | if retransmissionCounter < SCMessage.kMaxRetransmit { 201 | let timeout = SCMessage.kAckTimeout * pow(2.0, Double(retransmissionCounter)) * (SCMessage.kAckRandomFactor - ((Double(arc4random()) / Double(UINT32_MAX)).truncatingRemainder(dividingBy: 0.5))); 202 | currentTransmitWait += timeout 203 | transmissionTimer = Timer(timeInterval: timeout, target: self, selector: #selector(SCClient.sendWithRentransmissionHandling), userInfo: nil, repeats: false) 204 | retransmissionCounter += 1 205 | } 206 | else { 207 | transmissionTimer = Timer(timeInterval: SCMessage.kMaxTransmitWait - currentTransmitWait, target: self, selector: #selector(SCClient.notifyNoResponseExpected), userInfo: nil, repeats: false) 208 | } 209 | RunLoop.current.add(transmissionTimer, forMode: RunLoop.Mode.common) 210 | } 211 | 212 | //Actually PRIVATE! Do not call from outside. Has to be internally visible as NSTimer won't find it otherwise 213 | 214 | @objc func notifyNoResponseExpected() { 215 | closeTransmission() 216 | notifyDelegateWithErrorCode(.noResponseExpectedError) 217 | } 218 | 219 | fileprivate func sendPendingMessage() { 220 | if let data = messageInTransmission.toData() { 221 | sendCoAPMessageOverTransportLayerWithData(data as Data, host: messageInTransmission.hostName!, port: messageInTransmission.port!, notifyDelegateAfterSuccess: true) 222 | } 223 | else { 224 | closeTransmission() 225 | notifyDelegateWithErrorCode(.messageInvalidForSendingError) 226 | } 227 | } 228 | 229 | fileprivate func sendEmptyMessageWithType(_ type: SCType, messageId: UInt16, toHost host: String, port: UInt16) { 230 | let emptyMessage = SCMessage() 231 | emptyMessage.type = type; 232 | emptyMessage.messageId = messageId 233 | if let messageData = emptyMessage.toData() { 234 | sendCoAPMessageOverTransportLayerWithData(messageData as Data, host: host, port: port) 235 | } 236 | } 237 | 238 | fileprivate func sendCoAPMessageOverTransportLayerWithData(_ data: Data, host: String, port: UInt16, notifyDelegateAfterSuccess: Bool = false) { 239 | do { 240 | try transportLayerObject.sendCoAPData(data, toHost: host, port: port) 241 | if notifyDelegateAfterSuccess { 242 | delegate?.swiftCoapClient?(self, didSendMessage: messageInTransmission, number: retransmissionCounter + 1) 243 | } 244 | } 245 | catch SCCoAPTransportLayerError.sendError(let errorDescription) { 246 | notifyDelegateWithTransportLayerErrorDescription(errorDescription) 247 | } 248 | catch SCCoAPTransportLayerError.setupError(let errorDescription) { 249 | notifyDelegateWithTransportLayerErrorDescription(errorDescription) 250 | } 251 | catch {} 252 | } 253 | 254 | fileprivate func notifyDelegateWithTransportLayerErrorDescription(_ errorDescription: String) { 255 | delegate?.swiftCoapClient?(self, didFailWithError: NSError(domain: SCMessage.kCoapErrorDomain, code: 0, userInfo: [NSLocalizedDescriptionKey : errorDescription])) 256 | } 257 | 258 | fileprivate func notifyDelegateWithErrorCode(_ clientErrorCode: SCClientErrorCode) { 259 | delegate?.swiftCoapClient?(self, didFailWithError: NSError(domain: SCMessage.kCoapErrorDomain, code: clientErrorCode.rawValue, userInfo: [NSLocalizedDescriptionKey : clientErrorCode.descriptionString()])) 260 | } 261 | 262 | fileprivate func handleBlock2WithMessage(_ message: SCMessage) { 263 | if let block2opt = message.options[SCOption.block2.rawValue], let blockData = block2opt.first { 264 | let actualValue = UInt.fromData(blockData) 265 | if actualValue & 8 == 8 { 266 | //more bit is set, request next block 267 | let blockMessage = SCMessage(code: messageInTransmission.code, type: messageInTransmission.type, payload: messageInTransmission.payload) 268 | blockMessage.options = messageInTransmission.options 269 | let newValue = (actualValue & ~8) + 16 270 | var byteArray = newValue.toByteArray() 271 | blockMessage.options[SCOption.block2.rawValue] = [Data(bytes: &byteArray, count: byteArray.count)] 272 | sendCoAPMessage(blockMessage, hostName: messageInTransmission.hostName!, port: messageInTransmission.port!) 273 | } 274 | else { 275 | isMessageInTransmission = false 276 | } 277 | } 278 | } 279 | 280 | fileprivate func continueBlock1ForBlockNumber(_ block: Int, szx: UInt) { 281 | let byteSize = pow(2, Double(szx) + 4) 282 | let blocksCount = ceil(Double(messageInTransmission.blockBody!.count) / byteSize) 283 | if block < Int(blocksCount) { 284 | var nextBlockLength: Int 285 | var blockValue: UInt = (UInt(block) << 4) + UInt(szx) 286 | 287 | if block < Int(blocksCount - 1) { 288 | nextBlockLength = Int(byteSize) 289 | blockValue += 8 290 | } 291 | else { 292 | nextBlockLength = messageInTransmission.blockBody!.count - Int(byteSize) * block 293 | } 294 | 295 | let startPos = Int(byteSize) * block 296 | sendBlock1MessageForCurrentContext(payload: messageInTransmission.blockBody!.subdata(in: (startPos ..< startPos + nextBlockLength)), blockValue: blockValue) 297 | } 298 | } 299 | 300 | fileprivate func sendBlock1MessageForCurrentContext(payload: Data, blockValue: UInt) { 301 | let blockMessage = SCMessage(code: messageInTransmission.code, type: messageInTransmission.type, payload: payload) 302 | blockMessage.options = messageInTransmission.options 303 | blockMessage.blockBody = messageInTransmission.blockBody 304 | var byteArray = blockValue.toByteArray() 305 | blockMessage.options[SCOption.block1.rawValue] = [Data(bytes: &byteArray, count: byteArray.count)] 306 | 307 | sendCoAPMessage(blockMessage, hostName: messageInTransmission.hostName!, port: messageInTransmission.port!) 308 | } 309 | 310 | fileprivate func sendHttpMessageFromCoAPMessage(_ message: SCMessage) { 311 | let urlRequest = message.toHttpUrlRequestWithUrl() 312 | let urlString = "http://\(httpProxyingData!.hostName):\(httpProxyingData!.port)/\(message.hostName!):\(message.port!)" 313 | urlRequest.url = URL(string: urlString) 314 | urlRequest.timeoutInterval = SCMessage.kMaxTransmitWait 315 | urlRequest.cachePolicy = .useProtocolCachePolicy 316 | 317 | NSURLConnection.sendAsynchronousRequest(urlRequest as URLRequest, queue: OperationQueue.main) { (response, data, error) -> Void in 318 | if error != nil { 319 | self.notifyDelegateWithErrorCode(.proxyingError) 320 | } 321 | else { 322 | let coapResponse = SCMessage.fromHttpUrlResponse(response as! HTTPURLResponse, data: data) 323 | coapResponse.timeStamp = Date() 324 | 325 | if self.cachingActive && self.messageInTransmission.code == SCCodeValue(classValue: 0, detailValue: 01) { 326 | self.cachedMessagePairs[self.messageInTransmission] = SCMessage.copyFromMessage(coapResponse) 327 | } 328 | 329 | self.delegate?.swiftCoapClient(self, didReceiveMessage: coapResponse) 330 | self.handleBlock2WithMessage(coapResponse) 331 | } 332 | } 333 | } 334 | } 335 | 336 | 337 | // MARK: 338 | // MARK: SC Client Extension 339 | // MARK: SC CoAP Transport Layer Delegate 340 | 341 | extension SCClient: SCCoAPTransportLayerDelegate { 342 | func transportLayerObject(_ transportLayerObject: SCCoAPTransportLayerProtocol, didReceiveData data: Data, fromHost host: String, port: UInt16) { 343 | if let message = SCMessage.fromData(data) { 344 | 345 | //Check for spam 346 | if message.messageId != messageInTransmission.messageId && message.token != messageInTransmission.token { 347 | if message.type.rawValue <= SCType.nonConfirmable.rawValue { 348 | sendEmptyMessageWithType(.reset, messageId: message.messageId, toHost: host, port: port) 349 | } 350 | return 351 | } 352 | 353 | //Invalidate Timer 354 | transmissionTimer?.invalidate() 355 | transmissionTimer = nil 356 | 357 | //Set timestamp 358 | message.timeStamp = Date() 359 | 360 | //Set return address 361 | message.hostName = host 362 | 363 | //Handle Caching, Separate, etc 364 | if cachingActive && messageInTransmission.code == SCCodeValue(classValue: 0, detailValue: 01) { 365 | cachedMessagePairs[messageInTransmission] = SCMessage.copyFromMessage(message) 366 | } 367 | 368 | //Handle Observe-Option (Observe Draft Section 3.4) 369 | if let observeValueArray = message.options[SCOption.observe.rawValue], let observeValue = observeValueArray.first { 370 | let currentNumber = UInt.fromData(observeValue) 371 | if recentNotificationInfo == nil || 372 | (recentNotificationInfo.1 < currentNumber && currentNumber - recentNotificationInfo.1 < kMaxObserveOptionValue) || 373 | (recentNotificationInfo.1 > currentNumber && recentNotificationInfo.1 - currentNumber > kMaxObserveOptionValue) || 374 | (recentNotificationInfo.0 .compare(message.timeStamp!.addingTimeInterval(128)) == .orderedAscending) { 375 | recentNotificationInfo = (message.timeStamp!, currentNumber) 376 | } 377 | else { 378 | return 379 | } 380 | } 381 | 382 | //Notify Delegate 383 | delegate?.swiftCoapClient(self, didReceiveMessage: message) 384 | 385 | //Handle Block2 386 | handleBlock2WithMessage(message) 387 | 388 | //Handle Block1 389 | if message.code.toCodeSample() == SCCodeSample.continue, let block1opt = message.options[SCOption.block1.rawValue], let blockData = block1opt.first { 390 | var actualValue = UInt.fromData(blockData) 391 | let serverSZX = actualValue & 0b111 392 | actualValue >>= 4 393 | if serverSZX <= 6 { 394 | var blockOffset = 1 395 | if serverSZX < autoBlock1SZX! { 396 | blockOffset = Int(pow(2, Double(autoBlock1SZX! - serverSZX))) 397 | autoBlock1SZX = serverSZX 398 | } 399 | continueBlock1ForBlockNumber(Int(actualValue) + blockOffset, szx: serverSZX) 400 | } 401 | } 402 | 403 | //Further Operations 404 | if message.type == .confirmable { 405 | sendEmptyMessageWithType(.acknowledgement, messageId: message.messageId, toHost: host, port: port) 406 | } 407 | 408 | if (message.type != .acknowledgement || message.code.toCodeSample() != .empty) && message.options[SCOption.block2.rawValue] == nil && message.code.toCodeSample() != SCCodeSample.continue { 409 | isMessageInTransmission = false 410 | } 411 | } 412 | else { 413 | notifyDelegateWithErrorCode(.receivedInvalidMessageError) 414 | } 415 | } 416 | 417 | func transportLayerObject(_ transportLayerObject: SCCoAPTransportLayerProtocol, didFailWithError error: NSError) { 418 | notifyDelegateWithErrorCode(.transportLayerSendError) 419 | transmissionTimer?.invalidate() 420 | transmissionTimer = nil 421 | } 422 | } 423 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample/SCClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SCClient.swift 3 | // SwiftCoAP 4 | // 5 | // Created by Wojtek Kordylewski on 03.05.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | //MARK: 12 | //MARK: SC Client Delegate Protocol declaration 13 | 14 | @objc protocol SCClientDelegate { 15 | 16 | //Tells the delegate that a valid CoAP message was received 17 | func swiftCoapClient(_ client: SCClient, didReceiveMessage message: SCMessage) 18 | 19 | //Tells the delegate that an error occured during or before transmission (refer to the "SCClientErrorCode" Enum) 20 | @objc optional func swiftCoapClient(_ client: SCClient, didFailWithError error: NSError) 21 | 22 | //Tells the delegate that the respective message was sent. The property "number" indicates the amount of (re-)transmission attempts 23 | @objc optional func swiftCoapClient(_ client: SCClient, didSendMessage message: SCMessage, number: Int) 24 | } 25 | 26 | 27 | //MARK: 28 | //MARK: SC Client Error Code Enumeration 29 | 30 | enum SCClientErrorCode: Int { 31 | case transportLayerSendError, messageInvalidForSendingError, receivedInvalidMessageError, noResponseExpectedError, proxyingError 32 | 33 | func descriptionString() -> String { 34 | switch self { 35 | case .transportLayerSendError: 36 | return "Failed to send data via the given Transport Layer" 37 | case .messageInvalidForSendingError: 38 | return "CoAP-Message is not valid" 39 | case .receivedInvalidMessageError: 40 | return "Data received was not a valid CoAP Message" 41 | case .noResponseExpectedError: 42 | return "The recipient does not respond" 43 | case .proxyingError: 44 | return "HTTP-URL Request could not be sent" 45 | } 46 | } 47 | } 48 | 49 | 50 | //MARK: 51 | //MARK: SC Client IMPLEMENTATION 52 | 53 | class SCClient: NSObject { 54 | 55 | //MARK: Constants and Properties 56 | 57 | //CONSTANTS 58 | let kMaxObserveOptionValue: UInt = 8388608 59 | 60 | //INTERNAL PROPERTIES (allowed to modify) 61 | 62 | weak var delegate: SCClientDelegate? 63 | var sendToken = true //If true, a token with 4-8 Bytes is sent 64 | var autoBlock1SZX: UInt? = 2 { didSet { if let newValue = autoBlock1SZX { autoBlock1SZX = min(6, newValue) } } } //If not nil, Block1 transfer will be used automatically when the payload size exceeds the value 2^(autoBlock1SZX + 4). Valid Values: 0-6. 65 | 66 | var httpProxyingData: (hostName: String, port: UInt16)? //If not nil, all messages will be sent via http to the given proxy address 67 | var cachingActive = false //Activates caching 68 | var disableRetransmissions = false 69 | 70 | //READ-ONLY PROPERTIES 71 | 72 | fileprivate (set) var isMessageInTransmission = false //Indicates whether a message is in transmission and/or responses are still expected (e.g. separate, block, observe) 73 | 74 | //PRIVATE PROPERTIES 75 | 76 | fileprivate var transportLayerObject: SCCoAPTransportLayerProtocol! 77 | fileprivate var transmissionTimer: Timer! 78 | fileprivate var messageInTransmission: SCMessage! 79 | fileprivate var currentMessageId: UInt16 = UInt16(arc4random_uniform(0xFFFF) &+ 1) 80 | fileprivate var retransmissionCounter = 0 81 | fileprivate var currentTransmitWait = 0.0 82 | fileprivate var recentNotificationInfo: (Date, UInt)! 83 | lazy fileprivate var cachedMessagePairs = [SCMessage : SCMessage]() 84 | 85 | 86 | //MARK: Internal Methods (allowed to use) 87 | 88 | init(delegate: SCClientDelegate?, transportLayerObject: SCCoAPTransportLayerProtocol = SCCoAPUDPTransportLayer()) { 89 | self.delegate = delegate 90 | super.init() 91 | self.transportLayerObject = transportLayerObject 92 | self.transportLayerObject.transportLayerDelegate = self 93 | } 94 | 95 | func sendCoAPMessage(_ message: SCMessage, hostName: String, port: UInt16) { 96 | currentMessageId = (currentMessageId % 0xFFFF) + 1 97 | 98 | message.hostName = hostName 99 | message.port = port 100 | message.messageId = currentMessageId 101 | message.timeStamp = Date() 102 | 103 | messageInTransmission = message 104 | 105 | if sendToken { 106 | message.token = UInt64(arc4random_uniform(0xFFFFFFFF) + 1) + (UInt64(arc4random_uniform(0xFFFFFFFF) + 1) << 32) 107 | } 108 | 109 | if cachingActive && message.code == SCCodeValue(classValue: 0, detailValue: 01) { 110 | for cachedMessage in cachedMessagePairs.keys { 111 | if cachedMessage.equalForCachingWithMessage(message) { 112 | if cachedMessage.isFresh() { 113 | if message.options[SCOption.observe.rawValue] == nil { cachedMessage.options[SCOption.observe.rawValue] = nil } 114 | delegate?.swiftCoapClient(self, didReceiveMessage: cachedMessagePairs[cachedMessage]!) 115 | handleBlock2WithMessage(cachedMessagePairs[cachedMessage]!) 116 | return 117 | } 118 | else { 119 | cachedMessagePairs[cachedMessage] = nil 120 | break 121 | } 122 | } 123 | } 124 | } 125 | 126 | if httpProxyingData != nil { 127 | sendHttpMessageFromCoAPMessage(message) 128 | } 129 | else { 130 | if message.blockBody == nil, let autoB1SZX = autoBlock1SZX { 131 | let fixedByteSize = pow(2, Double(autoB1SZX) + 4) 132 | if let payload = message.payload { 133 | let blocksCount = ceil(Double(payload.count) / fixedByteSize) 134 | if blocksCount > 1 { 135 | message.blockBody = payload 136 | let blockValue = 8 + UInt(autoB1SZX) 137 | sendBlock1MessageForCurrentContext(payload: payload.subdata(in: (0 ..< Int(fixedByteSize))), blockValue: blockValue) 138 | return 139 | } 140 | } 141 | } 142 | 143 | initiateSending() 144 | } 145 | } 146 | 147 | 148 | // Cancels observe directly, sending the previous message with an Observe-Option Value of 1. Only effective, if the previous message initiated a registration as observer with the respective server. To cancel observer indirectly (forget about the current state) call "closeTransmission()" or send another Message (this cleans up the old state automatically) 149 | func cancelObserve() { 150 | let cancelMessage = SCMessage(code: SCCodeValue(classValue: 0, detailValue: 01)!, type: .nonConfirmable, payload: nil) 151 | cancelMessage.token = messageInTransmission.token 152 | cancelMessage.options = messageInTransmission.options 153 | currentMessageId = (currentMessageId % 0xFFFF) + 1 154 | cancelMessage.messageId = currentMessageId 155 | cancelMessage.hostName = messageInTransmission.hostName 156 | cancelMessage.port = messageInTransmission.port 157 | var cancelByte: UInt8 = 1 158 | cancelMessage.options[SCOption.observe.rawValue] = [Data(bytes: &cancelByte, count: 1)] 159 | if let messageData = cancelMessage.toData() { 160 | sendCoAPMessageOverTransportLayerWithData(messageData, host: messageInTransmission.hostName!, port: messageInTransmission.port!) 161 | } 162 | } 163 | 164 | 165 | //Closes the transmission. It is recommended to call this method anytime you do not expect to receive a response any longer. 166 | 167 | func closeTransmission() { 168 | transportLayerObject.closeTransmission() 169 | messageInTransmission = nil 170 | isMessageInTransmission = false 171 | transmissionTimer?.invalidate() 172 | transmissionTimer = nil 173 | recentNotificationInfo = nil 174 | cachedMessagePairs = [:] 175 | } 176 | 177 | // MARK: Private Methods 178 | 179 | fileprivate func initiateSending() { 180 | isMessageInTransmission = true 181 | transmissionTimer?.invalidate() 182 | transmissionTimer = nil 183 | recentNotificationInfo = nil 184 | 185 | if messageInTransmission.type == .confirmable && !disableRetransmissions { 186 | retransmissionCounter = 0 187 | currentTransmitWait = 0 188 | sendWithRentransmissionHandling() 189 | } 190 | else { 191 | sendPendingMessage() 192 | } 193 | } 194 | 195 | //Actually PRIVATE! Do not call from outside. Has to be internally visible as NSTimer won't find it otherwise 196 | 197 | @objc func sendWithRentransmissionHandling() { 198 | sendPendingMessage() 199 | 200 | if retransmissionCounter < SCMessage.kMaxRetransmit { 201 | let timeout = SCMessage.kAckTimeout * pow(2.0, Double(retransmissionCounter)) * (SCMessage.kAckRandomFactor - ((Double(arc4random()) / Double(UINT32_MAX)).truncatingRemainder(dividingBy: 0.5))); 202 | currentTransmitWait += timeout 203 | transmissionTimer = Timer(timeInterval: timeout, target: self, selector: #selector(SCClient.sendWithRentransmissionHandling), userInfo: nil, repeats: false) 204 | retransmissionCounter += 1 205 | } 206 | else { 207 | transmissionTimer = Timer(timeInterval: SCMessage.kMaxTransmitWait - currentTransmitWait, target: self, selector: #selector(SCClient.notifyNoResponseExpected), userInfo: nil, repeats: false) 208 | } 209 | RunLoop.current.add(transmissionTimer, forMode: RunLoop.Mode.common) 210 | } 211 | 212 | //Actually PRIVATE! Do not call from outside. Has to be internally visible as NSTimer won't find it otherwise 213 | 214 | @objc func notifyNoResponseExpected() { 215 | closeTransmission() 216 | notifyDelegateWithErrorCode(.noResponseExpectedError) 217 | } 218 | 219 | fileprivate func sendPendingMessage() { 220 | if let data = messageInTransmission.toData() { 221 | sendCoAPMessageOverTransportLayerWithData(data as Data, host: messageInTransmission.hostName!, port: messageInTransmission.port!, notifyDelegateAfterSuccess: true) 222 | } 223 | else { 224 | closeTransmission() 225 | notifyDelegateWithErrorCode(.messageInvalidForSendingError) 226 | } 227 | } 228 | 229 | fileprivate func sendEmptyMessageWithType(_ type: SCType, messageId: UInt16, toHost host: String, port: UInt16) { 230 | let emptyMessage = SCMessage() 231 | emptyMessage.type = type; 232 | emptyMessage.messageId = messageId 233 | if let messageData = emptyMessage.toData() { 234 | sendCoAPMessageOverTransportLayerWithData(messageData as Data, host: host, port: port) 235 | } 236 | } 237 | 238 | fileprivate func sendCoAPMessageOverTransportLayerWithData(_ data: Data, host: String, port: UInt16, notifyDelegateAfterSuccess: Bool = false) { 239 | do { 240 | try transportLayerObject.sendCoAPData(data, toHost: host, port: port) 241 | if notifyDelegateAfterSuccess { 242 | delegate?.swiftCoapClient?(self, didSendMessage: messageInTransmission, number: retransmissionCounter + 1) 243 | } 244 | } 245 | catch SCCoAPTransportLayerError.sendError(let errorDescription) { 246 | notifyDelegateWithTransportLayerErrorDescription(errorDescription) 247 | } 248 | catch SCCoAPTransportLayerError.setupError(let errorDescription) { 249 | notifyDelegateWithTransportLayerErrorDescription(errorDescription) 250 | } 251 | catch {} 252 | } 253 | 254 | fileprivate func notifyDelegateWithTransportLayerErrorDescription(_ errorDescription: String) { 255 | delegate?.swiftCoapClient?(self, didFailWithError: NSError(domain: SCMessage.kCoapErrorDomain, code: 0, userInfo: [NSLocalizedDescriptionKey : errorDescription])) 256 | } 257 | 258 | fileprivate func notifyDelegateWithErrorCode(_ clientErrorCode: SCClientErrorCode) { 259 | delegate?.swiftCoapClient?(self, didFailWithError: NSError(domain: SCMessage.kCoapErrorDomain, code: clientErrorCode.rawValue, userInfo: [NSLocalizedDescriptionKey : clientErrorCode.descriptionString()])) 260 | } 261 | 262 | fileprivate func handleBlock2WithMessage(_ message: SCMessage) { 263 | if let block2opt = message.options[SCOption.block2.rawValue], let blockData = block2opt.first { 264 | let actualValue = UInt.fromData(blockData) 265 | if actualValue & 8 == 8 { 266 | //more bit is set, request next block 267 | let blockMessage = SCMessage(code: messageInTransmission.code, type: messageInTransmission.type, payload: messageInTransmission.payload) 268 | blockMessage.options = messageInTransmission.options 269 | let newValue = (actualValue & ~8) + 16 270 | var byteArray = newValue.toByteArray() 271 | blockMessage.options[SCOption.block2.rawValue] = [Data(bytes: &byteArray, count: byteArray.count)] 272 | sendCoAPMessage(blockMessage, hostName: messageInTransmission.hostName!, port: messageInTransmission.port!) 273 | } 274 | else { 275 | isMessageInTransmission = false 276 | } 277 | } 278 | } 279 | 280 | fileprivate func continueBlock1ForBlockNumber(_ block: Int, szx: UInt) { 281 | let byteSize = pow(2, Double(szx) + 4) 282 | let blocksCount = ceil(Double(messageInTransmission.blockBody!.count) / byteSize) 283 | if block < Int(blocksCount) { 284 | var nextBlockLength: Int 285 | var blockValue: UInt = (UInt(block) << 4) + UInt(szx) 286 | 287 | if block < Int(blocksCount - 1) { 288 | nextBlockLength = Int(byteSize) 289 | blockValue += 8 290 | } 291 | else { 292 | nextBlockLength = messageInTransmission.blockBody!.count - Int(byteSize) * block 293 | } 294 | 295 | let startPos = Int(byteSize) * block 296 | sendBlock1MessageForCurrentContext(payload: messageInTransmission.blockBody!.subdata(in: (startPos ..< startPos + nextBlockLength)), blockValue: blockValue) 297 | } 298 | } 299 | 300 | fileprivate func sendBlock1MessageForCurrentContext(payload: Data, blockValue: UInt) { 301 | let blockMessage = SCMessage(code: messageInTransmission.code, type: messageInTransmission.type, payload: payload) 302 | blockMessage.options = messageInTransmission.options 303 | blockMessage.blockBody = messageInTransmission.blockBody 304 | var byteArray = blockValue.toByteArray() 305 | blockMessage.options[SCOption.block1.rawValue] = [Data(bytes: &byteArray, count: byteArray.count)] 306 | 307 | sendCoAPMessage(blockMessage, hostName: messageInTransmission.hostName!, port: messageInTransmission.port!) 308 | } 309 | 310 | fileprivate func sendHttpMessageFromCoAPMessage(_ message: SCMessage) { 311 | let urlRequest = message.toHttpUrlRequestWithUrl() 312 | let urlString = "http://\(httpProxyingData!.hostName):\(httpProxyingData!.port)/\(message.hostName!):\(message.port!)" 313 | urlRequest.url = URL(string: urlString) 314 | urlRequest.timeoutInterval = SCMessage.kMaxTransmitWait 315 | urlRequest.cachePolicy = .useProtocolCachePolicy 316 | 317 | NSURLConnection.sendAsynchronousRequest(urlRequest as URLRequest, queue: OperationQueue.main) { (response, data, error) -> Void in 318 | if error != nil { 319 | self.notifyDelegateWithErrorCode(.proxyingError) 320 | } 321 | else { 322 | let coapResponse = SCMessage.fromHttpUrlResponse(response as! HTTPURLResponse, data: data) 323 | coapResponse.timeStamp = Date() 324 | 325 | if self.cachingActive && self.messageInTransmission.code == SCCodeValue(classValue: 0, detailValue: 01) { 326 | self.cachedMessagePairs[self.messageInTransmission] = SCMessage.copyFromMessage(coapResponse) 327 | } 328 | 329 | self.delegate?.swiftCoapClient(self, didReceiveMessage: coapResponse) 330 | self.handleBlock2WithMessage(coapResponse) 331 | } 332 | } 333 | } 334 | } 335 | 336 | 337 | // MARK: 338 | // MARK: SC Client Extension 339 | // MARK: SC CoAP Transport Layer Delegate 340 | 341 | extension SCClient: SCCoAPTransportLayerDelegate { 342 | func transportLayerObject(_ transportLayerObject: SCCoAPTransportLayerProtocol, didReceiveData data: Data, fromHost host: String, port: UInt16) { 343 | if let message = SCMessage.fromData(data) { 344 | 345 | //Check for spam 346 | if message.messageId != messageInTransmission.messageId && message.token != messageInTransmission.token { 347 | if message.type.rawValue <= SCType.nonConfirmable.rawValue { 348 | sendEmptyMessageWithType(.reset, messageId: message.messageId, toHost: host, port: port) 349 | } 350 | return 351 | } 352 | 353 | //Invalidate Timer 354 | transmissionTimer?.invalidate() 355 | transmissionTimer = nil 356 | 357 | //Set timestamp 358 | message.timeStamp = Date() 359 | 360 | //Set return address 361 | message.hostName = host 362 | 363 | //Handle Caching, Separate, etc 364 | if cachingActive && messageInTransmission.code == SCCodeValue(classValue: 0, detailValue: 01) { 365 | cachedMessagePairs[messageInTransmission] = SCMessage.copyFromMessage(message) 366 | } 367 | 368 | //Handle Observe-Option (Observe Draft Section 3.4) 369 | if let observeValueArray = message.options[SCOption.observe.rawValue], let observeValue = observeValueArray.first { 370 | let currentNumber = UInt.fromData(observeValue) 371 | if recentNotificationInfo == nil || 372 | (recentNotificationInfo.1 < currentNumber && currentNumber - recentNotificationInfo.1 < kMaxObserveOptionValue) || 373 | (recentNotificationInfo.1 > currentNumber && recentNotificationInfo.1 - currentNumber > kMaxObserveOptionValue) || 374 | (recentNotificationInfo.0 .compare(message.timeStamp!.addingTimeInterval(128)) == .orderedAscending) { 375 | recentNotificationInfo = (message.timeStamp!, currentNumber) 376 | } 377 | else { 378 | return 379 | } 380 | } 381 | 382 | //Notify Delegate 383 | delegate?.swiftCoapClient(self, didReceiveMessage: message) 384 | 385 | //Handle Block2 386 | handleBlock2WithMessage(message) 387 | 388 | //Handle Block1 389 | if message.code.toCodeSample() == SCCodeSample.continue, let block1opt = message.options[SCOption.block1.rawValue], let blockData = block1opt.first { 390 | var actualValue = UInt.fromData(blockData) 391 | let serverSZX = actualValue & 0b111 392 | actualValue >>= 4 393 | if serverSZX <= 6 { 394 | var blockOffset = 1 395 | if serverSZX < autoBlock1SZX! { 396 | blockOffset = Int(pow(2, Double(autoBlock1SZX! - serverSZX))) 397 | autoBlock1SZX = serverSZX 398 | } 399 | continueBlock1ForBlockNumber(Int(actualValue) + blockOffset, szx: serverSZX) 400 | } 401 | } 402 | 403 | //Further Operations 404 | if message.type == .confirmable { 405 | sendEmptyMessageWithType(.acknowledgement, messageId: message.messageId, toHost: host, port: port) 406 | } 407 | 408 | if (message.type != .acknowledgement || message.code.toCodeSample() != .empty) && message.options[SCOption.block2.rawValue] == nil && message.code.toCodeSample() != SCCodeSample.continue { 409 | isMessageInTransmission = false 410 | } 411 | } 412 | else { 413 | notifyDelegateWithErrorCode(.receivedInvalidMessageError) 414 | } 415 | } 416 | 417 | func transportLayerObject(_ transportLayerObject: SCCoAPTransportLayerProtocol, didFailWithError error: NSError) { 418 | notifyDelegateWithErrorCode(.transportLayerSendError) 419 | transmissionTimer?.invalidate() 420 | transmissionTimer = nil 421 | } 422 | } 423 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPClientExample/SwiftCoAPClientExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 934E12081B1F858100441526 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934E12071B1F858100441526 /* AppDelegate.swift */; }; 11 | 934E120D1B1F858100441526 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 934E120B1B1F858100441526 /* Main.storyboard */; }; 12 | 934E120F1B1F858100441526 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 934E120E1B1F858100441526 /* Images.xcassets */; }; 13 | 934E12121B1F858100441526 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 934E12101B1F858100441526 /* LaunchScreen.xib */; }; 14 | 934E121E1B1F858100441526 /* SwiftCoAPClientExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934E121D1B1F858100441526 /* SwiftCoAPClientExampleTests.swift */; }; 15 | 934E122D1B1F865100441526 /* GCDAsyncUdpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 934E122A1B1F865100441526 /* GCDAsyncUdpSocket.m */; }; 16 | 934E122E1B1F865100441526 /* SCClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934E122B1B1F865100441526 /* SCClient.swift */; }; 17 | 934E122F1B1F865100441526 /* SCMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934E122C1B1F865100441526 /* SCMessage.swift */; }; 18 | 934E12311B1F8A2800441526 /* ExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934E12301B1F8A2800441526 /* ExampleViewController.swift */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 934E12181B1F858100441526 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 934E11FA1B1F858100441526 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 934E12011B1F858100441526; 27 | remoteInfo = SwiftCoAPClientExample; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 934E12021B1F858100441526 /* SwiftCoAPClientExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftCoAPClientExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 934E12061B1F858100441526 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | 934E12071B1F858100441526 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 35 | 934E120C1B1F858100441526 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 36 | 934E120E1B1F858100441526 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 37 | 934E12111B1F858100441526 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 38 | 934E12171B1F858100441526 /* SwiftCoAPClientExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftCoAPClientExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 934E121C1B1F858100441526 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 934E121D1B1F858100441526 /* SwiftCoAPClientExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftCoAPClientExampleTests.swift; sourceTree = ""; }; 41 | 934E12281B1F865000441526 /* SwiftCoAPClientExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftCoAPClientExample-Bridging-Header.h"; sourceTree = ""; }; 42 | 934E12291B1F865100441526 /* GCDAsyncUdpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDAsyncUdpSocket.h; sourceTree = ""; }; 43 | 934E122A1B1F865100441526 /* GCDAsyncUdpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDAsyncUdpSocket.m; sourceTree = ""; }; 44 | 934E122B1B1F865100441526 /* SCClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SCClient.swift; sourceTree = ""; }; 45 | 934E122C1B1F865100441526 /* SCMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SCMessage.swift; sourceTree = ""; }; 46 | 934E12301B1F8A2800441526 /* ExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleViewController.swift; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 934E11FF1B1F858100441526 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | 934E12141B1F858100441526 /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 934E11F91B1F858100441526 = { 68 | isa = PBXGroup; 69 | children = ( 70 | 934E12041B1F858100441526 /* SwiftCoAPClientExample */, 71 | 934E121A1B1F858100441526 /* SwiftCoAPClientExampleTests */, 72 | 934E12031B1F858100441526 /* Products */, 73 | ); 74 | sourceTree = ""; 75 | }; 76 | 934E12031B1F858100441526 /* Products */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 934E12021B1F858100441526 /* SwiftCoAPClientExample.app */, 80 | 934E12171B1F858100441526 /* SwiftCoAPClientExampleTests.xctest */, 81 | ); 82 | name = Products; 83 | sourceTree = ""; 84 | }; 85 | 934E12041B1F858100441526 /* SwiftCoAPClientExample */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 934E12271B1F862600441526 /* SwiftCoAP Library */, 89 | 934E12071B1F858100441526 /* AppDelegate.swift */, 90 | 934E12301B1F8A2800441526 /* ExampleViewController.swift */, 91 | 934E120B1B1F858100441526 /* Main.storyboard */, 92 | 934E120E1B1F858100441526 /* Images.xcassets */, 93 | 934E12101B1F858100441526 /* LaunchScreen.xib */, 94 | 934E12051B1F858100441526 /* Supporting Files */, 95 | ); 96 | path = SwiftCoAPClientExample; 97 | sourceTree = ""; 98 | }; 99 | 934E12051B1F858100441526 /* Supporting Files */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 934E12061B1F858100441526 /* Info.plist */, 103 | ); 104 | name = "Supporting Files"; 105 | sourceTree = ""; 106 | }; 107 | 934E121A1B1F858100441526 /* SwiftCoAPClientExampleTests */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 934E121D1B1F858100441526 /* SwiftCoAPClientExampleTests.swift */, 111 | 934E121B1B1F858100441526 /* Supporting Files */, 112 | ); 113 | path = SwiftCoAPClientExampleTests; 114 | sourceTree = ""; 115 | }; 116 | 934E121B1B1F858100441526 /* Supporting Files */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 934E121C1B1F858100441526 /* Info.plist */, 120 | ); 121 | name = "Supporting Files"; 122 | sourceTree = ""; 123 | }; 124 | 934E12271B1F862600441526 /* SwiftCoAP Library */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 934E12291B1F865100441526 /* GCDAsyncUdpSocket.h */, 128 | 934E122A1B1F865100441526 /* GCDAsyncUdpSocket.m */, 129 | 934E122B1B1F865100441526 /* SCClient.swift */, 130 | 934E122C1B1F865100441526 /* SCMessage.swift */, 131 | 934E12281B1F865000441526 /* SwiftCoAPClientExample-Bridging-Header.h */, 132 | ); 133 | name = "SwiftCoAP Library"; 134 | sourceTree = ""; 135 | }; 136 | /* End PBXGroup section */ 137 | 138 | /* Begin PBXNativeTarget section */ 139 | 934E12011B1F858100441526 /* SwiftCoAPClientExample */ = { 140 | isa = PBXNativeTarget; 141 | buildConfigurationList = 934E12211B1F858100441526 /* Build configuration list for PBXNativeTarget "SwiftCoAPClientExample" */; 142 | buildPhases = ( 143 | 934E11FE1B1F858100441526 /* Sources */, 144 | 934E11FF1B1F858100441526 /* Frameworks */, 145 | 934E12001B1F858100441526 /* Resources */, 146 | ); 147 | buildRules = ( 148 | ); 149 | dependencies = ( 150 | ); 151 | name = SwiftCoAPClientExample; 152 | productName = SwiftCoAPClientExample; 153 | productReference = 934E12021B1F858100441526 /* SwiftCoAPClientExample.app */; 154 | productType = "com.apple.product-type.application"; 155 | }; 156 | 934E12161B1F858100441526 /* SwiftCoAPClientExampleTests */ = { 157 | isa = PBXNativeTarget; 158 | buildConfigurationList = 934E12241B1F858100441526 /* Build configuration list for PBXNativeTarget "SwiftCoAPClientExampleTests" */; 159 | buildPhases = ( 160 | 934E12131B1F858100441526 /* Sources */, 161 | 934E12141B1F858100441526 /* Frameworks */, 162 | 934E12151B1F858100441526 /* Resources */, 163 | ); 164 | buildRules = ( 165 | ); 166 | dependencies = ( 167 | 934E12191B1F858100441526 /* PBXTargetDependency */, 168 | ); 169 | name = SwiftCoAPClientExampleTests; 170 | productName = SwiftCoAPClientExampleTests; 171 | productReference = 934E12171B1F858100441526 /* SwiftCoAPClientExampleTests.xctest */; 172 | productType = "com.apple.product-type.bundle.unit-test"; 173 | }; 174 | /* End PBXNativeTarget section */ 175 | 176 | /* Begin PBXProject section */ 177 | 934E11FA1B1F858100441526 /* Project object */ = { 178 | isa = PBXProject; 179 | attributes = { 180 | LastSwiftUpdateCheck = 0700; 181 | LastUpgradeCheck = 1010; 182 | ORGANIZATIONNAME = "Wojtek Kordylewski"; 183 | TargetAttributes = { 184 | 934E12011B1F858100441526 = { 185 | CreatedOnToolsVersion = 6.3.2; 186 | LastSwiftMigration = 1010; 187 | }; 188 | 934E12161B1F858100441526 = { 189 | CreatedOnToolsVersion = 6.3.2; 190 | LastSwiftMigration = 1010; 191 | TestTargetID = 934E12011B1F858100441526; 192 | }; 193 | }; 194 | }; 195 | buildConfigurationList = 934E11FD1B1F858100441526 /* Build configuration list for PBXProject "SwiftCoAPClientExample" */; 196 | compatibilityVersion = "Xcode 3.2"; 197 | developmentRegion = English; 198 | hasScannedForEncodings = 0; 199 | knownRegions = ( 200 | en, 201 | Base, 202 | ); 203 | mainGroup = 934E11F91B1F858100441526; 204 | productRefGroup = 934E12031B1F858100441526 /* Products */; 205 | projectDirPath = ""; 206 | projectRoot = ""; 207 | targets = ( 208 | 934E12011B1F858100441526 /* SwiftCoAPClientExample */, 209 | 934E12161B1F858100441526 /* SwiftCoAPClientExampleTests */, 210 | ); 211 | }; 212 | /* End PBXProject section */ 213 | 214 | /* Begin PBXResourcesBuildPhase section */ 215 | 934E12001B1F858100441526 /* Resources */ = { 216 | isa = PBXResourcesBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | 934E120D1B1F858100441526 /* Main.storyboard in Resources */, 220 | 934E12121B1F858100441526 /* LaunchScreen.xib in Resources */, 221 | 934E120F1B1F858100441526 /* Images.xcassets in Resources */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | 934E12151B1F858100441526 /* Resources */ = { 226 | isa = PBXResourcesBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXResourcesBuildPhase section */ 233 | 234 | /* Begin PBXSourcesBuildPhase section */ 235 | 934E11FE1B1F858100441526 /* Sources */ = { 236 | isa = PBXSourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | 934E122D1B1F865100441526 /* GCDAsyncUdpSocket.m in Sources */, 240 | 934E122F1B1F865100441526 /* SCMessage.swift in Sources */, 241 | 934E122E1B1F865100441526 /* SCClient.swift in Sources */, 242 | 934E12081B1F858100441526 /* AppDelegate.swift in Sources */, 243 | 934E12311B1F8A2800441526 /* ExampleViewController.swift in Sources */, 244 | ); 245 | runOnlyForDeploymentPostprocessing = 0; 246 | }; 247 | 934E12131B1F858100441526 /* Sources */ = { 248 | isa = PBXSourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | 934E121E1B1F858100441526 /* SwiftCoAPClientExampleTests.swift in Sources */, 252 | ); 253 | runOnlyForDeploymentPostprocessing = 0; 254 | }; 255 | /* End PBXSourcesBuildPhase section */ 256 | 257 | /* Begin PBXTargetDependency section */ 258 | 934E12191B1F858100441526 /* PBXTargetDependency */ = { 259 | isa = PBXTargetDependency; 260 | target = 934E12011B1F858100441526 /* SwiftCoAPClientExample */; 261 | targetProxy = 934E12181B1F858100441526 /* PBXContainerItemProxy */; 262 | }; 263 | /* End PBXTargetDependency section */ 264 | 265 | /* Begin PBXVariantGroup section */ 266 | 934E120B1B1F858100441526 /* Main.storyboard */ = { 267 | isa = PBXVariantGroup; 268 | children = ( 269 | 934E120C1B1F858100441526 /* Base */, 270 | ); 271 | name = Main.storyboard; 272 | sourceTree = ""; 273 | }; 274 | 934E12101B1F858100441526 /* LaunchScreen.xib */ = { 275 | isa = PBXVariantGroup; 276 | children = ( 277 | 934E12111B1F858100441526 /* Base */, 278 | ); 279 | name = LaunchScreen.xib; 280 | sourceTree = ""; 281 | }; 282 | /* End PBXVariantGroup section */ 283 | 284 | /* Begin XCBuildConfiguration section */ 285 | 934E121F1B1F858100441526 /* Debug */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | ALWAYS_SEARCH_USER_PATHS = NO; 289 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 290 | CLANG_CXX_LIBRARY = "libc++"; 291 | CLANG_ENABLE_MODULES = YES; 292 | CLANG_ENABLE_OBJC_ARC = YES; 293 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 294 | CLANG_WARN_BOOL_CONVERSION = YES; 295 | CLANG_WARN_COMMA = YES; 296 | CLANG_WARN_CONSTANT_CONVERSION = YES; 297 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 298 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 299 | CLANG_WARN_EMPTY_BODY = YES; 300 | CLANG_WARN_ENUM_CONVERSION = YES; 301 | CLANG_WARN_INFINITE_RECURSION = YES; 302 | CLANG_WARN_INT_CONVERSION = YES; 303 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 304 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 305 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 306 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 307 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 308 | CLANG_WARN_STRICT_PROTOTYPES = YES; 309 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 310 | CLANG_WARN_UNREACHABLE_CODE = YES; 311 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 312 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 313 | COPY_PHASE_STRIP = NO; 314 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 315 | ENABLE_STRICT_OBJC_MSGSEND = YES; 316 | ENABLE_TESTABILITY = YES; 317 | GCC_C_LANGUAGE_STANDARD = gnu99; 318 | GCC_DYNAMIC_NO_PIC = NO; 319 | GCC_NO_COMMON_BLOCKS = YES; 320 | GCC_OPTIMIZATION_LEVEL = 0; 321 | GCC_PREPROCESSOR_DEFINITIONS = ( 322 | "DEBUG=1", 323 | "$(inherited)", 324 | ); 325 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 326 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 327 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 328 | GCC_WARN_UNDECLARED_SELECTOR = YES; 329 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 330 | GCC_WARN_UNUSED_FUNCTION = YES; 331 | GCC_WARN_UNUSED_VARIABLE = YES; 332 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 333 | MTL_ENABLE_DEBUG_INFO = YES; 334 | ONLY_ACTIVE_ARCH = YES; 335 | SDKROOT = iphoneos; 336 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 337 | TARGETED_DEVICE_FAMILY = "1,2"; 338 | }; 339 | name = Debug; 340 | }; 341 | 934E12201B1F858100441526 /* Release */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | ALWAYS_SEARCH_USER_PATHS = NO; 345 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 346 | CLANG_CXX_LIBRARY = "libc++"; 347 | CLANG_ENABLE_MODULES = YES; 348 | CLANG_ENABLE_OBJC_ARC = YES; 349 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 350 | CLANG_WARN_BOOL_CONVERSION = YES; 351 | CLANG_WARN_COMMA = YES; 352 | CLANG_WARN_CONSTANT_CONVERSION = YES; 353 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 354 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 355 | CLANG_WARN_EMPTY_BODY = YES; 356 | CLANG_WARN_ENUM_CONVERSION = YES; 357 | CLANG_WARN_INFINITE_RECURSION = YES; 358 | CLANG_WARN_INT_CONVERSION = YES; 359 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 360 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 361 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 362 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 363 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 364 | CLANG_WARN_STRICT_PROTOTYPES = YES; 365 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 366 | CLANG_WARN_UNREACHABLE_CODE = YES; 367 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 368 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 369 | COPY_PHASE_STRIP = NO; 370 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 371 | ENABLE_NS_ASSERTIONS = NO; 372 | ENABLE_STRICT_OBJC_MSGSEND = YES; 373 | GCC_C_LANGUAGE_STANDARD = gnu99; 374 | GCC_NO_COMMON_BLOCKS = YES; 375 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 376 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 377 | GCC_WARN_UNDECLARED_SELECTOR = YES; 378 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 379 | GCC_WARN_UNUSED_FUNCTION = YES; 380 | GCC_WARN_UNUSED_VARIABLE = YES; 381 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 382 | MTL_ENABLE_DEBUG_INFO = NO; 383 | SDKROOT = iphoneos; 384 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 385 | TARGETED_DEVICE_FAMILY = "1,2"; 386 | VALIDATE_PRODUCT = YES; 387 | }; 388 | name = Release; 389 | }; 390 | 934E12221B1F858100441526 /* Debug */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 394 | CLANG_ENABLE_MODULES = YES; 395 | INFOPLIST_FILE = SwiftCoAPClientExample/Info.plist; 396 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 397 | PRODUCT_BUNDLE_IDENTIFIER = "kordylewski.$(PRODUCT_NAME:rfc1034identifier)"; 398 | PRODUCT_NAME = "$(TARGET_NAME)"; 399 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftCoAPClientExample/SwiftCoAPClientExample-Bridging-Header.h"; 400 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 401 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 402 | SWIFT_VERSION = 4.2; 403 | }; 404 | name = Debug; 405 | }; 406 | 934E12231B1F858100441526 /* Release */ = { 407 | isa = XCBuildConfiguration; 408 | buildSettings = { 409 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 410 | CLANG_ENABLE_MODULES = YES; 411 | INFOPLIST_FILE = SwiftCoAPClientExample/Info.plist; 412 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 413 | PRODUCT_BUNDLE_IDENTIFIER = "kordylewski.$(PRODUCT_NAME:rfc1034identifier)"; 414 | PRODUCT_NAME = "$(TARGET_NAME)"; 415 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftCoAPClientExample/SwiftCoAPClientExample-Bridging-Header.h"; 416 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 417 | SWIFT_VERSION = 4.2; 418 | }; 419 | name = Release; 420 | }; 421 | 934E12251B1F858100441526 /* Debug */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | BUNDLE_LOADER = "$(TEST_HOST)"; 425 | FRAMEWORK_SEARCH_PATHS = ( 426 | "$(SDKROOT)/Developer/Library/Frameworks", 427 | "$(inherited)", 428 | ); 429 | GCC_PREPROCESSOR_DEFINITIONS = ( 430 | "DEBUG=1", 431 | "$(inherited)", 432 | ); 433 | INFOPLIST_FILE = SwiftCoAPClientExampleTests/Info.plist; 434 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 435 | PRODUCT_BUNDLE_IDENTIFIER = "kordylewski.$(PRODUCT_NAME:rfc1034identifier)"; 436 | PRODUCT_NAME = "$(TARGET_NAME)"; 437 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 438 | SWIFT_VERSION = 4.2; 439 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftCoAPClientExample.app/SwiftCoAPClientExample"; 440 | }; 441 | name = Debug; 442 | }; 443 | 934E12261B1F858100441526 /* Release */ = { 444 | isa = XCBuildConfiguration; 445 | buildSettings = { 446 | BUNDLE_LOADER = "$(TEST_HOST)"; 447 | FRAMEWORK_SEARCH_PATHS = ( 448 | "$(SDKROOT)/Developer/Library/Frameworks", 449 | "$(inherited)", 450 | ); 451 | INFOPLIST_FILE = SwiftCoAPClientExampleTests/Info.plist; 452 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 453 | PRODUCT_BUNDLE_IDENTIFIER = "kordylewski.$(PRODUCT_NAME:rfc1034identifier)"; 454 | PRODUCT_NAME = "$(TARGET_NAME)"; 455 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 456 | SWIFT_VERSION = 4.2; 457 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftCoAPClientExample.app/SwiftCoAPClientExample"; 458 | }; 459 | name = Release; 460 | }; 461 | /* End XCBuildConfiguration section */ 462 | 463 | /* Begin XCConfigurationList section */ 464 | 934E11FD1B1F858100441526 /* Build configuration list for PBXProject "SwiftCoAPClientExample" */ = { 465 | isa = XCConfigurationList; 466 | buildConfigurations = ( 467 | 934E121F1B1F858100441526 /* Debug */, 468 | 934E12201B1F858100441526 /* Release */, 469 | ); 470 | defaultConfigurationIsVisible = 0; 471 | defaultConfigurationName = Release; 472 | }; 473 | 934E12211B1F858100441526 /* Build configuration list for PBXNativeTarget "SwiftCoAPClientExample" */ = { 474 | isa = XCConfigurationList; 475 | buildConfigurations = ( 476 | 934E12221B1F858100441526 /* Debug */, 477 | 934E12231B1F858100441526 /* Release */, 478 | ); 479 | defaultConfigurationIsVisible = 0; 480 | defaultConfigurationName = Release; 481 | }; 482 | 934E12241B1F858100441526 /* Build configuration list for PBXNativeTarget "SwiftCoAPClientExampleTests" */ = { 483 | isa = XCConfigurationList; 484 | buildConfigurations = ( 485 | 934E12251B1F858100441526 /* Debug */, 486 | 934E12261B1F858100441526 /* Release */, 487 | ); 488 | defaultConfigurationIsVisible = 0; 489 | defaultConfigurationName = Release; 490 | }; 491 | /* End XCConfigurationList section */ 492 | }; 493 | rootObject = 934E11FA1B1F858100441526 /* Project object */; 494 | } 495 | -------------------------------------------------------------------------------- /Example_Projects/SwiftCoAPServerExample/SwiftCoAPServerExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9316D79C1B2AE54400B0B70D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9316D79B1B2AE54400B0B70D /* AppDelegate.swift */; }; 11 | 9316D7A11B2AE54400B0B70D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9316D79F1B2AE54400B0B70D /* Main.storyboard */; }; 12 | 9316D7A31B2AE54400B0B70D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9316D7A21B2AE54400B0B70D /* Images.xcassets */; }; 13 | 9316D7A61B2AE54400B0B70D /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9316D7A41B2AE54400B0B70D /* LaunchScreen.xib */; }; 14 | 9316D7B21B2AE54400B0B70D /* SwiftCoAPServerExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9316D7B11B2AE54400B0B70D /* SwiftCoAPServerExampleTests.swift */; }; 15 | 9316D7C11B2AE89600B0B70D /* GCDAsyncUdpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 9316D7BE1B2AE89600B0B70D /* GCDAsyncUdpSocket.m */; }; 16 | 9316D7C21B2AE89600B0B70D /* SCMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9316D7BF1B2AE89600B0B70D /* SCMessage.swift */; }; 17 | 9316D7C31B2AE89600B0B70D /* SCServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9316D7C01B2AE89600B0B70D /* SCServer.swift */; }; 18 | 9316D7C51B2AE8C600B0B70D /* ExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9316D7C41B2AE8C600B0B70D /* ExampleViewController.swift */; }; 19 | 9316D7CA1B2C40C000B0B70D /* DefaultTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9316D7C91B2C40C000B0B70D /* DefaultTableViewCell.swift */; }; 20 | 936CD06E1B3963580041837F /* TimeResourceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 936CD06D1B3963580041837F /* TimeResourceModel.swift */; }; 21 | 936CD0701B3963A40041837F /* SeparateResourceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 936CD06F1B3963A40041837F /* SeparateResourceModel.swift */; }; 22 | 93CC553B1B3C92150001DAAF /* TestResourceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CC553A1B3C92150001DAAF /* TestResourceModel.swift */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | 9316D7AC1B2AE54400B0B70D /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 9316D78E1B2AE54400B0B70D /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = 9316D7951B2AE54400B0B70D; 31 | remoteInfo = SwiftCoAPServerExample; 32 | }; 33 | /* End PBXContainerItemProxy section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 9316D7961B2AE54400B0B70D /* SwiftCoAPServerExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftCoAPServerExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 9316D79A1B2AE54400B0B70D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 9316D79B1B2AE54400B0B70D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 39 | 9316D7A01B2AE54400B0B70D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 40 | 9316D7A21B2AE54400B0B70D /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 41 | 9316D7A51B2AE54400B0B70D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 42 | 9316D7AB1B2AE54400B0B70D /* SwiftCoAPServerExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftCoAPServerExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 9316D7B01B2AE54400B0B70D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 9316D7B11B2AE54400B0B70D /* SwiftCoAPServerExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftCoAPServerExampleTests.swift; sourceTree = ""; }; 45 | 9316D7BC1B2AE89500B0B70D /* SwiftCoAPServerExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftCoAPServerExample-Bridging-Header.h"; sourceTree = ""; }; 46 | 9316D7BD1B2AE89600B0B70D /* GCDAsyncUdpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDAsyncUdpSocket.h; sourceTree = ""; }; 47 | 9316D7BE1B2AE89600B0B70D /* GCDAsyncUdpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDAsyncUdpSocket.m; sourceTree = ""; }; 48 | 9316D7BF1B2AE89600B0B70D /* SCMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SCMessage.swift; sourceTree = ""; }; 49 | 9316D7C01B2AE89600B0B70D /* SCServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SCServer.swift; sourceTree = ""; }; 50 | 9316D7C41B2AE8C600B0B70D /* ExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleViewController.swift; sourceTree = ""; }; 51 | 9316D7C91B2C40C000B0B70D /* DefaultTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultTableViewCell.swift; sourceTree = ""; }; 52 | 936CD06D1B3963580041837F /* TimeResourceModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeResourceModel.swift; sourceTree = ""; }; 53 | 936CD06F1B3963A40041837F /* SeparateResourceModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SeparateResourceModel.swift; sourceTree = ""; }; 54 | 93CC553A1B3C92150001DAAF /* TestResourceModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestResourceModel.swift; sourceTree = ""; }; 55 | /* End PBXFileReference section */ 56 | 57 | /* Begin PBXFrameworksBuildPhase section */ 58 | 9316D7931B2AE54400B0B70D /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | 9316D7A81B2AE54400B0B70D /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 9316D78D1B2AE54400B0B70D = { 76 | isa = PBXGroup; 77 | children = ( 78 | 9316D7981B2AE54400B0B70D /* SwiftCoAPServerExample */, 79 | 9316D7AE1B2AE54400B0B70D /* SwiftCoAPServerExampleTests */, 80 | 9316D7971B2AE54400B0B70D /* Products */, 81 | ); 82 | sourceTree = ""; 83 | }; 84 | 9316D7971B2AE54400B0B70D /* Products */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 9316D7961B2AE54400B0B70D /* SwiftCoAPServerExample.app */, 88 | 9316D7AB1B2AE54400B0B70D /* SwiftCoAPServerExampleTests.xctest */, 89 | ); 90 | name = Products; 91 | sourceTree = ""; 92 | }; 93 | 9316D7981B2AE54400B0B70D /* SwiftCoAPServerExample */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 9316D7BB1B2AE59300B0B70D /* SwiftCoAPLibrary */, 97 | 9316D79B1B2AE54400B0B70D /* AppDelegate.swift */, 98 | 9316D7C41B2AE8C600B0B70D /* ExampleViewController.swift */, 99 | 9316D7C91B2C40C000B0B70D /* DefaultTableViewCell.swift */, 100 | 9316D7C61B2AE8E600B0B70D /* CoAPServer Resource Models */, 101 | 9316D79F1B2AE54400B0B70D /* Main.storyboard */, 102 | 9316D7A21B2AE54400B0B70D /* Images.xcassets */, 103 | 9316D7A41B2AE54400B0B70D /* LaunchScreen.xib */, 104 | 9316D7991B2AE54400B0B70D /* Supporting Files */, 105 | ); 106 | path = SwiftCoAPServerExample; 107 | sourceTree = ""; 108 | }; 109 | 9316D7991B2AE54400B0B70D /* Supporting Files */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 9316D79A1B2AE54400B0B70D /* Info.plist */, 113 | ); 114 | name = "Supporting Files"; 115 | sourceTree = ""; 116 | }; 117 | 9316D7AE1B2AE54400B0B70D /* SwiftCoAPServerExampleTests */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 9316D7B11B2AE54400B0B70D /* SwiftCoAPServerExampleTests.swift */, 121 | 9316D7AF1B2AE54400B0B70D /* Supporting Files */, 122 | ); 123 | path = SwiftCoAPServerExampleTests; 124 | sourceTree = ""; 125 | }; 126 | 9316D7AF1B2AE54400B0B70D /* Supporting Files */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 9316D7B01B2AE54400B0B70D /* Info.plist */, 130 | ); 131 | name = "Supporting Files"; 132 | sourceTree = ""; 133 | }; 134 | 9316D7BB1B2AE59300B0B70D /* SwiftCoAPLibrary */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 9316D7BD1B2AE89600B0B70D /* GCDAsyncUdpSocket.h */, 138 | 9316D7BE1B2AE89600B0B70D /* GCDAsyncUdpSocket.m */, 139 | 9316D7BF1B2AE89600B0B70D /* SCMessage.swift */, 140 | 9316D7C01B2AE89600B0B70D /* SCServer.swift */, 141 | 9316D7BC1B2AE89500B0B70D /* SwiftCoAPServerExample-Bridging-Header.h */, 142 | ); 143 | name = SwiftCoAPLibrary; 144 | sourceTree = ""; 145 | }; 146 | 9316D7C61B2AE8E600B0B70D /* CoAPServer Resource Models */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 936CD06D1B3963580041837F /* TimeResourceModel.swift */, 150 | 936CD06F1B3963A40041837F /* SeparateResourceModel.swift */, 151 | 93CC553A1B3C92150001DAAF /* TestResourceModel.swift */, 152 | ); 153 | name = "CoAPServer Resource Models"; 154 | sourceTree = ""; 155 | }; 156 | /* End PBXGroup section */ 157 | 158 | /* Begin PBXNativeTarget section */ 159 | 9316D7951B2AE54400B0B70D /* SwiftCoAPServerExample */ = { 160 | isa = PBXNativeTarget; 161 | buildConfigurationList = 9316D7B51B2AE54400B0B70D /* Build configuration list for PBXNativeTarget "SwiftCoAPServerExample" */; 162 | buildPhases = ( 163 | 9316D7921B2AE54400B0B70D /* Sources */, 164 | 9316D7931B2AE54400B0B70D /* Frameworks */, 165 | 9316D7941B2AE54400B0B70D /* Resources */, 166 | ); 167 | buildRules = ( 168 | ); 169 | dependencies = ( 170 | ); 171 | name = SwiftCoAPServerExample; 172 | productName = SwiftCoAPServerExample; 173 | productReference = 9316D7961B2AE54400B0B70D /* SwiftCoAPServerExample.app */; 174 | productType = "com.apple.product-type.application"; 175 | }; 176 | 9316D7AA1B2AE54400B0B70D /* SwiftCoAPServerExampleTests */ = { 177 | isa = PBXNativeTarget; 178 | buildConfigurationList = 9316D7B81B2AE54400B0B70D /* Build configuration list for PBXNativeTarget "SwiftCoAPServerExampleTests" */; 179 | buildPhases = ( 180 | 9316D7A71B2AE54400B0B70D /* Sources */, 181 | 9316D7A81B2AE54400B0B70D /* Frameworks */, 182 | 9316D7A91B2AE54400B0B70D /* Resources */, 183 | ); 184 | buildRules = ( 185 | ); 186 | dependencies = ( 187 | 9316D7AD1B2AE54400B0B70D /* PBXTargetDependency */, 188 | ); 189 | name = SwiftCoAPServerExampleTests; 190 | productName = SwiftCoAPServerExampleTests; 191 | productReference = 9316D7AB1B2AE54400B0B70D /* SwiftCoAPServerExampleTests.xctest */; 192 | productType = "com.apple.product-type.bundle.unit-test"; 193 | }; 194 | /* End PBXNativeTarget section */ 195 | 196 | /* Begin PBXProject section */ 197 | 9316D78E1B2AE54400B0B70D /* Project object */ = { 198 | isa = PBXProject; 199 | attributes = { 200 | LastSwiftUpdateCheck = 0700; 201 | LastUpgradeCheck = 1010; 202 | ORGANIZATIONNAME = "Wojtek Kordylewski"; 203 | TargetAttributes = { 204 | 9316D7951B2AE54400B0B70D = { 205 | CreatedOnToolsVersion = 6.3.2; 206 | LastSwiftMigration = 1010; 207 | }; 208 | 9316D7AA1B2AE54400B0B70D = { 209 | CreatedOnToolsVersion = 6.3.2; 210 | LastSwiftMigration = 1010; 211 | TestTargetID = 9316D7951B2AE54400B0B70D; 212 | }; 213 | }; 214 | }; 215 | buildConfigurationList = 9316D7911B2AE54400B0B70D /* Build configuration list for PBXProject "SwiftCoAPServerExample" */; 216 | compatibilityVersion = "Xcode 3.2"; 217 | developmentRegion = English; 218 | hasScannedForEncodings = 0; 219 | knownRegions = ( 220 | en, 221 | Base, 222 | ); 223 | mainGroup = 9316D78D1B2AE54400B0B70D; 224 | productRefGroup = 9316D7971B2AE54400B0B70D /* Products */; 225 | projectDirPath = ""; 226 | projectRoot = ""; 227 | targets = ( 228 | 9316D7951B2AE54400B0B70D /* SwiftCoAPServerExample */, 229 | 9316D7AA1B2AE54400B0B70D /* SwiftCoAPServerExampleTests */, 230 | ); 231 | }; 232 | /* End PBXProject section */ 233 | 234 | /* Begin PBXResourcesBuildPhase section */ 235 | 9316D7941B2AE54400B0B70D /* Resources */ = { 236 | isa = PBXResourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | 9316D7A11B2AE54400B0B70D /* Main.storyboard in Resources */, 240 | 9316D7A61B2AE54400B0B70D /* LaunchScreen.xib in Resources */, 241 | 9316D7A31B2AE54400B0B70D /* Images.xcassets in Resources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | 9316D7A91B2AE54400B0B70D /* Resources */ = { 246 | isa = PBXResourcesBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | /* End PBXResourcesBuildPhase section */ 253 | 254 | /* Begin PBXSourcesBuildPhase section */ 255 | 9316D7921B2AE54400B0B70D /* Sources */ = { 256 | isa = PBXSourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | 9316D7C31B2AE89600B0B70D /* SCServer.swift in Sources */, 260 | 936CD0701B3963A40041837F /* SeparateResourceModel.swift in Sources */, 261 | 936CD06E1B3963580041837F /* TimeResourceModel.swift in Sources */, 262 | 9316D7C21B2AE89600B0B70D /* SCMessage.swift in Sources */, 263 | 9316D7C11B2AE89600B0B70D /* GCDAsyncUdpSocket.m in Sources */, 264 | 9316D79C1B2AE54400B0B70D /* AppDelegate.swift in Sources */, 265 | 9316D7C51B2AE8C600B0B70D /* ExampleViewController.swift in Sources */, 266 | 9316D7CA1B2C40C000B0B70D /* DefaultTableViewCell.swift in Sources */, 267 | 93CC553B1B3C92150001DAAF /* TestResourceModel.swift in Sources */, 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | }; 271 | 9316D7A71B2AE54400B0B70D /* Sources */ = { 272 | isa = PBXSourcesBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | 9316D7B21B2AE54400B0B70D /* SwiftCoAPServerExampleTests.swift in Sources */, 276 | ); 277 | runOnlyForDeploymentPostprocessing = 0; 278 | }; 279 | /* End PBXSourcesBuildPhase section */ 280 | 281 | /* Begin PBXTargetDependency section */ 282 | 9316D7AD1B2AE54400B0B70D /* PBXTargetDependency */ = { 283 | isa = PBXTargetDependency; 284 | target = 9316D7951B2AE54400B0B70D /* SwiftCoAPServerExample */; 285 | targetProxy = 9316D7AC1B2AE54400B0B70D /* PBXContainerItemProxy */; 286 | }; 287 | /* End PBXTargetDependency section */ 288 | 289 | /* Begin PBXVariantGroup section */ 290 | 9316D79F1B2AE54400B0B70D /* Main.storyboard */ = { 291 | isa = PBXVariantGroup; 292 | children = ( 293 | 9316D7A01B2AE54400B0B70D /* Base */, 294 | ); 295 | name = Main.storyboard; 296 | sourceTree = ""; 297 | }; 298 | 9316D7A41B2AE54400B0B70D /* LaunchScreen.xib */ = { 299 | isa = PBXVariantGroup; 300 | children = ( 301 | 9316D7A51B2AE54400B0B70D /* Base */, 302 | ); 303 | name = LaunchScreen.xib; 304 | sourceTree = ""; 305 | }; 306 | /* End PBXVariantGroup section */ 307 | 308 | /* Begin XCBuildConfiguration section */ 309 | 9316D7B31B2AE54400B0B70D /* Debug */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 314 | CLANG_CXX_LIBRARY = "libc++"; 315 | CLANG_ENABLE_MODULES = YES; 316 | CLANG_ENABLE_OBJC_ARC = YES; 317 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_COMMA = YES; 320 | CLANG_WARN_CONSTANT_CONVERSION = YES; 321 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 323 | CLANG_WARN_EMPTY_BODY = YES; 324 | CLANG_WARN_ENUM_CONVERSION = YES; 325 | CLANG_WARN_INFINITE_RECURSION = YES; 326 | CLANG_WARN_INT_CONVERSION = YES; 327 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 328 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 329 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 330 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 331 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 332 | CLANG_WARN_STRICT_PROTOTYPES = YES; 333 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 334 | CLANG_WARN_UNREACHABLE_CODE = YES; 335 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 336 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 337 | COPY_PHASE_STRIP = NO; 338 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 339 | ENABLE_STRICT_OBJC_MSGSEND = YES; 340 | ENABLE_TESTABILITY = YES; 341 | GCC_C_LANGUAGE_STANDARD = gnu99; 342 | GCC_DYNAMIC_NO_PIC = NO; 343 | GCC_NO_COMMON_BLOCKS = YES; 344 | GCC_OPTIMIZATION_LEVEL = 0; 345 | GCC_PREPROCESSOR_DEFINITIONS = ( 346 | "DEBUG=1", 347 | "$(inherited)", 348 | ); 349 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 350 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 351 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 352 | GCC_WARN_UNDECLARED_SELECTOR = YES; 353 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 354 | GCC_WARN_UNUSED_FUNCTION = YES; 355 | GCC_WARN_UNUSED_VARIABLE = YES; 356 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 357 | MTL_ENABLE_DEBUG_INFO = YES; 358 | ONLY_ACTIVE_ARCH = YES; 359 | SDKROOT = iphoneos; 360 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 361 | TARGETED_DEVICE_FAMILY = "1,2"; 362 | }; 363 | name = Debug; 364 | }; 365 | 9316D7B41B2AE54400B0B70D /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ALWAYS_SEARCH_USER_PATHS = NO; 369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 370 | CLANG_CXX_LIBRARY = "libc++"; 371 | CLANG_ENABLE_MODULES = YES; 372 | CLANG_ENABLE_OBJC_ARC = YES; 373 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 374 | CLANG_WARN_BOOL_CONVERSION = YES; 375 | CLANG_WARN_COMMA = YES; 376 | CLANG_WARN_CONSTANT_CONVERSION = YES; 377 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 378 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 379 | CLANG_WARN_EMPTY_BODY = YES; 380 | CLANG_WARN_ENUM_CONVERSION = YES; 381 | CLANG_WARN_INFINITE_RECURSION = YES; 382 | CLANG_WARN_INT_CONVERSION = YES; 383 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 384 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 385 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 387 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 388 | CLANG_WARN_STRICT_PROTOTYPES = YES; 389 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 390 | CLANG_WARN_UNREACHABLE_CODE = YES; 391 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 392 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 393 | COPY_PHASE_STRIP = NO; 394 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 395 | ENABLE_NS_ASSERTIONS = NO; 396 | ENABLE_STRICT_OBJC_MSGSEND = YES; 397 | GCC_C_LANGUAGE_STANDARD = gnu99; 398 | GCC_NO_COMMON_BLOCKS = YES; 399 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 400 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 401 | GCC_WARN_UNDECLARED_SELECTOR = YES; 402 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 403 | GCC_WARN_UNUSED_FUNCTION = YES; 404 | GCC_WARN_UNUSED_VARIABLE = YES; 405 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 406 | MTL_ENABLE_DEBUG_INFO = NO; 407 | SDKROOT = iphoneos; 408 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 409 | TARGETED_DEVICE_FAMILY = "1,2"; 410 | VALIDATE_PRODUCT = YES; 411 | }; 412 | name = Release; 413 | }; 414 | 9316D7B61B2AE54400B0B70D /* Debug */ = { 415 | isa = XCBuildConfiguration; 416 | buildSettings = { 417 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 418 | CLANG_ENABLE_MODULES = YES; 419 | DEVELOPMENT_TEAM = ""; 420 | INFOPLIST_FILE = SwiftCoAPServerExample/Info.plist; 421 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 422 | PRODUCT_BUNDLE_IDENTIFIER = "kordylewski.$(PRODUCT_NAME:rfc1034identifier)"; 423 | PRODUCT_NAME = "$(TARGET_NAME)"; 424 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftCoAPServerExample/SwiftCoAPServerExample-Bridging-Header.h"; 425 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 426 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 427 | SWIFT_VERSION = 4.2; 428 | }; 429 | name = Debug; 430 | }; 431 | 9316D7B71B2AE54400B0B70D /* Release */ = { 432 | isa = XCBuildConfiguration; 433 | buildSettings = { 434 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 435 | CLANG_ENABLE_MODULES = YES; 436 | DEVELOPMENT_TEAM = ""; 437 | INFOPLIST_FILE = SwiftCoAPServerExample/Info.plist; 438 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 439 | PRODUCT_BUNDLE_IDENTIFIER = "kordylewski.$(PRODUCT_NAME:rfc1034identifier)"; 440 | PRODUCT_NAME = "$(TARGET_NAME)"; 441 | SWIFT_OBJC_BRIDGING_HEADER = "SwiftCoAPServerExample/SwiftCoAPServerExample-Bridging-Header.h"; 442 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 443 | SWIFT_VERSION = 4.2; 444 | }; 445 | name = Release; 446 | }; 447 | 9316D7B91B2AE54400B0B70D /* Debug */ = { 448 | isa = XCBuildConfiguration; 449 | buildSettings = { 450 | BUNDLE_LOADER = "$(TEST_HOST)"; 451 | DEVELOPMENT_TEAM = ""; 452 | FRAMEWORK_SEARCH_PATHS = ( 453 | "$(SDKROOT)/Developer/Library/Frameworks", 454 | "$(inherited)", 455 | ); 456 | GCC_PREPROCESSOR_DEFINITIONS = ( 457 | "DEBUG=1", 458 | "$(inherited)", 459 | ); 460 | INFOPLIST_FILE = SwiftCoAPServerExampleTests/Info.plist; 461 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 462 | PRODUCT_BUNDLE_IDENTIFIER = "kordylewski.$(PRODUCT_NAME:rfc1034identifier)"; 463 | PRODUCT_NAME = "$(TARGET_NAME)"; 464 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 465 | SWIFT_VERSION = 4.2; 466 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftCoAPServerExample.app/SwiftCoAPServerExample"; 467 | }; 468 | name = Debug; 469 | }; 470 | 9316D7BA1B2AE54400B0B70D /* Release */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | BUNDLE_LOADER = "$(TEST_HOST)"; 474 | DEVELOPMENT_TEAM = ""; 475 | FRAMEWORK_SEARCH_PATHS = ( 476 | "$(SDKROOT)/Developer/Library/Frameworks", 477 | "$(inherited)", 478 | ); 479 | INFOPLIST_FILE = SwiftCoAPServerExampleTests/Info.plist; 480 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 481 | PRODUCT_BUNDLE_IDENTIFIER = "kordylewski.$(PRODUCT_NAME:rfc1034identifier)"; 482 | PRODUCT_NAME = "$(TARGET_NAME)"; 483 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 484 | SWIFT_VERSION = 4.2; 485 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftCoAPServerExample.app/SwiftCoAPServerExample"; 486 | }; 487 | name = Release; 488 | }; 489 | /* End XCBuildConfiguration section */ 490 | 491 | /* Begin XCConfigurationList section */ 492 | 9316D7911B2AE54400B0B70D /* Build configuration list for PBXProject "SwiftCoAPServerExample" */ = { 493 | isa = XCConfigurationList; 494 | buildConfigurations = ( 495 | 9316D7B31B2AE54400B0B70D /* Debug */, 496 | 9316D7B41B2AE54400B0B70D /* Release */, 497 | ); 498 | defaultConfigurationIsVisible = 0; 499 | defaultConfigurationName = Release; 500 | }; 501 | 9316D7B51B2AE54400B0B70D /* Build configuration list for PBXNativeTarget "SwiftCoAPServerExample" */ = { 502 | isa = XCConfigurationList; 503 | buildConfigurations = ( 504 | 9316D7B61B2AE54400B0B70D /* Debug */, 505 | 9316D7B71B2AE54400B0B70D /* Release */, 506 | ); 507 | defaultConfigurationIsVisible = 0; 508 | defaultConfigurationName = Release; 509 | }; 510 | 9316D7B81B2AE54400B0B70D /* Build configuration list for PBXNativeTarget "SwiftCoAPServerExampleTests" */ = { 511 | isa = XCConfigurationList; 512 | buildConfigurations = ( 513 | 9316D7B91B2AE54400B0B70D /* Debug */, 514 | 9316D7BA1B2AE54400B0B70D /* Release */, 515 | ); 516 | defaultConfigurationIsVisible = 0; 517 | defaultConfigurationName = Release; 518 | }; 519 | /* End XCConfigurationList section */ 520 | }; 521 | rootObject = 9316D78E1B2AE54400B0B70D /* Project object */; 522 | } 523 | -------------------------------------------------------------------------------- /SwiftCoAP_Library/SCServer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SCServer.swift 3 | // SwiftCoAP 4 | // 5 | // Created by Wojtek Kordylewski on 03.06.15. 6 | // Copyright (c) 2015 Wojtek Kordylewski. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | //MARK: 13 | //MARK: SC Server Delegate Protocol implementation 14 | 15 | protocol SCServerDelegate: class { 16 | 17 | //Tells the delegate that an error occured during or before transmission (refer to the "SCServerErrorCode" Enum) 18 | func swiftCoapServer(_ server: SCServer, didFailWithError error: NSError) 19 | 20 | //Tells the delegate that a request on a particular resource was successfully handled and a response was or will be provided with the given response code 21 | func swiftCoapServer(_ server: SCServer, didHandleRequestWithCode requestCode: SCCodeValue, forResource resource: SCResourceModel, withResponseCode responseCode: SCCodeValue) 22 | 23 | //Tells the delegate that a recently received request with a uripath was rejected with a particular reponse (error) code (e.g. Method Not Allowed, Not Found, etc.) 24 | func swiftCoapServer(_ server: SCServer, didRejectRequestWithCode requestCode: SCCodeValue, forPath path: String, withResponseCode responseCode: SCCodeValue) 25 | 26 | //Tells the delegate that a separate response was processed 27 | func swiftCoapServer(_ server: SCServer, didSendSeparateResponseMessage: SCMessage, number: Int) 28 | 29 | //Tells the delegate that all registered observers for the particular resource will be notified due to a change of its data representation 30 | func swiftCoapServer(_ server: SCServer, willUpdatedObserversForResource resource: SCResourceModel) 31 | } 32 | 33 | 34 | //MARK: 35 | //MARK: SC Server Error Code Enumeration 36 | 37 | enum SCServerErrorCode: Int { 38 | case transportLayerError, receivedInvalidMessageError, noResponseExpectedError 39 | 40 | func descriptionString() -> String { 41 | switch self { 42 | case .transportLayerError: 43 | return "Failed to send data via the given Transport Layer" 44 | case .receivedInvalidMessageError: 45 | return "Data received was not a valid CoAP Message" 46 | case .noResponseExpectedError: 47 | return "The recipient does not respond" 48 | } 49 | } 50 | } 51 | 52 | 53 | //MARK: 54 | //MARK: SC Server IMPLEMENTATION 55 | 56 | class SCServer: NSObject { 57 | fileprivate class SCAddressWrapper: NSObject { 58 | let hostname: String 59 | let port: UInt16 60 | override var hash: Int { 61 | get { return hostname.hashValue &+ port.hashValue } 62 | } 63 | 64 | init(hostname: String, port: UInt16) { 65 | self.hostname = hostname 66 | self.port = port 67 | } 68 | 69 | override func isEqual(_ object: Any?) -> Bool { 70 | if let other = object as? SCAddressWrapper, other.hostname == self.hostname && other.port == self.port { 71 | return true 72 | } 73 | return false 74 | } 75 | } 76 | 77 | 78 | //MARK: Properties 79 | 80 | 81 | //INTERNAL PROPERTIES (allowed to modify) 82 | 83 | weak var delegate: SCServerDelegate? 84 | var autoBlock2SZX: UInt? = 2 { didSet { if let newValue = autoBlock2SZX { autoBlock2SZX = min(6, newValue) } } } //If not nil, Block2 transfer will be used automatically when the payload size exceeds the value 2^(autoBlock2SZX + 4). Valid Values: 0-6. 85 | var autoWellKnownCore = true //If set to true, the server will automatically provide responses for the resource "well-known/core" with its current resources. 86 | lazy var resources = [SCResourceModel]() 87 | 88 | //PRIVATE PROPERTIES 89 | fileprivate var transportLayerObject: SCCoAPTransportLayerProtocol! 90 | fileprivate lazy var pendingMessagesForEndpoints = [SCAddressWrapper : [(SCMessage, Timer?)]]() 91 | fileprivate lazy var registeredObserverForResource = [SCResourceModel : [(UInt64, String, UInt16, UInt, UInt?)]]() //Token, hostname, port, SequenceNumber, PrefferedBlock2SZX 92 | fileprivate lazy var block1UploadsForEndpoints = [SCAddressWrapper : [(SCResourceModel, UInt, Data?)]]() 93 | 94 | 95 | //MARK: Internal Methods (allowed to use) 96 | 97 | 98 | //Initializer (failable): Starts server on initialization. 99 | 100 | init?(delegate: SCServerDelegate?, transportLayerObject: SCCoAPTransportLayerProtocol = SCCoAPUDPTransportLayer(port: 5683)) { 101 | self.delegate = delegate 102 | super.init() 103 | self.transportLayerObject = transportLayerObject 104 | do { 105 | try start() 106 | self.transportLayerObject.transportLayerDelegate = self 107 | } 108 | catch { 109 | return nil 110 | } 111 | } 112 | 113 | //Start server manually, with the given port 114 | 115 | func start() throws { 116 | try self.transportLayerObject?.startListening() 117 | } 118 | 119 | 120 | //Close UDP socket and server ativity 121 | 122 | func close() { 123 | self.transportLayerObject?.closeTransmission() 124 | } 125 | 126 | 127 | //Reset Context Information 128 | 129 | func reset() { 130 | pendingMessagesForEndpoints = [:] 131 | registeredObserverForResource = [:] 132 | block1UploadsForEndpoints = [:] 133 | resources = [] 134 | } 135 | 136 | 137 | //Call this method when your resource is ready to process a separate response. The concerned resource must return true for the method `willHandleDataAsynchronouslyForGet(...)`. It is necessary to pass the original message and the resource (both received in `willHandleDataAsynchronouslyForGet`) so that the server is able to retrieve the current context. Additionay, you have to pass the typical "values" tuple which form the response (as described in SCMessage -> SCResourceModel) 138 | 139 | func didCompleteAsynchronousRequestForOriginalMessage(_ message: SCMessage, resource: SCResourceModel, values: (statusCode: SCCodeValue, payloadData: Data?, contentFormat: SCContentFormat?, locationUri: String?)) { 140 | let type: SCType = message.type == .confirmable ? .confirmable : .nonConfirmable 141 | if let separateMessage = createMessageForValues((values.statusCode, values.payloadData, values.contentFormat, values.locationUri), withType: type, relatedMessage: message, requestedResource: resource) { 142 | separateMessage.messageId = UInt16(arc4random_uniform(0xFFFF) &+ 1) 143 | setupReliableTransmissionOfMessage(separateMessage, forResource: resource) 144 | } 145 | } 146 | 147 | 148 | //Call this method when the given resource has updated its data representation in order to notify all registered users (and has "observable" set to true) 149 | 150 | func updateRegisteredObserversForResource(_ resource: SCResourceModel) { 151 | if var valueArray = registeredObserverForResource[resource] { 152 | for i in 0 ..< valueArray.count { 153 | let (token, hostname, port, sequenceNumber, prefferredBlock2SZX) = valueArray[i] 154 | let notification = SCMessage(code: SCCodeValue(classValue: 2, detailValue: 05)!, type: .confirmable, payload: resource.dataRepresentation) 155 | notification.token = token 156 | notification.messageId = UInt16(arc4random_uniform(0xFFFF) &+ 1) 157 | notification.hostName = hostname 158 | notification.port = port 159 | let newSequenceNumber = (sequenceNumber + 1) % UInt(pow(2.0, 24)) 160 | var byteArray = newSequenceNumber.toByteArray() 161 | notification.addOption(SCOption.observe.rawValue, data: Data(bytes: &byteArray, count: byteArray.count)) 162 | handleBlock2ServerRequirementsForMessage(notification, preferredBlockSZX: prefferredBlock2SZX) 163 | valueArray[i] = (token, hostname, port, newSequenceNumber, prefferredBlock2SZX) 164 | registeredObserverForResource[resource] = valueArray 165 | setupReliableTransmissionOfMessage(notification, forResource: resource) 166 | } 167 | } 168 | delegate?.swiftCoapServer(self, willUpdatedObserversForResource: resource) 169 | } 170 | 171 | 172 | // MARK: Private Methods 173 | 174 | fileprivate func setupReliableTransmissionOfMessage(_ message: SCMessage, forResource resource: SCResourceModel) { 175 | if let host = message.hostName, let port = message.port { 176 | var timer: Timer! 177 | if message.type == .confirmable { 178 | message.resourceForConfirmableResponse = resource 179 | let timeout = SCMessage.kAckTimeout * 2.0 * (SCMessage.kAckRandomFactor - ((Double(arc4random()) / Double(UINT32_MAX)).truncatingRemainder(dividingBy: 0.5))); 180 | timer = Timer(timeInterval: timeout, target: self, selector: #selector(SCServer.handleRetransmission(_:)), userInfo: ["retransmissionCount" : 1, "totalTime" : timeout, "message" : message, "resource" : resource], repeats: false) 181 | RunLoop.current.add(timer, forMode: RunLoop.Mode.common) 182 | } 183 | sendMessage(message) 184 | let addressWrapper = SCAddressWrapper(hostname: host, port: port) 185 | if var contextArray = pendingMessagesForEndpoints[addressWrapper] { 186 | let newTuple: (SCMessage, Timer?) = (message, timer) 187 | contextArray += [newTuple] 188 | pendingMessagesForEndpoints[addressWrapper] = contextArray 189 | } 190 | else { 191 | pendingMessagesForEndpoints[addressWrapper] = [(message, timer)] 192 | } 193 | } 194 | } 195 | 196 | fileprivate func sendMessageWithType(_ type: SCType, code: SCCodeValue, payload: Data?, messageId: UInt16, hostname: String, port: UInt16, token: UInt64 = 0, options: [Int: [Data]]! = nil) { 197 | let emptyMessage = SCMessage(code: code, type: type, payload: payload) 198 | emptyMessage.messageId = messageId 199 | emptyMessage.token = token 200 | emptyMessage.hostName = hostname 201 | emptyMessage.port = port 202 | if let opt = options { 203 | emptyMessage.options = opt 204 | } 205 | sendMessage(emptyMessage) 206 | } 207 | 208 | fileprivate func sendMessage(_ message: SCMessage) { 209 | if let messageData = message.toData(), let host = message.hostName, let port = message.port { 210 | do { 211 | try transportLayerObject.sendCoAPData(messageData, toHost: host, port: port) 212 | } 213 | catch SCCoAPTransportLayerError.sendError(let errorDescription) { 214 | notifyDelegateWithTransportLayerErrorDescription(errorDescription) 215 | } 216 | catch SCCoAPTransportLayerError.setupError(let errorDescription) { 217 | notifyDelegateWithTransportLayerErrorDescription(errorDescription) 218 | } 219 | catch {} 220 | } 221 | } 222 | 223 | 224 | //Actually PRIVATE! Do not call from outside. Has to be internally visible as NSTimer won't find it otherwise 225 | 226 | @objc func handleRetransmission(_ timer: Timer) { 227 | guard let userInfoDict = timer.userInfo as? [String: Any] else { return } 228 | 229 | let retransmissionCount = userInfoDict["retransmissionCount"] as! Int 230 | let totalTime = userInfoDict["totalTime"] as! Double 231 | let message = userInfoDict["message"] as! SCMessage 232 | let resource = userInfoDict["resource"] as! SCResourceModel 233 | sendMessage(message) 234 | delegate?.swiftCoapServer(self, didSendSeparateResponseMessage: message, number: retransmissionCount) 235 | 236 | if let hostname = message.hostName, let port = message.port { 237 | let wrapper = SCAddressWrapper(hostname: hostname, port: port) 238 | if var contextArray = pendingMessagesForEndpoints[wrapper] { 239 | let nextTimer: Timer 240 | if retransmissionCount < SCMessage.kMaxRetransmit { 241 | let timeout = SCMessage.kAckTimeout * pow(2.0, Double(retransmissionCount)) * (SCMessage.kAckRandomFactor - ((Double(arc4random()) / Double(UINT32_MAX)).truncatingRemainder(dividingBy: 0.5))); 242 | nextTimer = Timer(timeInterval: timeout, target: self, selector: #selector(SCServer.handleRetransmission(_:)), userInfo: ["retransmissionCount" : retransmissionCount + 1, "totalTime" : totalTime + timeout, "message" : message, "resource" : resource], repeats: false) 243 | } 244 | else { 245 | nextTimer = Timer(timeInterval: SCMessage.kMaxTransmitWait - totalTime, target: self, selector: #selector(SCServer.notifyNoResponseExpected(_:)), userInfo: ["message" : message, "resource" : resource], repeats: false) 246 | } 247 | RunLoop.current.add(nextTimer, forMode: RunLoop.Mode.common) 248 | 249 | //Update context 250 | for i in 0 ..< contextArray.count { 251 | let tuple = contextArray[i] 252 | if tuple.0 == message { 253 | contextArray[i] = (tuple.0, nextTimer) 254 | pendingMessagesForEndpoints[wrapper] = contextArray 255 | break 256 | } 257 | } 258 | } 259 | } 260 | } 261 | 262 | 263 | //Actually PRIVATE! Do not call from outside. Has to be internally visible as NSTimer won't find it otherwise 264 | 265 | @objc func notifyNoResponseExpected(_ timer: Timer) { 266 | guard let userInfoDict = timer.userInfo as? [String: Any] else { return } 267 | 268 | let message = userInfoDict["message"] as! SCMessage 269 | let resource = userInfoDict["resource"] as! SCResourceModel 270 | 271 | removeContextForMessage(message) 272 | notifyDelegateWithErrorCode(.noResponseExpectedError) 273 | 274 | if message.options[SCOption.observe.rawValue] != nil, let hostname = message.hostName, let port = message.port { 275 | deregisterObserveForResource(resource, hostname: hostname, port: port) 276 | } 277 | } 278 | 279 | fileprivate func notifyDelegateWithTransportLayerErrorDescription(_ errorDescription: String) { 280 | delegate?.swiftCoapServer(self, didFailWithError: NSError(domain: SCMessage.kCoapErrorDomain, code: 0, userInfo: [NSLocalizedDescriptionKey : errorDescription])) 281 | } 282 | 283 | fileprivate func removeContextForMessage(_ message: SCMessage) { 284 | if let hostname = message.hostName, let port = message.port { 285 | let wrapper = SCAddressWrapper(hostname: hostname, port: port) 286 | if var contextArray = pendingMessagesForEndpoints[wrapper] { 287 | func removeFromContextAtIndex(_ index: Int) { 288 | contextArray.remove(at: index) 289 | if contextArray.count > 0 { 290 | pendingMessagesForEndpoints[wrapper] = contextArray 291 | } 292 | else { 293 | pendingMessagesForEndpoints.removeValue(forKey: wrapper) 294 | } 295 | } 296 | 297 | for i in 0 ..< contextArray.count { 298 | let tuple = contextArray[i] 299 | if tuple.0.messageId == message.messageId { 300 | if let oldTimer = tuple.1 { 301 | oldTimer.invalidate() 302 | } 303 | if message.type == .reset && tuple.0.options[SCOption.observe.rawValue] != nil, let resource = tuple.0.resourceForConfirmableResponse { 304 | deregisterObserveForResource(resource, hostname: hostname, port: port) 305 | } 306 | removeFromContextAtIndex(i) 307 | break 308 | } 309 | } 310 | } 311 | } 312 | } 313 | 314 | fileprivate func notifyDelegateWithErrorCode(_ clientErrorCode: SCServerErrorCode) { 315 | delegate?.swiftCoapServer(self, didFailWithError: NSError(domain: SCMessage.kCoapErrorDomain, code: clientErrorCode.rawValue, userInfo: [NSLocalizedDescriptionKey : clientErrorCode.descriptionString()])) 316 | } 317 | 318 | fileprivate func handleBlock2ServerRequirementsForMessage(_ message: SCMessage, preferredBlockSZX: UInt?) { 319 | var req = autoBlock2SZX 320 | if let adjustedSZX = preferredBlockSZX { 321 | if let currentSZX = req { 322 | req = min(currentSZX, adjustedSZX) 323 | } 324 | else { 325 | req = adjustedSZX 326 | } 327 | } 328 | 329 | if let activeBlock2SZX = req, let currentPayload = message.payload, currentPayload.count > Int(pow(2, Double(4 + activeBlock2SZX))) { 330 | let blockValue = UInt(activeBlock2SZX) + 8 331 | var byteArray = blockValue.toByteArray() 332 | message.addOption(SCOption.block2.rawValue, data: Data(bytes: &byteArray, count: byteArray.count)) 333 | message.payload = currentPayload.subdata(in: (0 ..< Int(pow(2, Double(activeBlock2SZX + 4))))) 334 | } 335 | } 336 | 337 | fileprivate func createMessageForValues(_ values: (statusCode: SCCodeValue, payloadData: Data?, contentFormat: SCContentFormat?, locationUri: String?), withType type: SCType, relatedMessage message: SCMessage, requestedResource resource: SCResourceModel) -> SCMessage? { 338 | let responseMessage = SCMessage(code: values.statusCode, type: type, payload: values.payloadData) 339 | 340 | if values.contentFormat != nil, var contentFormatByteArray = values.contentFormat?.rawValue.toByteArray() { 341 | responseMessage.addOption(SCOption.contentFormat.rawValue, data: Data(bytes: &contentFormatByteArray, count: contentFormatByteArray.count)) 342 | } 343 | 344 | if let locationUri = values.locationUri { 345 | if let (pathDataArray, queryDataArray) = SCMessage.getPathAndQueryDataArrayFromUriString(locationUri), pathDataArray.count > 0 { 346 | responseMessage.options[SCOption.locationPath.rawValue] = pathDataArray 347 | if queryDataArray.count > 0 { 348 | responseMessage.options[SCOption.locationQuery.rawValue] = queryDataArray 349 | } 350 | } 351 | } 352 | 353 | if message.code == SCCodeValue(classValue: 0, detailValue: 01) { 354 | if let maxAgeVal = resource.maxAgeValue { 355 | var byteArray = maxAgeVal.toByteArray() 356 | responseMessage.addOption(SCOption.maxAge.rawValue, data: Data(bytes: &byteArray, count: byteArray.count)) 357 | } 358 | 359 | if let etag = resource.etag { 360 | responseMessage.addOption(SCOption.etag.rawValue, data: etag) 361 | } 362 | } 363 | 364 | //Block 2 365 | if let block2ValueArray = message.options[SCOption.block2.rawValue], let block2Data = block2ValueArray.first { 366 | var actualValue = UInt.fromData(block2Data) 367 | var requestedBlockSZX = min(actualValue & 0b111, 6) 368 | actualValue >>= 4 369 | 370 | if let activeBlock2SZX = autoBlock2SZX, activeBlock2SZX < requestedBlockSZX { 371 | requestedBlockSZX = activeBlock2SZX 372 | } 373 | 374 | let fixedByteSize = pow(2, Double(requestedBlockSZX + 4)) 375 | 376 | if let currentPayload = values.payloadData { 377 | let blocksCount = UInt(ceil(Double(currentPayload.count) / fixedByteSize)) 378 | if actualValue >= blocksCount { 379 | //invalid block requested 380 | respondWithErrorCode(SCCodeSample.badOption.codeValue(), diagnosticPayload: "Invalid Block Requested".data(using: String.Encoding.utf8), forMessage: message, withType: message.type == .confirmable ? .acknowledgement : .nonConfirmable) 381 | return nil 382 | } 383 | else { 384 | var nextBlockLength: Int 385 | var blockValue = (UInt(actualValue) << 4) + UInt(requestedBlockSZX) 386 | if actualValue < blocksCount - 1 { 387 | blockValue += 8 388 | nextBlockLength = Int(fixedByteSize) 389 | } 390 | else { 391 | nextBlockLength = currentPayload.count - Int(fixedByteSize) * Int(actualValue) 392 | } 393 | var byteArray = blockValue.toByteArray() 394 | responseMessage.addOption(SCOption.block2.rawValue, data: Data(bytes: &byteArray, count: byteArray.count)) 395 | let startPos = Int(fixedByteSize) * Int(actualValue) 396 | responseMessage.payload = currentPayload.subdata(in: (startPos ..< startPos + nextBlockLength)) 397 | } 398 | } 399 | } 400 | else { 401 | handleBlock2ServerRequirementsForMessage(responseMessage, preferredBlockSZX: nil) 402 | } 403 | 404 | //Block 1 405 | if let block1ValueArray = message.options[SCOption.block1.rawValue] { 406 | responseMessage.options[SCOption.block1.rawValue] = block1ValueArray //Echo the option 407 | } 408 | 409 | //Observe 410 | if resource.observable, let observeValueArray = message.options[SCOption.observe.rawValue], let observeValue = observeValueArray.first, let hostname = message.hostName, let port = message.port { 411 | if observeValue.count > 0 && UInt.fromData(observeValue) == 1 { 412 | deregisterObserveForResource(resource, hostname: hostname, port: port) 413 | } 414 | else { 415 | //Register for Observe 416 | var newValueArray: [(UInt64, String, UInt16, UInt, UInt?)] 417 | var currentSequenceNumber: UInt = 0 418 | var prefferredBlock2SZX: UInt? 419 | if let block2ValueArray = message.options[SCOption.block2.rawValue], let block2Data = block2ValueArray.first { 420 | let blockValue = UInt.fromData(block2Data) 421 | prefferredBlock2SZX = blockValue & 0b111 422 | } 423 | 424 | if var valueArray = registeredObserverForResource[resource] { 425 | if let index = getIndexOfObserverInValueArray(valueArray, hostname: hostname, port: port) { 426 | let (_, _, _, sequenceNumber, _) = valueArray[index] 427 | let newSequenceNumber = (sequenceNumber + 1) % UInt(pow(2.0, 24)) 428 | currentSequenceNumber = UInt(newSequenceNumber) 429 | valueArray[index] = (message.token, hostname, port, newSequenceNumber, prefferredBlock2SZX) 430 | } 431 | else { 432 | valueArray.append((message.token, hostname, port, 0, prefferredBlock2SZX)) 433 | } 434 | newValueArray = valueArray 435 | } 436 | else { 437 | newValueArray = [(message.token, hostname, port, 0, prefferredBlock2SZX)] 438 | } 439 | 440 | registeredObserverForResource[resource] = newValueArray 441 | var byteArray = currentSequenceNumber.toByteArray() 442 | responseMessage.addOption(SCOption.observe.rawValue, data: Data(bytes: &byteArray, count: byteArray.count)) 443 | } 444 | } 445 | 446 | 447 | responseMessage.messageId = message.messageId 448 | responseMessage.token = message.token 449 | responseMessage.hostName = message.hostName 450 | responseMessage.port = message.port 451 | 452 | return responseMessage 453 | } 454 | 455 | fileprivate func deregisterObserveForResource(_ resource: SCResourceModel, hostname: String, port: UInt16 ) { 456 | if var valueArray = registeredObserverForResource[resource], let index = getIndexOfObserverInValueArray(valueArray, hostname: hostname, port: port) { 457 | valueArray.remove(at: index) 458 | if valueArray.count == 0 { 459 | registeredObserverForResource.removeValue(forKey: resource) 460 | } 461 | else { 462 | registeredObserverForResource[resource] = valueArray 463 | } 464 | } 465 | } 466 | 467 | fileprivate func getIndexOfObserverInValueArray(_ valueArray: [(UInt64, String, UInt16, UInt, UInt?)], hostname: String, port: UInt16) -> Int? { 468 | for i in 0 ..< valueArray.count { 469 | let (_, currentHost, currentPort, _, _) = valueArray[i] 470 | if currentHost == hostname && currentPort == port { 471 | return i 472 | } 473 | } 474 | return nil 475 | } 476 | 477 | fileprivate func retrievePayloadAfterBlock1HandlingWithMessage(_ message: SCMessage, resultResource: SCResourceModel) -> Data? { 478 | var currentPayload = message.payload 479 | 480 | if let block1ValueArray = message.options[SCOption.block1.rawValue], let blockData = block1ValueArray.first, let hostname = message.hostName, let port = message.port { 481 | let blockAsInt = UInt.fromData(blockData) 482 | let blockNumber = blockAsInt >> 4 483 | let wrapper = SCAddressWrapper(hostname: hostname, port: port) 484 | if var uploadArray = block1UploadsForEndpoints[wrapper] { 485 | for i in 0 ..< uploadArray.count { 486 | let (resource, sequenceNumber, storedPayload) = uploadArray[i] 487 | if resource == resultResource { 488 | if sequenceNumber + 1 != blockNumber { 489 | respondWithErrorCode(SCCodeSample.requestEntityIncomplete.codeValue(), diagnosticPayload: "Incomplete Transmission".data(using: String.Encoding.utf8), forMessage: message, withType: message.type == .confirmable ? .acknowledgement : .nonConfirmable) 490 | return nil 491 | } 492 | var newPayload = NSData(data: storedPayload ?? Data()) as Data 493 | newPayload.append(message.payload ?? Data()) 494 | currentPayload = newPayload 495 | 496 | if blockAsInt & 8 == 8 { 497 | //more bit is set: Store Information 498 | uploadArray[i] = (resource, sequenceNumber + 1, currentPayload as Data?) 499 | block1UploadsForEndpoints[wrapper] = uploadArray 500 | sendMessageWithType(.confirmable, code: SCCodeSample.continue.codeValue(), payload: nil, messageId: message.messageId, hostname: hostname, port: port, token: message.token, options: [SCOption.block1.rawValue : block1ValueArray]) 501 | return nil 502 | } 503 | else { 504 | //No more blocks will be received, cleanup context 505 | uploadArray.remove(at: i) 506 | if uploadArray.count > 0 { 507 | block1UploadsForEndpoints[wrapper] = uploadArray 508 | } 509 | else { 510 | block1UploadsForEndpoints.removeValue(forKey: wrapper) 511 | } 512 | } 513 | break 514 | } 515 | } 516 | } 517 | else if blockNumber == 0 { 518 | if blockAsInt & 8 == 8 { 519 | block1UploadsForEndpoints[SCAddressWrapper(hostname: hostname, port: port)] = [(resultResource, 0, currentPayload as Optional)] 520 | sendMessageWithType(.confirmable, code: SCCodeSample.continue.codeValue(), payload: nil, messageId: message.messageId, hostname: hostname, port: port, token: message.token, options: [SCOption.block1.rawValue : block1ValueArray]) 521 | return nil 522 | } 523 | } 524 | else { 525 | respondWithErrorCode(SCCodeSample.requestEntityIncomplete.codeValue(), diagnosticPayload: "Incomplete Transmission".data(using: String.Encoding.utf8), forMessage: message, withType: message.type == .confirmable ? .acknowledgement : .nonConfirmable) 526 | return nil 527 | } 528 | } 529 | return currentPayload as Data? 530 | } 531 | 532 | func respondWithErrorCode(_ responseCode: SCCodeValue, diagnosticPayload: Data?, forMessage message: SCMessage, withType type: SCType) { 533 | if let hostname = message.hostName, let port = message.port { 534 | sendMessageWithType(type, code: responseCode, payload: diagnosticPayload, messageId: message.messageId, hostname: hostname, port: port, token: message.token) 535 | delegate?.swiftCoapServer(self, didRejectRequestWithCode: message.code, forPath: message.completeUriPath(), withResponseCode: responseCode) 536 | } 537 | } 538 | } 539 | 540 | 541 | // MARK: 542 | // MARK: SC Server Extension 543 | // MARK: SC CoAP Transport Layer Delegate 544 | 545 | extension SCServer: SCCoAPTransportLayerDelegate { 546 | func transportLayerObject(_ transportLayerObject: SCCoAPTransportLayerProtocol, didReceiveData data: Data, fromHost host: String, port: UInt16) { 547 | if let message = SCMessage.fromData(data) { 548 | message.hostName = host 549 | message.port = port 550 | 551 | //Filter 552 | 553 | var resultType: SCType 554 | switch message.type { 555 | case .confirmable: 556 | resultType = .acknowledgement 557 | case .nonConfirmable: 558 | resultType = .nonConfirmable 559 | default: 560 | removeContextForMessage(message) 561 | return 562 | } 563 | 564 | if message.code == SCCodeValue(classValue: 0, detailValue: 00) || message.code.classValue >= 1 { 565 | if message.type == .confirmable || message.type == .nonConfirmable { 566 | sendMessageWithType(.reset, code: SCCodeValue(classValue: 0, detailValue: 00)!, payload: nil, messageId: message.messageId, hostname: host, port: port) 567 | } 568 | return 569 | } 570 | 571 | //URI-Path 572 | 573 | var resultResource: SCResourceModel! 574 | let completeUri = message.completeUriPath() 575 | if completeUri == ".well-known/core" && autoWellKnownCore { 576 | var wellKnownString = "" 577 | for resource in resources { 578 | wellKnownString += "" 579 | if resource != resources.last && resources.count > 0 { 580 | wellKnownString += "," 581 | } 582 | } 583 | if let wellKnownData = wellKnownString.data(using: String.Encoding.utf8) { 584 | let wellKnownResponseMessage = SCMessage(code: SCCodeValue(classValue: 2, detailValue: 05)!, type: resultType, payload: wellKnownData) 585 | wellKnownResponseMessage.messageId = message.messageId 586 | wellKnownResponseMessage.token = message.token 587 | wellKnownResponseMessage.hostName = host 588 | wellKnownResponseMessage.port = port 589 | var hashInt = data.hashValue 590 | wellKnownResponseMessage.addOption(SCOption.etag.rawValue, data: Data(bytes: &hashInt, count: MemoryLayout.size)) 591 | var contentValue: UInt8 = UInt8(SCContentFormat.linkFormat.rawValue) 592 | wellKnownResponseMessage.addOption(SCOption.contentFormat.rawValue, data: Data(bytes: &contentValue, count: 1)) 593 | handleBlock2ServerRequirementsForMessage(wellKnownResponseMessage, preferredBlockSZX: nil) 594 | sendMessage(wellKnownResponseMessage) 595 | return 596 | } 597 | } 598 | else { 599 | for resource in resources { 600 | if resource.name == completeUri { 601 | resultResource = resource 602 | break 603 | } 604 | } 605 | } 606 | 607 | if resultResource != nil { 608 | 609 | func didHandleAsyncRequestForRoute(_ route: SCAllowedRoute) -> Bool { 610 | if resultResource.willHandleDataAsynchronouslyForRoute(route, queryDictionary: message.uriQueryDictionary(), options: message.options, originalMessage: message) { 611 | if message.type == .confirmable { 612 | sendMessageWithType(.acknowledgement, code: SCCodeValue(classValue: 0, detailValue: 00)!, payload: nil, messageId: message.messageId, hostname: host, port: port) 613 | } 614 | delegate?.swiftCoapServer(self, didHandleRequestWithCode: message.code, forResource: resultResource, withResponseCode: SCCodeValue(classValue: 0, detailValue: 00)!) 615 | return true 616 | } 617 | return false 618 | } 619 | 620 | var resultTuple: (statusCode: SCCodeValue, payloadData: Data?, contentFormat: SCContentFormat?, locationUri: String?)? 621 | 622 | switch message.code { 623 | case SCCodeValue(classValue: 0, detailValue: 01)! where resultResource.allowedRoutes & SCAllowedRoute.get.rawValue == SCAllowedRoute.get.rawValue: 624 | //ETAG verification 625 | if resultResource.etag != nil, let etagValueArray = message.options[SCOption.etag.rawValue] { 626 | for etagData in etagValueArray { 627 | if etagData == resultResource.etag { 628 | sendMessageWithType(resultType, code: SCCodeSample.valid.codeValue(), payload: nil, messageId: message.messageId, hostname: host, port: port, token: message.token, options: [SCOption.etag.rawValue : [etagData]]) 629 | delegate?.swiftCoapServer(self, didHandleRequestWithCode: message.code, forResource: resultResource, withResponseCode: SCCodeSample.valid.codeValue()) 630 | return 631 | } 632 | } 633 | } 634 | 635 | if didHandleAsyncRequestForRoute(.get) { 636 | return 637 | } 638 | else if let (statusCode, payloadData, contentFormat) = resultResource.dataForGet(queryDictionary: message.uriQueryDictionary(), options: message.options) { 639 | resultTuple = (statusCode, payloadData, contentFormat, nil) 640 | } 641 | case SCCodeValue(classValue: 0, detailValue: 02)! where resultResource.allowedRoutes & SCAllowedRoute.post.rawValue == SCAllowedRoute.post.rawValue: 642 | if let payload = retrievePayloadAfterBlock1HandlingWithMessage(message, resultResource: resultResource) { 643 | if didHandleAsyncRequestForRoute(.post) { 644 | return 645 | } 646 | else if let tuple = resultResource.dataForPost(queryDictionary: message.uriQueryDictionary(), options: message.options, requestData: payload) { 647 | resultTuple = tuple 648 | } 649 | } 650 | else { 651 | return 652 | } 653 | case SCCodeValue(classValue: 0, detailValue: 03)! where resultResource.allowedRoutes & SCAllowedRoute.put.rawValue == SCAllowedRoute.put.rawValue: 654 | if let payload = retrievePayloadAfterBlock1HandlingWithMessage(message, resultResource: resultResource) { 655 | if didHandleAsyncRequestForRoute(.put) { 656 | return 657 | } 658 | else if let tuple = resultResource.dataForPut(queryDictionary: message.uriQueryDictionary(), options: message.options, requestData: payload) { 659 | resultTuple = tuple 660 | } 661 | } 662 | else { 663 | return 664 | } 665 | case SCCodeValue(classValue: 0, detailValue: 04)! where resultResource.allowedRoutes & SCAllowedRoute.delete.rawValue == SCAllowedRoute.delete.rawValue: 666 | if didHandleAsyncRequestForRoute(.delete) { 667 | return 668 | } 669 | else if let (statusCode, payloadData, contentFormat) = resultResource.dataForDelete(queryDictionary: message.uriQueryDictionary(), options: message.options) { 670 | resultTuple = (statusCode, payloadData, contentFormat, nil) 671 | } 672 | default: 673 | respondWithErrorCode(SCCodeSample.methodNotAllowed.codeValue(), diagnosticPayload: "Method Not Allowed".data(using: String.Encoding.utf8), forMessage: message, withType: resultType) 674 | return 675 | } 676 | 677 | if let finalTuple = resultTuple, let responseMessage = createMessageForValues(finalTuple, withType: resultType, relatedMessage: message, requestedResource: resultResource) { 678 | sendMessage(responseMessage) 679 | delegate?.swiftCoapServer(self, didHandleRequestWithCode: message.code, forResource: resultResource, withResponseCode: responseMessage.code) 680 | } 681 | else { 682 | respondWithErrorCode(SCCodeSample.methodNotAllowed.codeValue(), diagnosticPayload: "Method Not Allowed".data(using: String.Encoding.utf8), forMessage: message, withType: resultType) 683 | } 684 | } 685 | else { 686 | respondWithErrorCode(SCCodeValue(classValue: 4, detailValue: 04)!, diagnosticPayload: "Not Found".data(using: String.Encoding.utf8), forMessage: message, withType: resultType) 687 | } 688 | } 689 | } 690 | 691 | func transportLayerObject(_ transportLayerObject: SCCoAPTransportLayerProtocol, didFailWithError error: NSError) { 692 | notifyDelegateWithErrorCode(.transportLayerError) 693 | } 694 | } 695 | --------------------------------------------------------------------------------