├── 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 += "\(resource.name)>"
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 |
--------------------------------------------------------------------------------