├── Central Manager
├── CentralManager.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ ├── olivier.xcuserdatad
│ │ │ ├── UserInterfaceState.xcuserstate
│ │ │ └── xcdebugger
│ │ │ │ └── Expressions.xcexplist
│ │ │ └── ormaa.xcuserdatad
│ │ │ ├── UserInterfaceState.xcuserstate
│ │ │ └── xcdebugger
│ │ │ └── Expressions.xcexplist
│ └── xcuserdata
│ │ ├── olivier.xcuserdatad
│ │ ├── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ │ └── ormaa.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ ├── CentralManager.xcscheme
│ │ └── xcschememanagement.plist
├── CentralManager
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── CentralManager.entitlements
│ ├── Info.plist
│ ├── Singleton.swift
│ ├── Tools.swift
│ └── ViewController.swift
├── CentralManager_MacOS
│ ├── Base.lproj
│ │ └── Main.storyboard
│ ├── CentralManager_MacOS.entitlements
│ ├── Supporting files
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ └── Info.plist
│ ├── Tools_MACOS.swift
│ └── ViewController.swift
└── SharedCode
│ ├── BLE Stack
│ ├── BLEProtocol.swift
│ ├── BLE_CentralManager.swift
│ ├── BLE_Connection.swift
│ ├── BLE_InitStartStop.swift
│ ├── BLE_ReadWrite.swift
│ ├── BLE_ServicesCharacteristics.swift
│ └── BLE_Tools.swift
│ ├── BluetoothController.swift
│ ├── Datas.swift
│ ├── Extensions.swift
│ └── Log.swift
├── LICENSE
├── Peripheral Manager
├── BlueToothPeripheral.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ ├── olivier.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ │ │ └── ormaa.xcuserdatad
│ │ │ ├── UserInterfaceState (2017-01-08T11_16_26.629).xcuserstate
│ │ │ ├── UserInterfaceState (2017-01-08T11_30_12.633).xcuserstate
│ │ │ ├── UserInterfaceState.xcuserstate
│ │ │ └── xcdebugger
│ │ │ └── Expressions.xcexplist
│ └── xcuserdata
│ │ ├── olivier.xcuserdatad
│ │ ├── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ │ └── ormaa.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ ├── BlueToothPeripheral.xcscheme
│ │ └── xcschememanagement.plist
├── BlueToothPeripheral
│ ├── BLEPeripheralManager (2017-01-08T17_36_04.875).swift
│ ├── Base.lproj
│ │ └── Main.storyboard
│ ├── Info.plist
│ ├── Supporting Files
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ └── Base.lproj
│ │ │ └── LaunchScreen.storyboard
│ └── ViewController.swift
├── BlueToothPeripheral_MACOS
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Base.lproj
│ │ └── Main.storyboard
│ ├── BlueToothPeripheral_MACOS.entitlements
│ ├── Info.plist
│ └── ViewController.swift
└── Shared
│ ├── BLE_PeripheralManager.swift
│ ├── BLE_Protocol.swift
│ ├── BLE_Tools.swift
│ └── Tools.swift
├── README.md
└── logViewer
└── LogViewer
├── LogViewer.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── ormaa.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── ormaa.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ ├── LogViewer.xcscheme
│ └── xcschememanagement.plist
└── LogViewer
├── AppDelegate.swift
├── Assets.xcassets
├── AppIcon.appiconset
│ ├── Contents.json
│ ├── Icon-40.png
│ ├── Icon-40@2x.png
│ ├── Icon-40@3x.png
│ ├── Icon-60@2x.png
│ ├── Icon-60@3x.png
│ ├── Icon-72.png
│ ├── Icon-72@2x.png
│ ├── Icon-76.png
│ ├── Icon-76@2x.png
│ ├── Icon-83.5@2x.png
│ ├── Icon-Small-50.png
│ ├── Icon-Small-50@2x.png
│ ├── Icon-Small.png
│ ├── Icon-Small@2x.png
│ ├── Icon-Small@3x.png
│ ├── Icon.png
│ ├── Icon@2x.png
│ ├── NotificationIcon@2x.png
│ ├── NotificationIcon@3x.png
│ ├── NotificationIcon~ipad.png
│ └── NotificationIcon~ipad@2x.png
└── Contents.json
├── Base.lproj
├── LaunchScreen.storyboard
└── Main.storyboard
├── Info.plist
├── LogViewer-Bridging-Header.h
├── LogViewer.entitlements
└── ViewController.swift
/Central Manager/CentralManager.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager.xcodeproj/project.xcworkspace/xcuserdata/olivier.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/Central Manager/CentralManager.xcodeproj/project.xcworkspace/xcuserdata/olivier.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Central Manager/CentralManager.xcodeproj/project.xcworkspace/xcuserdata/olivier.xcuserdatad/xcdebugger/Expressions.xcexplist:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
10 |
11 |
12 |
13 |
15 |
16 |
18 |
19 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/Central Manager/CentralManager.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Central Manager/CentralManager.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/xcdebugger/Expressions.xcexplist:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
10 |
11 |
12 |
13 |
15 |
16 |
18 |
19 |
20 |
21 |
23 |
24 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager.xcodeproj/xcuserdata/olivier.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
22 |
23 |
24 |
26 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager.xcodeproj/xcuserdata/olivier.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CentralManager.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 | CentralManager.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 1
16 |
17 | CentralManager_MacOS.xcscheme
18 |
19 | orderHint
20 | 1
21 |
22 | CentralManager_MacOS.xcscheme_^#shared#^_
23 |
24 | orderHint
25 | 0
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager.xcodeproj/xcuserdata/ormaa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager.xcodeproj/xcuserdata/ormaa.xcuserdatad/xcschemes/CentralManager.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 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager.xcodeproj/xcuserdata/ormaa.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CentralManager.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 1C10F2671E6B1F7B00DDD431
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CentralManager
4 | //
5 | // Created by Olivier Robin on 04/03/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import UserNotifications
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | var window: UIWindow?
16 |
17 | let singleton = Singleton.sharedInstance()
18 |
19 | internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
20 | // Override point for customization after application launch.
21 |
22 | singleton.logger.log("application didFinishLaunchingWithOptions")
23 |
24 | singleton.appRestored = false
25 | singleton.centralManagerToRestore = nil
26 |
27 | registerLocal()
28 |
29 | // if waked up by the system, with bluetooth identifier in parameter :
30 | // We need to initialize a central manager, with same name.
31 | // a bluetooth event accoured.
32 | //
33 | if let peripheralManagerIdentifiers: [String] = launchOptions?[UIApplication.LaunchOptionsKey.bluetoothCentrals] as? [String]{
34 | if peripheralManagerIdentifiers.count > 1 {
35 | // TODO : manage this case
36 | }
37 | if peripheralManagerIdentifiers.count == 1 {
38 | // only one central Manager to initialize again
39 | let identifier = peripheralManagerIdentifiers.first
40 |
41 | singleton.logger.log("UIApplicationLaunchOptionsKey.bluetoothCentrals] : ")
42 | singleton.logger.log("App was closed by system. will restore the central manager ")
43 | singleton.logger.log("--> " + identifier!)
44 |
45 | // flag allowing to know that we need to restore the central manager
46 | singleton.appRestored = true
47 | // name of central manager to restore
48 | singleton.centralManagerToRestore = identifier!
49 |
50 | }
51 | }
52 | return true
53 | }
54 |
55 |
56 | @objc func registerLocal() {
57 | let center = UNUserNotificationCenter.current()
58 |
59 | center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
60 | if granted {
61 | print("Yay!")
62 | } else {
63 | print("D'oh")
64 | }
65 | }
66 | }
67 |
68 | @objc func scheduleLocal() {
69 | let hour = Calendar.current.component(.hour, from: Date())
70 | let min = Calendar.current.component(.minute, from: Date())
71 | let sec = Calendar.current.component(.second, from: Date())
72 |
73 | var dateComponents = DateComponents()
74 | dateComponents.hour = hour // 10
75 | dateComponents.minute = min // 30
76 | dateComponents.second = sec + 5
77 |
78 | let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
79 |
80 | let content = UNMutableNotificationContent()
81 | content.title = "Title goes here"
82 | content.body = "Main text goes here"
83 | content.categoryIdentifier = "customIdentifier"
84 | content.userInfo = ["customData": "fizzbuzz"]
85 | content.sound = UNNotificationSound.default
86 |
87 | let center = UNUserNotificationCenter.current()
88 | let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
89 | center.add(request)
90 | }
91 |
92 |
93 | func applicationWillResignActive(_ application: UIApplication) {
94 | // 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.
95 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
96 | singleton.logger.log("applicationWillResignActive")
97 | }
98 |
99 | func applicationDidEnterBackground(_ application: UIApplication) {
100 | // 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.
101 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
102 | singleton.logger.log("applicationDidEnterBackground")
103 | }
104 |
105 | func applicationWillEnterForeground(_ application: UIApplication) {
106 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
107 | singleton.logger.log("applicationWillEnterForeground")
108 | }
109 |
110 | func applicationDidBecomeActive(_ application: UIApplication) {
111 | // 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.
112 | singleton.logger.log("applicationDidBecomeActive")
113 | }
114 |
115 | func applicationWillTerminate(_ application: UIApplication) {
116 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
117 | singleton.logger.log("applicationWillTerminate")
118 | }
119 |
120 |
121 | }
122 |
123 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager/Assets.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" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
--------------------------------------------------------------------------------
/Central Manager/CentralManager/Base.lproj/LaunchScreen.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 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager/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 |
32 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
63 |
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 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager/CentralManager.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.application-groups
6 |
7 | group.fr.ormaa
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSBluetoothPeripheralUsageDescription
6 | need to use bluetooth
7 | NSBluetoothAlwaysUsageDescription
8 | need to use bluetooth always
9 |
10 | CFBundleDevelopmentRegion
11 | en
12 | CFBundleExecutable
13 | $(EXECUTABLE_NAME)
14 | CFBundleIdentifier
15 | $(PRODUCT_BUNDLE_IDENTIFIER)
16 | CFBundleInfoDictionaryVersion
17 | 6.0
18 | CFBundleName
19 | $(PRODUCT_NAME)
20 | CFBundlePackageType
21 | APPL
22 | CFBundleShortVersionString
23 | 1.0
24 | CFBundleVersion
25 | 1
26 | LSRequiresIPhoneOS
27 |
28 | UIBackgroundModes
29 |
30 | bluetooth-central
31 | remote-notification
32 |
33 | UILaunchStoryboardName
34 | LaunchScreen
35 | UIMainStoryboardFile
36 | Main
37 | UIRequiredDeviceCapabilities
38 |
39 | armv7
40 |
41 | UISupportedInterfaceOrientations
42 |
43 | UIInterfaceOrientationPortrait
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager/Singleton.swift:
--------------------------------------------------------------------------------
1 | //import UIKit
2 |
3 | import Foundation
4 |
5 | //Model singleton so that we can refer to this from throughout the app.
6 | let appControllerSingletonGlobal = Singleton()
7 |
8 |
9 | // this class is a singleton
10 | //
11 | class Singleton: NSObject {
12 |
13 | class func sharedInstance() -> Singleton {
14 | return appControllerSingletonGlobal
15 | }
16 |
17 |
18 |
19 | // used by BLE stack
20 | let serialQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default) //DispatchQueue(label: "my serial queue")
21 |
22 | // Be careful. those class cannot use appController, because it would be a cyclic redundance
23 | let bluetoothController = BluetoothController()
24 | let tools = Tools()
25 |
26 | // App restore after IOS killed it, and bluetooth status has changed : device switch on, or advertise something.
27 | var appRestored = false
28 | var centralManagerToRestore: String?
29 |
30 | // logger
31 | let logger = Log()
32 |
33 |
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager/Tools.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Tools.swift
3 | //
4 | //
5 | // Created by Olivier Robin on 24/01/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SystemConfiguration
11 | import UIKit
12 |
13 | class Tools {
14 |
15 |
16 | // convert string into Bool or Double
17 | //
18 | static func string2Bool(value: String) -> Bool {
19 | if let b = Bool(value) {
20 | return b
21 | }
22 | return true
23 | }
24 | static func string2Double(value: String) -> Double {
25 | if let b = Double(value) {
26 | return b
27 | }
28 | return 0.5
29 | }
30 | static func string2Float(value: String) -> Double {
31 | if let b = Double(value) {
32 | return b
33 | }
34 | return 0.5
35 | }
36 |
37 |
38 | // Return a string, even if nil
39 | //
40 | static func toString(_ txt: String?) -> String {
41 | if (txt == nil) {
42 | return "???"
43 | }
44 | else {
45 | return txt!
46 | }
47 | }
48 |
49 |
50 | // Display an alert popup
51 | func simpleAlert(message: String) {
52 |
53 | if let window = UIApplication.shared.windows.filter({$0.isKeyWindow}).first, let controller = window.rootViewController {
54 |
55 | DispatchQueue.main.async {
56 | let picker = UIAlertController(title: "Attention", message: message, preferredStyle: .alert)
57 | var action: UIAlertAction
58 | action = UIAlertAction(title: "OK", style: .default)
59 |
60 | picker.addAction(action)
61 | controller.present(picker, animated: true, completion: nil)
62 | }
63 | }
64 | }
65 |
66 | // Display an alert popup, use closure for returned value
67 | func simpleSyncAlert( message: String, completion:@escaping (_ message: String) -> Void) {
68 |
69 | if let window = UIApplication.shared.windows.filter({$0.isKeyWindow}).first, let controller = window.rootViewController {
70 |
71 | let picker = UIAlertController(title: "Attention",
72 | message: message, preferredStyle: .alert)
73 | var action: UIAlertAction
74 | action = UIAlertAction(title: "OK", style: .default, handler: { (UIAlertAction) in
75 | completion("ok")
76 | })
77 |
78 | picker.addAction(action)
79 | controller.present(picker, animated: true, completion: nil)
80 | }
81 | }
82 |
83 | // Display an alert popup, use closure for returned value
84 | func SyncAlert( message: String, completion:@escaping (_ retour: String) -> Void) {
85 |
86 | if let window = UIApplication.shared.windows.filter({$0.isKeyWindow}).first, let controller = window.rootViewController {
87 |
88 | let picker = UIAlertController(title: "Attention", message: message, preferredStyle: .alert)
89 | var action: UIAlertAction
90 | action = UIAlertAction(title: "Oui", style: .default, handler: { (UIAlertAction) in
91 | completion("oui")
92 | })
93 | var action2: UIAlertAction
94 | action2 = UIAlertAction(title: "Non", style: .default, handler: { (UIAlertAction) in
95 | completion("non")
96 | })
97 |
98 | picker.addAction(action)
99 | picker.addAction(action2)
100 | controller.present(picker, animated: true, completion: nil)
101 | }
102 | }
103 |
104 |
105 | }
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // CentralManager
4 | //
5 | // Created by Olivier Robin on 04/03/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController, BLEProtocol {
12 |
13 | @IBOutlet weak var bleLogTextView: UITextView!
14 |
15 | var appDelegate:AppDelegate? = nil
16 |
17 |
18 | // this is the name of the peripheral that we are looking for.
19 | // change it as you want. but change it also on peripheral side.
20 | // note : if you want to connect using the main service of the peripheral : this peripheral need to advertise its service, in advertise area
21 | //
22 | //let peripheralName = "ORMAA_P1.0"
23 | let peripheralUUID = "00001901-0000-1000-8000-00805F9B34FB"
24 | let characRead = "2AC6A7BF-2C60-42D0-8013-AECEF2A124C0" //"00002B00-0000-1000-8000-00805F9B34FB"
25 | let characWrite = "9B89E762-226A-4BBB-A381-A4B8CC6E1105"
26 |
27 | var timerRead: Timer? = nil
28 | var timerWrite: Timer? = nil
29 |
30 | override func viewDidLoad() {
31 | super.viewDidLoad()
32 |
33 | appDelegate = UIApplication.shared.delegate as! AppDelegate?
34 | appDelegate!.singleton.logger.log("viewDidLoad")
35 | }
36 |
37 | override func didReceiveMemoryWarning() {
38 | super.didReceiveMemoryWarning()
39 | // Dispose of any resources that can be recreated.
40 | }
41 |
42 | override func viewDidAppear(_ animated: Bool) {
43 | appDelegate!.singleton.logger.log("viewDidAppear")
44 |
45 | bleLogTextView.text = ""
46 |
47 | if appDelegate!.singleton.appRestored,
48 | let id = appDelegate!.singleton.centralManagerToRestore {
49 |
50 | log("View did appear, restore peropheral connection \(id)")
51 |
52 | appDelegate!.singleton.bluetoothController.restoreCentralManager(viewControllerDelegate: self, centralName: id )
53 | }
54 | }
55 |
56 | func log(_ object: Any?) {
57 | appDelegate?.singleton.logger.log(object)
58 | }
59 |
60 | // Add text to the UITextView
61 | func addText(text: String) {
62 | log(text)
63 |
64 | DispatchQueue.main.async {
65 | let txt = self.bleLogTextView.text + "\n"
66 | self.bleLogTextView.text = txt + text
67 | }
68 | }
69 |
70 |
71 | @IBAction func stopCentralManager(_ sender: Any) {
72 | self.appDelegate!.singleton.bluetoothController.stopCentralManager()
73 |
74 | self.bleLogTextView.text = ""
75 | }
76 |
77 |
78 | // user clicked on start central button
79 | @IBAction func startCentralClick(_ sender: Any) {
80 | log("start central click")
81 |
82 | DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {
83 |
84 | self.addText(text: "Starting central Manager")
85 |
86 | self.appDelegate!.singleton.bluetoothController.appDelegate = self.appDelegate // TODO remove this ugly thing
87 | self.appDelegate!.singleton.bluetoothController.ble.appDelegate = self.appDelegate // TODO remove this ugly thing
88 |
89 | if !self.appDelegate!.singleton.bluetoothController.startCentralManager(viewControllerDelegate: self) {
90 | // Display en error
91 | self.addText(text: "I believe that Bluetooth is off, because starting central manager return an error")
92 | return
93 | }
94 |
95 | self.addText(text: "central manager started. will search for peripheral : " + self.peripheralUUID)
96 |
97 | if !self.appDelegate!.singleton.bluetoothController.searchDevice(uuidName: self.peripheralUUID) {
98 | // Display en error
99 | self.addText(text: "The peripheral was not found, even after a while. check its name, and if it is near, and swithed on.\n please try again later.")
100 | return
101 | }
102 |
103 | self.addText(text: "peripheral : " + self.peripheralUUID + " discovered.")
104 |
105 | if !self.appDelegate!.singleton.bluetoothController.connectToDevice(uuid: self.peripheralUUID) {
106 | // Display en error
107 | self.addText(text: "Connection failed !!!")
108 | return
109 | }
110 |
111 | if !self.appDelegate!.singleton.bluetoothController.discoverServices() {
112 | self.addText(text: "discover service and characteristics failed !!!")
113 | return
114 | }
115 |
116 | self.addText(text: "services discovered")
117 |
118 | self.addText(text: "request notification for charac :\n" + self.characRead)
119 | self.appDelegate!.singleton.bluetoothController.requestNotify(uuid: self.characRead)
120 |
121 | self.addText(text: "\nRead a value from peripheral " + self.characRead)
122 | self.appDelegate?.singleton.bluetoothController.read(uuid: self.characRead)
123 |
124 | self.appDelegate?.singleton.bluetoothController.write(uuid: self.characWrite, message: "I am ok guy")
125 | self.addText(text: "\nwrite to peripheral : I am ok guy, char :" + self.characWrite)
126 |
127 | self.appDelegate?.singleton.bluetoothController.writeAsync(uuid: self.characWrite, message: "hello mate")
128 | self.addText(text: "\nwriteAsync to peripheral : hello mate, char :" + self.characWrite)
129 |
130 | // start some timer, which will read or write data from / to peripheral
131 | //self.timerRead = Timer.scheduledTimer(timeInterval: TimeInterval(2), target: self, selector: #selector(self.read), userInfo: nil, repeats: true)
132 | //self.timerWrite = Timer.scheduledTimer(timeInterval: TimeInterval(5), target: self, selector: #selector(self.write), userInfo: nil, repeats: true)
133 |
134 | }
135 | }
136 |
137 |
138 | func read() {
139 | self.addText(text: "Read value returned by peripheral : " + self.characRead)
140 |
141 | self.appDelegate?.singleton.bluetoothController.read(uuid: characRead)
142 |
143 | }
144 |
145 |
146 |
147 | // called by bluetooth stack
148 | //
149 | func valueRead(message: String) {
150 | self.addText(text: "Read : " + message)
151 |
152 | }
153 |
154 | func valueWrite(message: String) {
155 | self.addText(text: "write : " + message)
156 | }
157 |
158 |
159 |
160 |
161 | // disconnected : ble is too far away, or sitched off
162 | func disconnected(message: String) {
163 | self.addText(text: "\ndisconnected from peripheral : " + message)
164 | }
165 |
166 |
167 |
168 | // connection to ble failed
169 | func failConnected(message: String) {
170 | self.addText(text: "\nconnection to peripheral failed : " + message)
171 | }
172 |
173 |
174 |
175 | func connected(message: String) {
176 | self.addText(text: "\nconnected to peripheral : " + message + " - " + self.peripheralUUID)
177 | }
178 |
179 |
180 |
181 | }
182 |
183 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager_MacOS/CentralManager_MacOS.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager_MacOS/Supporting files/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CentralManager_MacOS
4 | //
5 | // Created by olivier Robin on 16/07/2018.
6 | // Copyright © 2018 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 | let singleton = Singleton.sharedInstance()
15 |
16 |
17 | func applicationDidFinishLaunching(_ aNotification: Notification) {
18 | // Insert code here to initialize your application
19 | }
20 |
21 | func applicationWillTerminate(_ aNotification: Notification) {
22 | // Insert code here to tear down your application
23 | }
24 |
25 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager_MacOS/Supporting files/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Central Manager/CentralManager_MacOS/Supporting files/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Central Manager/CentralManager_MacOS/Supporting files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2018 fr.ormaa. All rights reserved.
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager_MacOS/Tools_MACOS.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Tools_MACOS.swift
3 | // CentralManager_MacOS
4 | //
5 | // Created by olivier Robin on 16/07/2018.
6 | // Copyright © 2018 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SystemConfiguration
11 |
12 | class Tools {
13 |
14 |
15 | // convert string into Bool or Double
16 | //
17 | static func string2Bool(value: String) -> Bool {
18 | if let b = Bool(value) {
19 | return b
20 | }
21 | return true
22 | }
23 | static func string2Double(value: String) -> Double {
24 | if let b = Double(value) {
25 | return b
26 | }
27 | return 0.5
28 | }
29 | static func string2Float(value: String) -> Double {
30 | if let b = Double(value) {
31 | return b
32 | }
33 | return 0.5
34 | }
35 |
36 |
37 | // Return a string, even if nil
38 | //
39 | static func toString(_ txt: String?) -> String {
40 | if (txt == nil) {
41 | return "???"
42 | }
43 | else {
44 | return txt!
45 | }
46 | }
47 |
48 |
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/Central Manager/CentralManager_MacOS/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // CentralManager_MacOS
4 | //
5 | // Created by olivier Robin on 16/07/2018.
6 | // Copyright © 2018 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class ViewController: NSViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 |
16 | // Do any additional setup after loading the view.
17 | }
18 |
19 | override var representedObject: Any? {
20 | didSet {
21 | // Update the view, if already loaded.
22 | }
23 | }
24 |
25 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/Central Manager/SharedCode/BLE Stack/BLEProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BLEProtocol.swift
3 | // CentralManager
4 | //
5 | // Created by Olivier Robin on 04/03/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | protocol BLEProtocol {
13 |
14 | func disconnected(message: String)
15 | func failConnected(message: String)
16 | func connected(message: String)
17 | func valueRead(message: String)
18 | func valueWrite(message: String)
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/Central Manager/SharedCode/BLE Stack/BLE_CentralManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BLECentralManager.swift
3 | // BlueToothCentral
4 | //
5 | // Created by Olivier Robin on 30/10/2016.
6 | // Copyright © 2016 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreBluetooth
11 | //import UIKit
12 | //import UserNotifications
13 |
14 |
15 | // Doc apple
16 | // https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/PerformingCommonCentralRoleTasks/PerformingCommonCentralRoleTasks.html#//apple_ref/doc/uid/TP40013257-CH3-SW1
17 |
18 |
19 |
20 |
21 |
22 | // BlueTooth do not work if it not instanciated, called, managed, from a uiview controller, linked to a .xib, or storyboard
23 | //
24 | class BLECentralManager : NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
25 |
26 | var appDelegate:AppDelegate? = nil
27 |
28 | var centralManager: CBCentralManager? = nil
29 |
30 | // peripheral we are looking for...
31 | var peripheralConnecting: CBPeripheral? = nil
32 | var peripheralConnected: CBPeripheral? = nil
33 | var rssiDBConnected:NSNumber? = nil
34 |
35 | var peripherals: [CBPeripheral] = []
36 | var servicesDiscovered: [CBService] = []
37 | var rssiDB:[NSNumber] = []
38 | var advertisementDatas: [oneAdvertisement] = []
39 |
40 | var isServicesDiscovered = false
41 | var blueToothEnabled: Bool? = nil
42 |
43 | var bleError = ""
44 |
45 | var valueWritten = false
46 | var valueReadBytes: [UInt8] = []
47 |
48 | var bleDelegate: BLEProtocol?
49 |
50 | var searchedPeripheralUUID: String = ""
51 | }
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/Central Manager/SharedCode/BLE Stack/BLE_Connection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BLECentralManager+Connect.swift
3 | // BlueToothCentral
4 | //
5 | // Created by Olivier Robin on 07/11/2016.
6 | // Copyright © 2016 fr.ormaa. All rights reserved.
7 | //
8 |
9 |
10 | import Foundation
11 | //import UIKit
12 | import CoreBluetooth
13 |
14 |
15 | extension BLECentralManager {
16 |
17 |
18 |
19 | // Connect to a peripheral, listed in the peripherals list
20 | //
21 | func connectToAsync(serviceUUID: String) {
22 |
23 | //centralManager?.stopScan()
24 | bleError = ""
25 |
26 | if peripherals.count > 0 {
27 |
28 | for i in 0...peripherals.count - 1 {
29 | let n = getPeripheralUUID(number: i)
30 | print(n)
31 | if n == serviceUUID {
32 | connect(peripheral: peripherals[i])
33 | }
34 | }
35 | }
36 | }
37 |
38 |
39 |
40 | // Connect to a peripheral
41 | //
42 | func connect(peripheral: CBPeripheral?) {
43 | //stopBLEScan()
44 |
45 | log("connect to peripheral \(peripheral?.identifier)")
46 |
47 | if (peripheral != nil) {
48 | peripheralConnecting = peripheral
49 | peripheralConnected = nil
50 | let options = [CBConnectPeripheralOptionNotifyOnConnectionKey: true as AnyObject,
51 | CBConnectPeripheralOptionNotifyOnDisconnectionKey: true as AnyObject,
52 | CBConnectPeripheralOptionNotifyOnNotificationKey: true as AnyObject,
53 | CBCentralManagerRestoredStatePeripheralsKey: true as AnyObject,
54 | CBCentralManagerRestoredStateScanServicesKey : true as AnyObject]
55 | peripheral?.delegate = self
56 | centralManager?.connect(peripheral!, options: options)
57 | }
58 | }
59 |
60 |
61 |
62 | // disconnect from a connected peripheral
63 | //
64 | func disconnect() {
65 | log( "disconnect peripheral")
66 | if peripheralConnecting != nil {
67 | centralManager?.cancelPeripheralConnection(peripheralConnecting!)
68 | }
69 | }
70 |
71 |
72 |
73 | // delegate
74 | //
75 | // Connected to a peripheral
76 | //
77 | func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral){
78 | if peripheral.name != nil {
79 | log("didConnect event, peripheral: " + peripheral.name!)
80 | }
81 | else {
82 | log("didConnect event, peripheral identifier: " + peripheral.identifier.uuidString)
83 | }
84 |
85 | peripheralConnected = peripheral
86 | var name = "nil"
87 | if peripheral.name != nil {
88 | name = peripheral.name!
89 | }
90 | bleDelegate?.connected(message: name)
91 | }
92 |
93 |
94 | // delegate
95 | // Fail to connect
96 | //
97 | func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
98 | if error != nil {
99 | bleError = ("Error didFailToConnect : \(error!.localizedDescription)")
100 | log(bleError)
101 | bleDelegate?.failConnected(message: error!.localizedDescription)
102 | }
103 | else {
104 | log("didFailToConnect")
105 | bleDelegate?.failConnected(message: "")
106 | }
107 |
108 | }
109 |
110 |
111 |
112 | // delegate
113 | //
114 | // disconnected form a peripheral
115 | //
116 | func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
117 | if error != nil {
118 | bleError = ("Error didDisconnectPeripheral : \(error!.localizedDescription)")
119 | log(bleError)
120 | bleDelegate?.disconnected(message: error!.localizedDescription)
121 | }
122 | else {
123 | log("didDisconnectPeripheral")
124 | bleDelegate?.disconnected(message: "")
125 | }
126 | }
127 |
128 |
129 |
130 | // delegate
131 | // Called when connection is lost, or done again
132 | //
133 | func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {
134 | log("peripheral service modified. May be connection is lost !")
135 |
136 | for s in invalidatedServices {
137 | log("service: " + s.uuid.uuidString)
138 | }
139 | }
140 |
141 |
142 |
143 | // delegate
144 | // name of the device update
145 | //
146 | func peripheralDidUpdateName(_ peripheral: CBPeripheral) {
147 | log("peripheral name has change : " + peripheral.name!)
148 | }
149 |
150 |
151 |
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/Central Manager/SharedCode/BLE Stack/BLE_InitStartStop.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BLECentraManager+InitStartStop.swift
3 | // BlueToothCentral
4 | //
5 | // Created by Olivier Robin on 07/11/2016.
6 | // Copyright © 2016 fr.ormaa. All rights reserved.
7 | //
8 |
9 |
10 | import Foundation
11 | import UIKit
12 | import CoreBluetooth
13 |
14 |
15 | // Apple documentation :
16 | // https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html
17 |
18 |
19 | extension BLECentralManager {
20 |
21 | func log(_ object: Any?) {
22 | appDelegate?.singleton.logger.log(object)
23 | }
24 |
25 |
26 | // start discover of BLE peripherals
27 | //
28 | func initCentralManager(bleDelegate: BLEProtocol) {
29 | self.bleDelegate = bleDelegate
30 |
31 | //// #if IOS
32 | // appDelegate = UIApplication.shared.delegate as! AppDelegate?
33 | //// #endif
34 | //// #if MACOS
35 | //// appDelegate = UIApplication.shared.delegate as! AppDelegate?
36 | //// #endif
37 | //
38 | log("initCentralManager - Starting CentralManager")
39 | bleError = ""
40 | blueToothEnabled = nil
41 |
42 | // Delete all device, services, rssDB found previously
43 | peripherals.removeAll()
44 | rssiDB.removeAll()
45 | servicesDiscovered.removeAll()
46 | advertisementDatas.removeAll()
47 |
48 | // in case of centralManager was already started, stop scan, and free the object
49 | centralManager?.stopScan()
50 | centralManager = nil
51 |
52 | // pause the processor, 0.5 sec, before initalize it
53 | // is it needed ???
54 | //usleep(500000)
55 |
56 | centralManager = CBCentralManager(delegate: self, queue: nil, options:[CBCentralManagerOptionRestoreIdentifierKey: "fr.ormaa.centralManager"])
57 | }
58 |
59 | func stop() {
60 | log("initCentralManager - Stop CentralManager")
61 |
62 | // in case of centralManager was already started, stop scan, and free the object
63 | centralManager?.stopScan()
64 | centralManager = nil
65 |
66 | bleError = ""
67 | blueToothEnabled = nil
68 |
69 | // Delete all device, services, rssDB found previously
70 | peripherals.removeAll()
71 | rssiDB.removeAll()
72 | servicesDiscovered.removeAll()
73 | advertisementDatas.removeAll()
74 |
75 | }
76 |
77 | // start to scan peripherals
78 | func startScan(uid: String) {
79 |
80 | searchedPeripheralUUID = uid
81 |
82 | log("startScan for peripheral \(uid)")
83 | let BLEServiceUUID = CBUUID(string: searchedPeripheralUUID) // "00001901-0000-1000-8000-00805F9B34FB")
84 | centralManager?.scanForPeripherals(withServices: [BLEServiceUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey: false])
85 | }
86 |
87 |
88 |
89 | // Stop scan
90 | func stopScan() {
91 | centralManager?.stopScan()
92 | }
93 |
94 |
95 |
96 |
97 |
98 | // delegate
99 | // called after init centralManager
100 | //
101 | func centralManagerDidUpdateState(_ central: CBCentralManager){
102 | appDelegate?.singleton.logger.log(" ")
103 | appDelegate?.singleton.logger.log("centralManagerDidUpdateState")
104 | appDelegate?.singleton.logger.log(" ")
105 |
106 | if (central.state == CBManagerState.poweredOn){
107 | log("BlueTooth is powered ON")
108 | blueToothEnabled = true
109 | }else{
110 | log("BlueTooth is OFF or disconnected !!!")
111 | blueToothEnabled = false
112 | }
113 |
114 | }
115 |
116 |
117 | // Delegate
118 | // The app was killed by IOS, then BLE was disconnected. this is called when device is visible again
119 | //
120 | public func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
121 |
122 | log("centralManager will restore connection")
123 |
124 | if let delegate = appDelegate,
125 | let id = delegate.singleton.centralManagerToRestore {
126 |
127 | log("retore id \(id)")
128 |
129 | if let peripheralsObject = dict[CBCentralManagerRestoredStatePeripheralsKey] {
130 | let peripherals = peripheralsObject as! Array
131 |
132 | for peripheral in peripherals {
133 | log("Peripheral found \(peripheral.identifier)")
134 |
135 | peripheral.delegate = self
136 | self.peripherals.append(peripheral)
137 | self.rssiDB.append(NSNumber())
138 | self.advertisementDatas.append(oneAdvertisement( array: ["none": "none"]))
139 |
140 | self.connect(peripheral: peripheral)
141 | }
142 | // if peripherals.count > 0 {
143 | // log("Peripheral found, nb = \(peripherals.count)")
144 | //
145 | // let peripheral = peripherals[0]
146 | // peripheral.delegate = self
147 | // self.peripherals.append(peripheral)
148 | // self.rssiDB.append(NSNumber())
149 | // self.advertisementDatas.append(oneAdvertisement( array: ["none": "none"]))
150 | //
151 | // if getState(peripheral.state) == "connected" {
152 | // log("connection to peripheral")
153 | // self.connect(peripheral: peripheral)
154 | // }
155 | // }
156 | }
157 |
158 | }
159 | }
160 |
161 |
162 |
163 | // delegate
164 | //
165 | // 3°) peripheral discovered.
166 | //
167 | func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
168 |
169 | // if it is already added -> return
170 | if (peripherals.contains(peripheral)) {
171 | return
172 | }
173 |
174 | var name = peripheral.name
175 | if name == nil {
176 | name = peripheral.identifier.uuidString
177 | }
178 |
179 | log("-- Peripheral discovered")
180 | log("-- Peripheral name - status : " + Tools.toString(name) + " - " + getState(peripheral.state))
181 | log("-- Peripheral uuid: " + peripheral.identifier.uuidString)
182 | log("-- Peripheral adv : " + String(describing: advertisementData))
183 |
184 | // save first level of signal
185 | rssiDB.append( RSSI )
186 | // Add the peripheral to the list
187 | peripherals.append(peripheral)
188 | // save advertisement datas
189 | if advertisementData.count > 0 {
190 | log("-- Peripheral adverstisement")
191 | log(advertisementData)
192 | // can be : ["kCBAdvDataIsConnectable": 1, "kCBAdvDataTxPowerLevel": 8, "kCBAdvDataLocalName": PTCmajcoghaoncp]
193 | self.advertisementDatas.append(oneAdvertisement(array: advertisementData))
194 | }
195 | else {
196 |
197 | self.advertisementDatas.append(oneAdvertisement( array: ["none": "none"])) //key: "none", value: "none"))
198 | }
199 |
200 | }
201 |
202 |
203 |
204 | }
205 |
--------------------------------------------------------------------------------
/Central Manager/SharedCode/BLE Stack/BLE_ReadWrite.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BLE_ReadWrite.swift
3 | // BlueToothCentral
4 | //
5 | // Created by Olivier Robin on 07/11/2016.
6 | // Copyright © 2016 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | //import UIKit
11 | import CoreBluetooth
12 |
13 |
14 |
15 | extension BLECentralManager {
16 |
17 |
18 |
19 | // read signal RSSI DB from peripheral
20 | //
21 | func readSignal(completion:@escaping (_ value: String, _ error: String) -> Void) {
22 |
23 | bleError = ""
24 | rssiDBConnected = nil
25 |
26 | // read battery level of peripheral
27 | peripheralConnected?.readRSSI()
28 |
29 | DispatchQueue.global(qos: .background).async {
30 |
31 | repeat {
32 | usleep(200000) // Sleep for 0.1s
33 | }
34 | while self.rssiDBConnected == nil && self.bleError == ""
35 |
36 | let str = Tools.toString(self.rssiDBConnected?.stringValue)
37 | completion(str, self.bleError)
38 | }
39 |
40 | }
41 |
42 |
43 |
44 |
45 |
46 | // read value from peripheral
47 | //
48 | func readValue(characUUID: String) {
49 |
50 | log("readValueAsync request : " + characUUID)
51 | bleError = ""
52 | valueReadBytes = []
53 |
54 | // read battery level of peripheral
55 | peripheralConnected?.services?.forEach({ (oneService) in
56 | oneService.characteristics?.forEach({ (oneCharacteristic) in
57 | if oneCharacteristic.uuid == CBUUID(string: characUUID) {
58 | peripheralConnected?.readValue(for: oneCharacteristic)
59 | log("read request sent")
60 | }
61 | })
62 | })
63 | }
64 |
65 |
66 |
67 | // Write a value to peripheral
68 | //
69 | func writeValue(characUUID: String, message: String) {
70 |
71 | log("writeValue request : " + characUUID)
72 | bleError = ""
73 | valueWritten = false
74 |
75 | peripheralConnected?.services?.forEach({ (oneService) in
76 | oneService.characteristics?.forEach({ (oneCharacteristic) in
77 |
78 | if oneCharacteristic.uuid == CBUUID(string: characUUID) {
79 |
80 | let data: Data = message.data(using: String.Encoding.utf16)!
81 | peripheralConnected?.writeValue(data, for: oneCharacteristic, type: CBCharacteristicWriteType.withResponse)
82 | log("write request sent to peripheral")
83 | }
84 | })
85 | })
86 |
87 | }
88 |
89 |
90 |
91 |
92 | // Write a value to peripheral
93 | //
94 | func writeValueAsync(characUUID: String, value: Data) {
95 | log("writeValue async request : " + characUUID )
96 | bleError = ""
97 | valueWritten = false
98 |
99 | peripheralConnected?.services?.forEach({ (oneService) in
100 | oneService.characteristics?.forEach({ (oneCharacteristic) in
101 |
102 | if oneCharacteristic.uuid == CBUUID(string: characUUID) {
103 | //appController.log("sending value to remote peripheral")
104 | peripheralConnected?.writeValue(value, for: oneCharacteristic, type: CBCharacteristicWriteType.withResponse)
105 | }
106 | })
107 | })
108 |
109 | usleep(10000) // Sleep for 0.01 sec
110 | }
111 |
112 |
113 |
114 |
115 | // delegate
116 | // data read from remote peripheral, or error
117 | //
118 | func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?){
119 |
120 | if error != nil {
121 | bleError = ("Error didUpdateValueFor : " + error!.localizedDescription)
122 | }
123 | else {
124 |
125 | let array = [UInt8](characteristic.value!)
126 | var str = ""
127 | for b in array {
128 | str += String(describing: b)
129 | }
130 |
131 | str = String.init(data: characteristic.value!, encoding: .utf16)!
132 |
133 | log("didUpdateValueFor characteristic : " + characteristic.uuid.uuidString + " -- " + str)
134 |
135 | // Save the value
136 | valueReadBytes = array
137 |
138 | bleDelegate?.valueRead(message: str)
139 | }
140 | }
141 |
142 |
143 |
144 | // delegate
145 | // data written to peripheral, with response from peripheral (ok, or error)
146 | //
147 | func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
148 |
149 | if error != nil {
150 | bleError = ("Error didWriteValueFor : " + error!.localizedDescription)
151 | }
152 | else {
153 | //appController.log("didWriteValueFor" )
154 | valueWritten = true
155 | }
156 | }
157 |
158 |
159 |
160 | // delegate
161 | // RSSI was read : signal power
162 | //
163 | func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) {
164 |
165 | if error != nil {
166 | log(("Error didReadRSSI : \(error!.localizedDescription)"))
167 | }
168 | else {
169 | log("didReadRSSI: " + Tools.toString(RSSI.stringValue))
170 | }
171 |
172 | rssiDBConnected = RSSI
173 | let index = peripherals.firstIndex(of: peripheral)
174 | if index != nil {
175 | rssiDB[index!] = RSSI
176 | }
177 | }
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/Central Manager/SharedCode/BLE Stack/BLE_ServicesCharacteristics.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BLECentralManager+ServicesCharacteristics.swift
3 | // BlueToothCentral
4 | //
5 | // Created by Olivier Robin on 07/11/2016.
6 | // Copyright © 2016 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreBluetooth
11 | //import UIKit
12 | //import UserNotifications
13 |
14 |
15 |
16 | extension BLECentralManager {
17 |
18 |
19 | // allow the discover services and characteristics of the peripharal already connected
20 | //
21 | func discoverServicesAndCharac() {
22 | bleError = ""
23 | servicesDiscovered.removeAll()
24 | peripheralConnected?.discoverServices( nil )
25 | }
26 |
27 |
28 |
29 | func setNotify(characteristicUUID: String) {
30 |
31 | peripheralConnected?.services?.forEach({ (oneService) in
32 | oneService.characteristics?.forEach({ (oneCharacteristic) in
33 | if oneCharacteristic.uuid == CBUUID(string: characteristicUUID) {
34 | peripheralConnected?.setNotifyValue(true, for: oneCharacteristic)
35 | log("request notification done")
36 | }
37 | })
38 | })
39 |
40 | }
41 |
42 |
43 |
44 |
45 | // delegate
46 | // Discovered service of the peripheral
47 | //
48 | func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
49 |
50 | if error != nil {
51 | bleError = ("Error discovering services: \(error!.localizedDescription)")
52 | log(bleError)
53 | }
54 | else {
55 | log("didDiscoverServices services")
56 | }
57 |
58 | peripheral.services?.forEach({ (oneService) in
59 | log("------ Service : " + Tools.toString( oneService.uuid.uuidString ))
60 | // discopver the characteristics for that service
61 | peripheral.discoverCharacteristics(nil, for: oneService)
62 | })
63 |
64 | }
65 |
66 |
67 |
68 | // delegate
69 | // discovered characteristics for a service
70 | //
71 | func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?){
72 | if error != nil {
73 | bleError = ("Error service characteristics: \(error!.localizedDescription)")
74 | log(bleError)
75 | }
76 | else {
77 | log("didDiscoverCharacteristicsFor")
78 | }
79 |
80 | if !servicesDiscovered.contains(service) {
81 | servicesDiscovered.append(service)
82 | }
83 |
84 | // enumerate all the characteristics of this service
85 | service.characteristics?.forEach({ (oneCharacteristic) in
86 | log("-- Characteristic : " + Tools.toString( oneCharacteristic.uuid.uuidString ))
87 | })
88 |
89 | isServicesDiscovered = true
90 |
91 | }
92 |
93 |
94 |
95 | // delegate
96 | // tell if notification when we request a notification for a charac.
97 | //
98 | func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
99 | if error != nil {
100 | bleError = ("Error didUpdateNotificationStateFor : " + error!.localizedDescription)
101 | }
102 | else {
103 | log("didUpdateNotificationStateFor characteristic : " + characteristic.uuid.uuidString )
104 | }
105 | }
106 |
107 |
108 |
109 |
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/Central Manager/SharedCode/BLE Stack/BLE_Tools.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BLE_Tools.swift
3 | //
4 | //
5 | // Created by Olivier Robin on 01/03/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreBluetooth
11 |
12 | extension BLECentralManager {
13 |
14 |
15 | // return bluetooth status
16 | //
17 | func getBlueToothStatus() -> Bool? {
18 | return blueToothEnabled
19 | }
20 |
21 |
22 |
23 | // read RSSI DB for a peripehral discovered
24 | //
25 | func getPeripheralRSSI(peripheralName: String) ->NSNumber? {
26 | if peripherals.count > 0 {
27 | for index in 0...peripherals.count - 1 {
28 | let name = getPeripheralName(number: index)
29 | if name == peripheralName {
30 | //return peripherals[index].identifier.uuidString
31 | let RSSI = rssiDB[index]
32 | return RSSI
33 | }
34 | }
35 | }
36 | return nil
37 | }
38 |
39 |
40 |
41 | func isDeviceFound(uuidName: String) ->Bool {
42 | log("is devie found")
43 |
44 | if peripherals.count > 0 {
45 | log("devices :")
46 | for i in 0...peripherals.count - 1 {
47 | let n = getPeripheralUUID(number: i)
48 | log("device : \(n)")
49 | if n == uuidName {
50 | return true
51 | }
52 | }
53 | } else {
54 | log("no BLE devices found")
55 | }
56 |
57 | return false
58 | }
59 |
60 |
61 | //
62 | // // Return peripheral UUID
63 | // //
64 | // func getPeripheralUUID(number: Int) -> String {
65 | //
66 | // let peripheral = peripherals[number]
67 | // let name = peripheral.identifier.uuidString
68 | // return name
69 | // }
70 |
71 |
72 | //REturn peripheral name
73 | func getShortPeripheralName(number: Int) -> String {
74 |
75 | let peripheral = peripherals[number]
76 | var name = peripheral.name //.replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "\t", with: "")
77 | if name == nil {
78 | name = peripheral.identifier.uuidString
79 | }
80 |
81 | return name!
82 | }
83 |
84 |
85 | //Return peripheral name
86 | //
87 | func getPeripheralUUID(number: Int) -> String {
88 |
89 | //name = name?.replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "\t", with: "")
90 | // if there is an advertisement with a local name, use it.
91 | let a = advertisementDatas[number]
92 |
93 | if a.array["kCBAdvDataServiceUUIDs"] != nil {
94 | let uuid = a.array["kCBAdvDataServiceUUIDs"] as! NSArray
95 |
96 | let str = String(describing: uuid[0])
97 | return str
98 | } else {
99 | return "advertisement issue. got " + String(describing: a.array)
100 | }
101 | // return ""
102 | }
103 |
104 | //Return peripheral name
105 | //
106 | func getPeripheralName(number: Int) -> String {
107 |
108 | let peripheral = peripherals[number]
109 | var name = peripheral.name
110 | if name != nil {
111 |
112 | //name = name?.replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "\t", with: "")
113 | // if there is an advertisement with a local name, use it.
114 | let a = advertisementDatas[number]
115 | if a.array["kCBAdvDataLocalName"] != nil {
116 | name = a.array["kCBAdvDataLocalName"] as! String?
117 | }
118 | }
119 | else {
120 | name = peripheral.identifier.uuidString
121 | }
122 | return name!
123 | }
124 |
125 |
126 | func getPeripheralIdentifier(peripheralName: String) ->String {
127 | if peripherals.count > 0 {
128 | for index in 0...peripherals.count - 1 {
129 |
130 | let name = getPeripheralName(number: index)
131 | if name == peripheralName {
132 | return peripherals[index].identifier.uuidString
133 | }
134 | }
135 | }
136 | return ""
137 | }
138 |
139 |
140 |
141 | // return a peripheral state, in string, easily readable.
142 | func getState(_ state: CBPeripheralState) -> String {
143 | if state == .connected {
144 | return "connected"
145 | }
146 | if state == .connecting {
147 | return "connecting"
148 | }
149 | if state == .disconnected {
150 | return "disconnected"
151 | }
152 | if state == .disconnecting {
153 | return "disconnecting"
154 | }
155 | return ""
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/Central Manager/SharedCode/BluetoothController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BluetoothController.swift
3 | // CentralManager
4 | //
5 | // Created by Olivier Robin on 04/03/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | //import UIKit
11 |
12 |
13 | class BluetoothController: BLEProtocol {
14 |
15 |
16 |
17 | var appDelegate:AppDelegate? = nil
18 |
19 |
20 | var isConnected = false
21 | var isConnecting = false
22 | var disconnectEvent = false
23 |
24 | var bleError = ""
25 |
26 | // bluetooth Stack
27 | var ble = BLECentralManager()
28 |
29 | // used to measure time and manage timeout
30 | var start = Date()
31 | var end = Date()
32 | var time = TimeInterval()
33 |
34 | var viewControllerDelegate: BLEProtocol? = nil
35 |
36 |
37 | func log(_ object: Any?) {
38 | appDelegate?.singleton.logger.log(object)
39 | }
40 |
41 |
42 |
43 | func restoreCentralManager(viewControllerDelegate: BLEProtocol, centralName: String) {
44 | //appDelegate = UIApplication.shared.delegate as! AppDelegate?
45 |
46 | self.viewControllerDelegate = viewControllerDelegate
47 |
48 | // Start the central manager. it will fire the willRestore Event
49 | self.ble.initCentralManager(bleDelegate: self)
50 | }
51 |
52 |
53 |
54 | //--------------------------------------------------------------------------------------------------------------
55 |
56 | // start central manager
57 | // do it in a separate thread than UI !
58 | //
59 | func startCentralManager(viewControllerDelegate: BLEProtocol) -> Bool {
60 |
61 | //appDelegate = UIApplication.shared.delegate as! AppDelegate?
62 | self.viewControllerDelegate = viewControllerDelegate
63 |
64 | //let controller = UIApplication.shared.keyWindow?.rootViewController
65 |
66 | isConnected = false
67 | isConnecting = false
68 | disconnectEvent = false
69 |
70 | //mainMenuDelegate?.displayText(message: "Recherche de votre device")
71 | log("----------------------------------------------------------------------")
72 | log("startCentralManager")
73 |
74 | // start the central manager
75 | self.ble.appDelegate = self.appDelegate
76 | self.ble.initCentralManager(bleDelegate: self )
77 |
78 | // centralmanager will fire an event : bluetooth on, or off
79 | let b = self.waitBluetoothStatus()
80 | if !b {
81 | return false
82 | }
83 |
84 | // // start to scan for peripherals
85 | // self.ble.startScan()
86 |
87 | log("startCentralManager completed")
88 | return true
89 | }
90 |
91 | func stopCentralManager() {
92 | self.ble.stop()
93 | }
94 |
95 | // wait for bluetooth status is true or false
96 | //
97 | func waitBluetoothStatus() -> Bool {
98 |
99 | repeat {
100 | usleep(500000)
101 | }
102 | while ble.getBlueToothStatus() == nil
103 |
104 | return ble.getBlueToothStatus()!
105 | }
106 |
107 |
108 |
109 | // search in peripherals foudn is there is one or more "PTC..." device
110 | // Search for 6 second, at least, to be sure all device are visible durting the scan
111 | func searchDevice(uuidName: String) -> Bool {
112 |
113 | log("----------------------------------------------------------------------")
114 | log("Search for Device: " + uuidName)
115 |
116 | // start to scan for peripherals
117 | self.ble.startScan(uid: uuidName)
118 |
119 | self.start = Date()
120 | repeat {
121 | usleep(500000)
122 | self.end = Date()
123 | self.time = self.end.timeIntervalSince(self.start) // * 1000
124 | }
125 | while self.time.second < 1 || (self.time.second >= 1 && !ble.isDeviceFound(uuidName: uuidName) && self.time.second < 20 )
126 |
127 | if self.time.second >= 20 {
128 | log("searchDevice timeout")
129 | return false
130 | }
131 | return true
132 | }
133 |
134 |
135 |
136 |
137 | // discover service and charac. with time out.
138 | func discoverServices() -> Bool {
139 | log("discovering services and Charac.")
140 |
141 | start = Date()
142 | self.ble.discoverServicesAndCharac()
143 |
144 | repeat {
145 | usleep(500000) // Sleep for 0.5s
146 | }
147 | while !self.ble.isServicesDiscovered && self.ble.bleError == ""
148 | //completion(self.bleError) }
149 |
150 | if ble.bleError == "" {
151 | return true
152 | }
153 | else {
154 | return false
155 | }
156 | }
157 |
158 |
159 |
160 | // connect to a device, with a time out
161 | //
162 | func connectToDevice(uuid: String) -> Bool {
163 | isConnected = false
164 |
165 | log("----------------------------------------------------------------------")
166 | log("connecting to peripheral ...")
167 | start = Date()
168 | self.ble.connectToAsync(serviceUUID: uuid)
169 |
170 | repeat {
171 | usleep(300000) // Sleep for 0.3s
172 | end = Date()
173 | time = end.timeIntervalSince(start)// * 1000
174 | }
175 | while !isConnected && time.second < 60
176 |
177 | bleError = ble.bleError
178 | if bleError == "" {
179 | bleError = "Timeout"
180 | }
181 | if !isConnected {
182 | log("connection failed.")
183 | //ble.disconnect()
184 | return false
185 | }
186 | else {
187 | log("connection to peripheral is done")
188 | return true
189 | }
190 | }
191 |
192 |
193 | func read(uuid: String) {
194 | ble.readValue(characUUID: uuid)
195 | }
196 |
197 | func write(uuid: String, message: String) {
198 | ble.writeValue(characUUID: uuid, message: message)
199 | }
200 |
201 | func writeAsync(uuid: String, message: String) {
202 | ble.writeValueAsync(characUUID: uuid, value: message.data(using: .utf16)!)
203 | }
204 |
205 | func requestNotify(uuid: String) {
206 | ble.setNotify(characteristicUUID: uuid)
207 | }
208 |
209 |
210 |
211 | // protocol events
212 |
213 |
214 | // called by bluetooth stack
215 | //
216 | func valueRead(message: String) {
217 | log("valueRead : " + message)
218 |
219 | viewControllerDelegate?.valueRead(message: message)
220 | }
221 |
222 | func valueWrite(message: String) {
223 | log("valueRead : " + message)
224 |
225 | viewControllerDelegate?.valueWrite(message: message)
226 | }
227 |
228 |
229 |
230 |
231 | // disconnected : ble is too far away, or sitched off
232 | func disconnected(message: String) {
233 | log("disconnect event")
234 |
235 | if let delegate = self.appDelegate {
236 | if !delegate.singleton.appRestored {
237 | if !isConnected {
238 | // if connection fail, this event can be fired even if we are not already connected
239 | log("connection failed. is not connected")
240 | return
241 | }
242 | }
243 |
244 | isConnected = false
245 | disconnectEvent = true // allow to know that we are not connected, and there is an event.
246 |
247 | // Will display some message
248 | viewControllerDelegate?.disconnected(message: message)
249 | } else {
250 | log("delegate no initalized !")
251 | }
252 | }
253 |
254 |
255 |
256 | // connection to ble failed
257 | func failConnected(message: String) {
258 | log("FailConnect event")
259 |
260 | isConnected = false
261 |
262 | // Will display some message
263 | viewControllerDelegate?.failConnected(message: message)
264 | }
265 |
266 |
267 |
268 | func connected(message: String) {
269 | log("device connected event : " + message)
270 | isConnected = true
271 |
272 | // Will display some message
273 | viewControllerDelegate?.connected(message: message)
274 | }
275 |
276 |
277 |
278 |
279 | }
280 |
281 |
282 |
--------------------------------------------------------------------------------
/Central Manager/SharedCode/Datas.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Advertisement.swift
3 | // CentralManager
4 | //
5 | // Created by Olivier Robin on 04/03/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 |
13 | class oneAdvertisement {
14 | var array: [String: Any]
15 |
16 | init(array: [String : Any]) {
17 | self.array = array
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/Central Manager/SharedCode/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Extensions.swift
3 | //
4 | //
5 | // Created by Olivier Robin on 08/02/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 |
13 |
14 | extension TimeInterval {
15 | var minuteSecond: String {
16 | return String(format:"%d m %02d s", minute, second)
17 | // return String(format:"%d:%02d.%03d", minute, second, millisecond)
18 | }
19 | var SecondMS: String {
20 | //return String(format:"%d min %02d sec", minute, second)
21 | return String(format:"%d m %02d s %03d", minute, second, millisecond)
22 | }
23 |
24 | var minute: Int {
25 | return Int((self/60.0).truncatingRemainder(dividingBy: 60))
26 | }
27 | var second: Int {
28 | return Int(self.truncatingRemainder(dividingBy: 60))
29 | }
30 | var millisecond: Int {
31 | return Int((self*1000).truncatingRemainder(dividingBy: 1000) )
32 | }
33 | }
34 |
35 | extension Int {
36 | var msToSeconds: Double {
37 | return Double(self) / 1000
38 | }
39 | }
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Central Manager/SharedCode/Log.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Log.swift
3 | // CentralManager
4 | //
5 | // Created by Olivier Robin on 04/03/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | class Log {
13 |
14 | let shared = UserDefaults(suiteName: "group.fr.ormaa")
15 | var logStr = ""
16 |
17 | // Delete log file
18 | func deleteLog() {
19 | shared?.removeObject(forKey: "group.fr.ormaa.central.log")
20 | }
21 |
22 | // save log to nsUserDefault.
23 | // This allow to use another app, with same group, in order to read what heppen
24 | // when this app is in background for example !
25 | //
26 | func saveLog(value: String) {
27 | shared?.setValue(value + "\n", forKey: "group.fr.ormaa.central.log")
28 | }
29 |
30 | // log an object
31 | //
32 | func log(_ text: Any?) {
33 |
34 | if text == nil {
35 |
36 | }
37 | else {
38 | let str = getDate() + " - " + String(describing: text! as Any)
39 | print(str)
40 |
41 | logStr += str + "\n"
42 | saveLog(value: logStr)
43 | }
44 | }
45 |
46 | // Format date, with milli seconds, and local time
47 | //
48 | func getDate() -> String {
49 | let date = Date()
50 | let formatter = DateFormatter()
51 | formatter.timeStyle = .medium
52 | formatter.dateStyle = .short
53 | formatter.timeZone = TimeZone.autoupdatingCurrent
54 | formatter.dateFormat = "y-MM-dd -- H:m:ss.SSSS"
55 | let dateStr = formatter.string(from: date)
56 | return dateStr
57 | }
58 |
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2015 - 2016 - 2017 ormaa - olivier robin
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 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/project.xcworkspace/xcuserdata/olivier.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/Peripheral Manager/BlueToothPeripheral.xcodeproj/project.xcworkspace/xcuserdata/olivier.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/UserInterfaceState (2017-01-08T11_16_26.629).xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/Peripheral Manager/BlueToothPeripheral.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/UserInterfaceState (2017-01-08T11_16_26.629).xcuserstate
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/UserInterfaceState (2017-01-08T11_30_12.633).xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/Peripheral Manager/BlueToothPeripheral.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/UserInterfaceState (2017-01-08T11_30_12.633).xcuserstate
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/Peripheral Manager/BlueToothPeripheral.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/xcdebugger/Expressions.xcexplist:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
10 |
11 |
12 |
13 |
15 |
16 |
18 |
19 |
20 |
21 |
23 |
24 |
26 |
27 |
28 |
29 |
31 |
32 |
34 |
35 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/xcuserdata/olivier.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/xcuserdata/olivier.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | BlueToothPeripheral.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 | BlueToothPeripheral.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 1
16 |
17 | BlueToothPeripheral_MACOS.xcscheme
18 |
19 | orderHint
20 | 1
21 |
22 | BlueToothPeripheral_MACOS.xcscheme_^#shared#^_
23 |
24 | orderHint
25 | 0
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/xcuserdata/ormaa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
20 |
21 |
22 |
24 |
36 |
37 |
38 |
40 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/xcuserdata/ormaa.xcuserdatad/xcschemes/BlueToothPeripheral.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 |
69 |
70 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral.xcodeproj/xcuserdata/ormaa.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | BlueToothPeripheral.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 1CFB9B2F1DC5EA6800C51C7E
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral/BLEPeripheralManager (2017-01-08T17_36_04.875).swift:
--------------------------------------------------------------------------------
1 | //
2 | // BLECentralManager.swift
3 | // BlueToothCentral
4 | //
5 | // Created by Olivier Robin on 30/10/2016.
6 | // Copyright © 2016 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreBluetooth
11 | import UIKit
12 | import UserNotifications
13 |
14 | import AELog
15 | import AEConsole
16 |
17 | // UUID of this peripheral service, and characteristics
18 | // can be generated using "uuidgen" under osx console
19 |
20 | let BLEGenericService = "1B0986D4-2B1C-4DF7-A998-D86DFD794AE6" //"A180"
21 | let BLERead = "2AC6A7BF-2C60-42D0-8013-AECEF2A124C0" //"A181"
22 | let BLEWrite = "9B89E762-226A-4BBB-A381-A4B8CC6E1105"
23 |
24 |
25 | protocol BLEPeripheralProtocol {
26 | func BlueToothStatus(peripheral: CBPeripheralManager)
27 | }
28 |
29 |
30 | // BlueTooth do not work if it not instanciated, called, managed, from a uiview controller, linked to a .xib, or storyboard
31 | //
32 | class BLEPeripheralManager : NSObject, CBPeripheralManagerDelegate {
33 |
34 | var localPeripheralManager: CBPeripheralManager! = nil
35 | var localService:CBService? = nil
36 | var localPeripheral:CBPeripheral? = nil
37 |
38 | // try to save the service created, inorder to be sure it is not emoved from memory
39 | // TODO check if useful
40 | var createdService:CBService? = nil
41 |
42 | var peripheralDiscoverableName = ""
43 |
44 | var powerOn = false
45 |
46 | // timer used to retry to scan for peripheral, when we don't find it
47 | var rescanTimer: Timer?
48 |
49 | var delegate: BLEPeripheralProtocol?
50 |
51 |
52 | // 1°) start CentralManager
53 | //
54 | func startBLEPeripheral(name: String) {
55 | peripheralDiscoverableName = name
56 |
57 | aelog("start Peripheral")
58 | aelog("Discoverable name : " + name)
59 |
60 | // start the Bluetooth periphal manager
61 | localPeripheralManager = CBPeripheralManager(delegate: self, queue: nil)
62 | }
63 |
64 | // Stop advertising.
65 | // TODO : close the comunication ???
66 | //
67 | func stopBLEPeripheral() {
68 | aelog("Stop advertising")
69 | aelog("Stop peripheral Service")
70 |
71 | stopServices()
72 | }
73 |
74 |
75 |
76 |
77 |
78 | // Stop service, when powered off
79 | //
80 | func stopServices() {
81 | aelog("Stopping BLE services...")
82 | self.localPeripheralManager.removeAllServices()
83 | self.localPeripheralManager.stopAdvertising()
84 | }
85 |
86 |
87 |
88 |
89 |
90 | // delegate
91 | //
92 | // 2°) Receive some update from bluetooth state : when device is set on, or off
93 | //
94 | func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager)
95 | {
96 | if (peripheral.state == CBManagerState.poweredOff) {
97 | // Bluetooth peripheral is off, sto all services
98 | aelog("peripheral is off")
99 | self.powerOn = false
100 | self.stopServices()
101 | }
102 | else {
103 | // Bluetooth peripheral is on, will start the services
104 | aelog("peripheral is on")
105 | self.powerOn = true
106 | self.startServices()
107 | }
108 |
109 | // Call delegate for parent (viewController ???)
110 | //Tools.sendNotification(name: "BlueToothStatusUpdate", objectName: "peripheral", object: peripheral)
111 |
112 | // Send the status to delegate subscribed
113 | if delegate != nil {
114 | delegate?.BlueToothStatus(peripheral: peripheral)
115 | }
116 | }
117 |
118 |
119 | // 3°) Create 1 service
120 | // 2 Characteristics : 1 for read, 1 for write. or 1 characteristic read/write
121 | //
122 | func startServices() {
123 | aelog("starting services")
124 | aelog("Service UUID: " + BLEGenericService)
125 | aelog("Characteristic: read UUID: " + BLERead)
126 | aelog("Characteristic: write UUID: " + BLEWrite)
127 |
128 | // read characteristic
129 | let characteristic1UUID = CBUUID(string: BLERead)
130 | let properties: CBCharacteristicProperties = [.read, ] //.notify,
131 | let permissions: CBAttributePermissions = [.readable]
132 | let characteristic1 = CBMutableCharacteristic(type: characteristic1UUID, properties: properties, value: nil, permissions: permissions)
133 |
134 | // write characteristic
135 | let characteristic2UUID = CBUUID(string: BLEWrite)
136 | let properties2: CBCharacteristicProperties = [.write]
137 | let permissions2: CBAttributePermissions = [.writeable]
138 | let characteristic2 = CBMutableCharacteristic(type: characteristic2UUID, properties: properties2, value: nil, permissions: permissions2)
139 |
140 | // define the service
141 | let serviceUUID = CBUUID(string: BLEGenericService)
142 | let service = CBMutableService(type: serviceUUID, primary: true)
143 |
144 | // Create the service, add the characteristic to it
145 | service.characteristics = [characteristic1, characteristic2]
146 | createdService = service
147 |
148 | // push the service to the peripheral
149 | localPeripheralManager.add(service)
150 | }
151 |
152 |
153 |
154 | // 4°) service + Charactersitic added to peripheral
155 | //
156 | func peripheralManager(_ peripheral: CBPeripheralManager,
157 | didAdd service: CBService,
158 | error: Error?){
159 |
160 | if error != nil {
161 | aelog(("Error adding services: \(error?.localizedDescription)"))
162 | }
163 | else {
164 | aelog("service added to peripheral")
165 |
166 | // Save service locally, used later to advertise
167 | localService = service
168 |
169 | //let identifier = Bundle.main.bundleIdentifier!
170 | //aelog("Bundle Identifier: " + Tools.toString(identifier))
171 | //let manufacturerData = identifier.data( using: String.Encoding.utf8, allowLossyConversion: false)
172 | // let advertisement: [String : Any] = CBAdvertisementDataManufacturerDataKey : manufacturerData,
173 | // CBAdvertisementDataServiceUUIDsKey : [service.uuid]]
174 |
175 | // Create an advertisement, using the service UUID
176 | let advertisement: [String : Any] = [CBAdvertisementDataLocalNameKey: self.peripheralDiscoverableName,
177 | //CBAdvertisementDataManufacturerDataKey : manufacturerData,
178 | CBAdvertisementDataServiceUUIDsKey : [service.uuid]]
179 | // start the advertisement
180 | self.localPeripheralManager.startAdvertising(advertisement)
181 |
182 | aelog("Starting to advertise.")
183 | }
184 | }
185 |
186 |
187 | // Advertising done
188 | //
189 | func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager,
190 | error: Error?){
191 | if error != nil {
192 | aelog(("Error while advertising: \(error?.localizedDescription)"))
193 | }
194 | else {
195 | aelog("adversiting done. no error")
196 | }
197 | //peripheral.stopAdvertising()
198 | }
199 |
200 |
201 | // called when CBCentral manager request to read
202 | //
203 | func peripheralManager(_ peripheral: CBPeripheralManager,
204 | didReceiveRead request: CBATTRequest) {
205 |
206 | aelog("CB Central Manager request from central: ")
207 | print(request)
208 |
209 | // if request.characteristic.UUID.isEqual(characteristic.UUID)
210 | // {
211 | // // Set the correspondent characteristic's value
212 | // // to the request
213 | // request.value = characteristic.value
214 | //
215 | // // Respond to the request
216 | // localPeripheralManager.respond(
217 | // to: request,
218 | // withResult: .success)
219 | // }
220 | //peripheral.respond(to: request, withResult: CBATTError.success)
221 | }
222 |
223 |
224 | // called when central manager write value to peripheral manager
225 | // a popup appear before that, asking to pair device : "jumelage avec ..."
226 | //
227 | public func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
228 | aelog("CB Central Manager request write from central: ")
229 |
230 | if requests.count > 0 {
231 | let str = NSString(data: requests[0].value!, encoding:String.Encoding.utf8.rawValue)!
232 | aelog("value sent by central Manager : " + String(describing: str))
233 | }
234 | }
235 |
236 | func respond(to request: CBATTRequest, withResult result: CBATTError.Code) {
237 | print("respnse requested")
238 | }
239 |
240 |
241 |
242 |
243 | // func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest)
244 | // {
245 | // if request.characteristic.UUID.isEqual(characteristic.UUID)
246 | // {
247 | // // Set the correspondent characteristic's value
248 | // // to the request
249 | // request.value = characteristic.value
250 | //
251 | // // Respond to the request
252 | // peripheralManager.respondToRequest(
253 | // request,
254 | // withResult: .Success)
255 | // }
256 | // }
257 |
258 |
259 |
260 | // // Every xxx second : if no peripheral == peripheralSearchedName is found, retry to scan
261 | // rescanTimer = Timer.scheduledTimer(timeInterval: 3.0, target: self,
262 | // selector: #selector(BLEPeripheralManager.refreshAdvertising), userInfo: nil, repeats: true)
263 |
264 | // // will refresh : To be defined
265 | // func refreshAdvertising() {
266 | // DispatchQueue.main.async {
267 | // if self.localService != nil && self.powerOn {
268 | // //Logs.info(message: "Advertise as: " + self.peripheralDiscoverableName)
269 | // //let advertisement: [String : Any] = [self.peripheralDiscoverableName : BLEGenericService]
270 | //
271 | // //self.localPeripheralManager.startAdvertising(advertisement)
272 | // }
273 | //
274 | // }
275 | // }
276 |
277 |
278 |
279 |
280 | }
281 |
282 |
283 |
284 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral/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 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
46 |
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 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSBluetoothPeripheralUsageDescription
6 | need to use bluetooth
7 | NSBluetoothAlwaysUsageDescription
8 | need to use bluetooth always
9 | CFBundleDevelopmentRegion
10 | en
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | 1.0
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | UIBackgroundModes
28 |
29 | bluetooth-peripheral
30 |
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Main
35 | UIRequiredDeviceCapabilities
36 |
37 | armv7
38 |
39 | UISupportedInterfaceOrientations
40 |
41 | UIInterfaceOrientationPortrait
42 |
43 | UISupportedInterfaceOrientations~ipad
44 |
45 | UIInterfaceOrientationPortrait
46 | UIInterfaceOrientationPortraitUpsideDown
47 | UIInterfaceOrientationLandscapeLeft
48 | UIInterfaceOrientationLandscapeRight
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral/Supporting Files/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // BlueToothPeripheral
4 | //
5 | // Created by Olivier Robin on 30/10/2016.
6 | // Copyright © 2016 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import UserNotifications
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | var window: UIWindow?
16 |
17 |
18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
19 | // Override point for customization after application launch.
20 |
21 |
22 | // let center = UNUserNotificationCenter.current()
23 | // let options: UNAuthorizationOptions = [.alert, .sound];
24 | // center.requestAuthorization(options: options) {
25 | // (granted, error) in
26 | // if !granted {
27 | // print("Something went wrong")
28 | // }
29 | // }
30 |
31 |
32 | return true
33 | }
34 |
35 | func applicationWillResignActive(_ application: UIApplication) {
36 | // 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.
37 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
38 | }
39 |
40 | func applicationDidEnterBackground(_ application: UIApplication) {
41 | // 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.
42 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
43 | }
44 |
45 | func applicationWillEnterForeground(_ application: UIApplication) {
46 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
47 | }
48 |
49 | func applicationDidBecomeActive(_ application: UIApplication) {
50 | // 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.
51 | }
52 |
53 | func applicationWillTerminate(_ application: UIApplication) {
54 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
55 | }
56 |
57 |
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral/Supporting Files/Assets.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 | }
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral/Supporting Files/Base.lproj/LaunchScreen.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 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // BlueToothPeripheral
4 | //
5 | // Created by Olivier Robin on 30/10/2016.
6 | // Copyright © 2016 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreBluetooth
11 | import UserNotifications
12 |
13 |
14 | class MyViewController: UIViewController, BLEPeripheralProtocol {
15 |
16 | @IBOutlet weak var logTextView: UITextView!
17 | @IBOutlet weak var switchPeripheral: UISwitch!
18 |
19 | var refreshLogs: Timer?
20 | var ble: BLEPeripheralManager?
21 |
22 | var timerOn: Bool = false
23 |
24 | override func viewDidLoad() {
25 | super.viewDidLoad()
26 | print("MyViewController viewDidLoad")
27 | }
28 |
29 | override func didReceiveMemoryWarning() {
30 | super.didReceiveMemoryWarning()
31 | // Dispose of any resources that can be recreated.
32 | }
33 |
34 |
35 | // Activate / disActivate the peripheral
36 | @IBAction func switchPeripheralOnOff(_ sender: AnyObject) {
37 |
38 | if self.switchPeripheral.isOn {
39 |
40 | logToScreen("starting peripheral")
41 |
42 | ble = BLEPeripheralManager()
43 | ble?.delegate = self
44 | ble!.startBLEPeripheral()
45 | timerOn = true
46 |
47 | // Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [self] timer in
48 | // print("Timer fired!")
49 | //
50 | // if !self.timerOn {
51 | // timer.invalidate()
52 | // }
53 | // }
54 | }
55 | else {
56 | print("stopping Peripheral")
57 | ble!.stopBLEPeripheral()
58 | timerOn = false
59 | logTextView.text = ""
60 | }
61 |
62 | }
63 |
64 |
65 |
66 |
67 | func logToScreen(_ text: String) {
68 | print(text)
69 |
70 | var str = logTextView.text + "\n"
71 | str += text
72 | logTextView.text = str
73 | }
74 |
75 |
76 | }
77 |
78 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral_MACOS/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // BlueToothPeripheral_MACOS
4 | //
5 | // Created by olivier Robin on 17/07/2018.
6 | // Copyright © 2018 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 |
15 |
16 | func applicationDidFinishLaunching(_ aNotification: Notification) {
17 | // Insert code here to initialize your application
18 | }
19 |
20 | func applicationWillTerminate(_ aNotification: Notification) {
21 | // Insert code here to tear down your application
22 | }
23 |
24 |
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral_MACOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral_MACOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral_MACOS/BlueToothPeripheral_MACOS.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral_MACOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2018 fr.ormaa. All rights reserved.
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Peripheral Manager/BlueToothPeripheral_MACOS/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // BlueToothPeripheral_MACOS
4 | //
5 | // Created by olivier Robin on 17/07/2018.
6 | // Copyright © 2018 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class ViewController: NSViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 |
16 | // Do any additional setup after loading the view.
17 | }
18 |
19 | override var representedObject: Any? {
20 | didSet {
21 | // Update the view, if already loaded.
22 | }
23 | }
24 |
25 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/Peripheral Manager/Shared/BLE_PeripheralManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BLECentralManager.swift
3 | // BlueToothCentral
4 | //
5 | // Created by Olivier Robin on 30/10/2016.
6 | // Copyright © 2016 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreBluetooth
11 | #if os(iOS) || os(watchOS) || os(tvOS)
12 | import UserNotifications
13 | #elseif os(OSX)
14 | import Cocoa
15 | #else
16 | // something else :o)
17 | #endif
18 |
19 |
20 | // Apple documentation :
21 | // https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html
22 |
23 | // UUID of peripheral service, and characteristics
24 | // can be generated using "uuidgen" under osx console
25 |
26 |
27 | let peripheralName = "ORMAA_1.0"
28 | // Service
29 | let BLEService = "00001901-0000-1000-8000-00805f9b34fb" // generic service
30 |
31 | // Characteristics
32 | let CH_READ = "2AC6A7BF-2C60-42D0-8013-AECEF2A124C0"
33 | let CH_WRITE = "9B89E762-226A-4BBB-A381-A4B8CC6E1105"
34 |
35 | let TextToAdvertise = "hey hey dude. what's up?" // < 28 bytes needed.
36 | var TextToNotify = "Notification: " // < 28 bytes needed ???
37 |
38 | // BlueTooth do not work if it not instanciated, called, managed, from a uiview controller, linked to a .xib, or storyboard
39 | //
40 | class BLEPeripheralManager : NSObject, CBPeripheralManagerDelegate {
41 |
42 | // use class variable, instead of variable in function :
43 | // allow to be retained in memory. if not, Swift can get ridd of them when it want.
44 | var localPeripheralManager: CBPeripheralManager! = nil
45 | var localPeripheral:CBPeripheral? = nil
46 | var createdService = [CBService]()
47 |
48 | var notifyCharac: CBMutableCharacteristic? = nil
49 | var notifyCentral: CBCentral? = nil
50 |
51 | // timer used to retry to scan for peripheral, when we don't find it
52 | var notifyValueTimer: Timer?
53 |
54 | // Delegate to a parent.will allow to display thing, or send value
55 | var delegate: BLEPeripheralProtocol?
56 |
57 | var cpt = 0
58 |
59 | // start the PeripheralManager
60 | //
61 | func startBLEPeripheral() {
62 |
63 | delegate?.logToScreen( "startBLEPeripheral")
64 | delegate?.logToScreen( "Discoverable name : " + peripheralName)
65 | delegate?.logToScreen( "Discoverable service :\n" + BLEService)
66 |
67 | // start the Bluetooth periphal manager
68 | localPeripheralManager = CBPeripheralManager(delegate: self, queue: nil)
69 | }
70 |
71 |
72 |
73 | // Stop advertising.
74 | //
75 | func stopBLEPeripheral() {
76 | delegate?.logToScreen( "stopBLEPeripheral")
77 |
78 | self.localPeripheralManager.removeAllServices()
79 | self.localPeripheralManager.stopAdvertising()
80 | }
81 |
82 |
83 |
84 | // delegate
85 | //
86 | // Receive bluetooth state
87 | //
88 | func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager)
89 | {
90 | // get a human readable value :o)
91 | let state = getState(peripheral: peripheral)
92 | delegate!.logToScreen( "peripheralManagerDidUpdateState is : \(state)")
93 |
94 | if peripheral.state == .poweredOn {
95 | self.createServices()
96 | }
97 | else {
98 | delegate?.logToScreen( "cannot create services. state is : " + getState(peripheral: peripheral))
99 | }
100 | }
101 |
102 |
103 |
104 |
105 | // Create 1 service
106 | // 2 Characteristics : 1 for read, 1 for write.
107 | //
108 | func createServices() {
109 | delegate?.logToScreen( "create Services")
110 |
111 | // service
112 | let serviceUUID = CBUUID(string: BLEService)
113 | let service = CBMutableService(type: serviceUUID, primary: true)
114 |
115 | // characteristic
116 | var chs = [CBMutableCharacteristic]()
117 |
118 | // Read characteristic
119 | delegate?.logToScreen( "Charac. read : \n" + CH_READ)
120 | let characteristic1UUID = CBUUID(string: CH_READ)
121 | let properties: CBCharacteristicProperties = [.read, .notify ]
122 | let permissions: CBAttributePermissions = [.readable]
123 | let ch = CBMutableCharacteristic(type: characteristic1UUID, properties: properties, value: nil, permissions: permissions)
124 | chs.append(ch)
125 |
126 | // Write characteristic
127 | delegate?.logToScreen( "Charac. write : \n" + CH_WRITE)
128 | let characteristic2UUID = CBUUID(string: CH_WRITE)
129 | let properties2: CBCharacteristicProperties = [.write, .notify ]
130 | let permissions2: CBAttributePermissions = [.writeable]
131 | let ch2 = CBMutableCharacteristic(type: characteristic2UUID, properties: properties2, value: nil, permissions: permissions2)
132 | chs.append(ch2)
133 |
134 | // Create the service, add the characteristic to it
135 | service.characteristics = chs
136 |
137 | createdService.append(service)
138 | localPeripheralManager.add(service)
139 | }
140 |
141 |
142 |
143 |
144 | // delegate
145 | // service + Charactersitic added to peripheral
146 | //
147 | func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?){
148 |
149 | if error != nil {
150 | delegate?.logToScreen( ("Error adding services: \(error!.localizedDescription)"))
151 | }
152 | else {
153 | delegate?.logToScreen( "peripheralManager didAdd service : \(service.uuid.uuidString)")
154 |
155 | // Create an advertisement, using the service UUID
156 | let advertisement: [String : Any] = [CBAdvertisementDataServiceUUIDsKey : [service.uuid]] //CBAdvertisementDataLocalNameKey: peripheralName]
157 | //28 bytes maxu !!!
158 | // only 10 bytes for the name
159 | // https://developer.apple.com/reference/corebluetooth/cbperipheralmanager/1393252-startadvertising
160 |
161 | // start the advertisement
162 | delegate?.logToScreen( "Advertisement datas: ")
163 | delegate?.logToScreen( String(describing: advertisement))
164 | self.localPeripheralManager.startAdvertising(advertisement)
165 |
166 | delegate?.logToScreen( "Service added to peripheral.")
167 | }
168 | }
169 |
170 |
171 |
172 | // Advertising done
173 | //
174 | func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?){
175 | if error != nil {
176 | delegate?.logToScreen( ("peripheralManagerDidStartAdvertising Error :\n \(error!.localizedDescription)"))
177 | }
178 | else {
179 | delegate?.logToScreen( "peripheralManagerDidStartAdvertising - started.\n")
180 | }
181 | }
182 |
183 |
184 | // Central request to be notified to a charac.
185 | //
186 | func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
187 | delegate?.logToScreen( "peripheralManager didSubscribeTo characteristic " +
188 | characteristic.uuid.uuidString.prefix(6))
189 |
190 | if characteristic.uuid.uuidString == CH_READ {
191 | delegate?.logToScreen( "Central manager requested to read values by BLE notification")
192 |
193 | self.notifyCharac = characteristic as? CBMutableCharacteristic
194 | self.notifyCentral = central
195 |
196 | // start a timer, which will update the value, every xyz seconds.
197 | self.notifyValueTimer = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(self.notifyValue), userInfo: nil, repeats: true)
198 | }
199 |
200 | }
201 |
202 |
203 |
204 | // called when Central manager send read request
205 | //
206 | func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
207 |
208 | delegate?.logToScreen( "\nperipheralManager didReceiveRead request")
209 | delegate?.logToScreen( "request uuid: " + request.characteristic.uuid.uuidString)
210 |
211 | // prepare advertisement data
212 | let data: Data = TextToAdvertise.data(using: String.Encoding.utf16)!
213 | request.value = data //characteristic.value
214 |
215 | delegate?.logToScreen( "send : \(TextToAdvertise)" )
216 |
217 | // Respond to the request
218 | localPeripheralManager.respond( to: request, withResult: .success)
219 |
220 | // acknowledge : ok
221 | peripheral.respond(to: request, withResult: CBATTError.success)
222 | }
223 |
224 |
225 |
226 | // called when central manager send write request
227 | //
228 | public func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
229 | delegate?.logToScreen( "\nperipheralManager didReceiveWrite request")
230 |
231 | for r in requests {
232 | delegate?.logToScreen( "request uuid: " + r.characteristic.uuid.uuidString)
233 | }
234 |
235 | if requests.count > 0 {
236 | let str = NSString(data: requests[0].value!, encoding:String.Encoding.utf16.rawValue)!
237 | print("value sent by central Manager :\n" + String(describing: str))
238 | delegate?.logToScreen( "string received \(str) ")
239 | } else {
240 | print("nothing sent by centralManager")
241 | delegate?.logToScreen( "NO string received")
242 | }
243 | peripheral.respond(to: requests[0], withResult: CBATTError.success)
244 | }
245 |
246 |
247 |
248 |
249 | func respond(to request: CBATTRequest, withResult result: CBATTError.Code) {
250 | delegate?.logToScreen( "response requested")
251 | }
252 |
253 |
254 |
255 |
256 | func peripheralDidUpdateName(_ peripheral: CBPeripheral) {
257 | delegate?.logToScreen( "peripheral name changed")
258 | }
259 |
260 |
261 |
262 | func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {
263 | delegate?.logToScreen( "peripheral service modified")
264 | }
265 |
266 |
267 | func peripheral(_ peripheral: CBPeripheral, willRestoreState: [String : Any]) {
268 |
269 | delegate?.logToScreen("Will restore peripheral to central connection")
270 | }
271 |
272 | }
273 |
274 |
275 |
276 |
--------------------------------------------------------------------------------
/Peripheral Manager/Shared/BLE_Protocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BLEProtocol.swift
3 | // BlueToothPeripheral
4 | //
5 | // Created by Olivier Robin on 06/03/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreBluetooth
11 |
12 | protocol BLEPeripheralProtocol {
13 | func logToScreen(_ text: String)
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/Peripheral Manager/Shared/BLE_Tools.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BLE_Tools.swift
3 | // BlueToothPeripheral
4 | //
5 | // Created by Olivier Robin on 06/03/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreBluetooth
11 |
12 | extension BLEPeripheralManager {
13 |
14 | func getState(peripheral: CBPeripheralManager) -> String {
15 |
16 | switch peripheral.state {
17 | case .poweredOn :
18 | return "poweredON"
19 | case .poweredOff :
20 | return "poweredOFF"
21 | case .resetting:
22 | return "resetting"
23 | case .unauthorized:
24 | return "unauthorized"
25 | case .unknown:
26 | return "unknown"
27 | case .unsupported:
28 | return "unsupported"
29 | @unknown default:
30 | return "unknown"
31 | }
32 | }
33 |
34 |
35 | @objc func notifyValue() {
36 |
37 | // Set the data to notify
38 | let text = TextToNotify + " " + String(describing: cpt)
39 | let data: Data = text.data(using: String.Encoding.utf16)!
40 | cpt += 1
41 |
42 | delegate?.logToScreen( "Notify a value to central manager \(text)")
43 |
44 | // update the value, which will generate a notification event on central side
45 | localPeripheralManager.updateValue(data, for: self.notifyCharac!, onSubscribedCentrals: [self.notifyCentral!])
46 |
47 | }
48 |
49 |
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/Peripheral Manager/Shared/Tools.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringExtension.swift
3 | // BlueToothCentral
4 | //
5 | // Created by Olivier Robin on 30/10/2016.
6 | // Copyright © 2016 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | #if os(iOS) || os(watchOS) || os(tvOS)
12 | import UserNotifications
13 | #elseif os(OSX)
14 | import Cocoa
15 | #else
16 | // something else :o)
17 | #endif
18 |
19 |
20 | class Tools {
21 |
22 | static func toString(_ txt: String?) -> String {
23 | if (txt == nil) {
24 | return "???"
25 | }
26 | else {
27 | return txt!
28 | }
29 | }
30 |
31 |
32 | static func sendNotification(name: String, objectName: String?, object: AnyObject?) {
33 |
34 | let user: [String:AnyObject] = [objectName! : object!]
35 |
36 | NotificationCenter.default.post(name: Notification.Name(rawValue: name),
37 | object: nil, userInfo: user)
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Bluetooth LE IOS Swift
2 |
3 | Please note thet the purpose here is not to implement a perfect nice best design pattern, but demonstrate how to use, at the best I can, the BLE on iPhone, iPad ( and soon on Mac )
4 | it is bnetter, for me, to provide a way for anybody to use my code, in the design pattern you could choose.
5 | so : the code in iplemented in a simple design pattern. that's it :o)
6 |
7 | This project contains 3 applications, written in Swift 5, for IOS 13 or above.
8 |
9 | - a central manager, which will comunicate with bluetooth peripheral
10 | - a peripheral manager, which will simulate a bluetooth device, for people who don't have real bluetooth developpement board
11 | - a log viewer, which will be able to read the log files générated by the central manager, even when this app is in background state. it is usefull to test that everything is working well when central manager is in background.
12 |
13 | - There is a MACOS target, for the central Manager. it is a WORK IN PROGRESS ! :o)
14 |
15 | # Who I am ?
16 |
17 | My name is **Olivier Robin**, I am a Freelance developper, for IOS, specialized in Swift ( all version ).
18 | I build native App for iPhone, iPad, Apple Watch, Apple TV, and MAC.
19 |
20 | I work for French company, but also for any company where we can talk in English, or French :o)
21 |
22 | During several years, I worked for a US Company ( Bioclinica ). The time spent with them was great. Now,
23 | Since megin of 2016, I decided to work for my own company, in order to choose my project, my technology, my method of work, my road map...
24 |
25 | More infos on my Website : www.ormaa.fr
26 |
27 | # The Central manager
28 |
29 | This application will be able to be connected to a peripheral, read a value, write a value, be notifyed for value modified on peripheral side.
30 | it will received the notified value in bacjkground.
31 | it will be wake up if killed by IOS, when peripheral will notify anything in bluetooth.
32 |
33 | This App contain a BLE stack, a BLE controller, and a view + viewcontroller.
34 |
35 | for this example, the name or UUID is predefined, in the viewcontroller
36 | You could create an App, using this project, which discover all device, and service, and Characteristics, and ask to the user to select which he ant to connect to...
37 |
38 | The App will start the central, discover the services + Characteristics, connect to the peripheral, read a value, request to be notified for value update, write a value.
39 |
40 | **Background**
41 | App request to work in background, for Bluetooth event. for that, in info.plist, I added this :
42 |
43 | ```
44 | UIBackgroundModes
45 |
46 | bluetooth-central
47 |
48 |
49 | ```
50 |
51 | This tell to IOS that we need to be called, when in background, by bluetooth event coming from a connected peripheral.
52 | You understand that we are not managning anything : IOS will call us. it will call the delegate we ghave defined when starting the central manager, when we were connected to peripheral, when we requested to be notified for value modified by peripheral...
53 |
54 | Note : when in background, the app does nothing : no timer, no code is running.
55 | You can request to finish some code, before entering in 'sleep state', but this time allowed by IOS is about 3 minute max !
56 | there is plenty of code explaining how to request this 3 minutes more, before entering in 'sleep state'.
57 |
58 | example of code you could call, when you detect the background mode :
59 |
60 | call registerBackgroundTask()
61 |
62 | ```
63 | func registerBackgroundTask() {
64 | appController.log("register bacground task")
65 | backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
66 | self?.endBackgroundTask()
67 | }
68 | assert(backgroundTask != UIBackgroundTaskInvalid)
69 | }
70 |
71 | func endBackgroundTask() {
72 | appController.log("Background task ended.")
73 | UIApplication.shared.endBackgroundTask(backgroundTask)
74 | backgroundTask = UIBackgroundTaskInvalid
75 | }
76 | ```
77 |
78 | To check what is the app state :
79 |
80 | ```
81 | switch UIApplication.shared.applicationState {
82 | case .active:
83 | // DO something
84 | case .background:
85 | // do spomething
86 | case .inactive:
87 | // do somehting
88 | break
89 | }
90 | ```
91 |
92 |
93 |
94 | **Long-term action in background**
95 |
96 | Sometime, IOS decide to kill you App : to save memory, to save battery, to ???
97 | in this case, you app does not do anything at all !
98 | There is a way to request to be wake up by IOS, when the bluetooth device update some notified value, is disconnected from peripheral, etc...
99 |
100 | To be able to have this behavior, we need to start the central manager, with some particular parameter :
101 |
102 | ```
103 | centralManager = CBCentralManager(delegate: self, queue: nil, options:[CBCentralManagerOptionRestoreIdentifierKey: "fr.ormaa.centralManager"])
104 |
105 | ```
106 | Note : I tried to define a queue, and have no better result !
107 |
108 | CBCentralManagerOptionRestoreIdentifierKey : tells to IOS : if you kill this App, and there is a peripheral which was connected to this central ( fr.ormaa.centralManager), wake up the app, call the appdelegate class etc...
109 | Note : in this case, the app stay in background, nothign is displayed on screen.
110 |
111 | when the peripheral is dosconnected, visible by ios bluetooth again, when peripheral notify some value, the App is restarted.
112 | in my appdelegate, I added this code
113 |
114 | ```
115 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
116 | // Override point for customization after application launch.
117 |
118 | singleton.logger.log("application didFinishLaunchingWithOptions")
119 |
120 | singleton.appRestored = false
121 | singleton.centralManagerToRestore = ""
122 |
123 | // if waked up by the system, with bluetooth identifier in parameter :
124 | // We need to initialize a central manager, with same name.
125 | // a bluetooth event accoured.
126 | //
127 | if let peripheralManagerIdentifiers: [String] = launchOptions?[UIApplicationLaunchOptionsKey.bluetoothCentrals] as? [String]{
128 | if peripheralManagerIdentifiers.count > 1 {
129 | // TODO : manage this case
130 | }
131 | if peripheralManagerIdentifiers.count == 1 {
132 | // only one central Manager to initialize again
133 | let identifier = peripheralManagerIdentifiers.first
134 |
135 | singleton.logger.log("UIApplicationLaunchOptionsKey.bluetoothCentrals] : ")
136 | singleton.logger.log("App was closed by system. will restore the central manager ")
137 | singleton.logger.log("--> " + identifier!)
138 |
139 | // flag allowing to know that we need to restore the central manager
140 | singleton.appRestored = true
141 | // name of central manager to restore
142 | singleton.centralManagerToRestore = identifier!
143 |
144 | }
145 | }
146 | return true
147 | }
148 | ```
149 |
150 | Here, we noticei that I get only string ! not central object, no peripheral object !!!
151 | I place some flag and value, in a singleton class, to tell to the main controller : hey, the central manager need to be update, but not using the normal process !
152 |
153 | in my main controller, here is what happen :
154 |
155 | ```
156 | if appDelegate!.singleton.appRestored {
157 | appDelegate!.singleton.bluetoothController.restoreCentralManager(viewControllerDelegate: self,
158 | centralName: appDelegate!.singleton.centralManagerToRestore)
159 | }
160 |
161 | ```
162 | RestoreCentralManager will do that :
163 |
164 | ```
165 | centralManager = CBCentralManager(delegate: self, queue: nil, options:[CBCentralManagerOptionRestoreIdentifierKey: "fr.ormaa.centralManager"])
166 | ```
167 |
168 | WE DO NOT call scan peripheral, or retreive peripheral !
169 |
170 |
171 | in the centralmanager class, I have implemented this :
172 |
173 | ```
174 | public func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
175 |
176 | log("will restore connection")
177 |
178 | if let peripheralsObject = dict[CBCentralManagerRestoredStatePeripheralsKey] {
179 | let peripherals = peripheralsObject as! Array
180 | if peripherals.count > 0 {
181 | log("Peripheral found")
182 |
183 | let peripheral = peripherals[0]
184 | peripheral.delegate = self
185 | self.peripherals.append(peripheral)
186 | self.rssiDB.append(NSNumber())
187 | self.advertisementDatas.append(oneAdvertisement( array: ["none": "none"]))
188 |
189 | if getState(peripheral.state) == "connected" {
190 | log("connection to peripheral")
191 | self.connect(peripheral: peripheral)
192 | }
193 | }
194 | }
195 | }
196 | ```
197 |
198 | when I initalize the central manager, using the same name used when I was connected to peripheral, before app xwas killed by IOS :
199 | because I don't start any scan, I don't try to retreive peripheral : then the delegate is called.
200 | in WillRestoreState, I can save the peripheral, the characteristics which were discovered, etc...
201 |
202 | as you can see, it is not really clear (at least, for me) in the Apple Doc, but it is wuite easy to use in reality.
203 |
204 | # Xcode
205 |
206 | BE CAREFUL : when you iphone is connected toxcode, using a lightning cable, you cannot expect having some background or longtemer action behavior like you will have, when iphone is disconnected.
207 | BUT : you can test the fact that IOS kill you app, by running you App using Xcode, then press stop.
208 | in this case, run log viewer, and start to send value using the pripheral : you will se that the app is restarted, in background
209 |
210 |
211 | # Peripheral manager
212 |
213 | I created this app, because I don't have real bluetoot device, and no bluetooth developement board.
214 | This app is really usefull, to simulate all workflow that a bluetoot device could do :
215 | advertise value, notify when value are updated, read, write.
216 | The only thing wihch is not good : there is no way to modity the timing of comunication, power of signal etc... there is no way to go in low level of bluetooth.
217 |
218 | same for central manager : you need to run it in an iphone, ipad. it does not work in the simulator !
219 |
220 | # log viewer
221 | this app is used with the central manager, when you are wondering : what happen when the central manager is in background.
222 | The lo viewer will read a file, saved by the logger of the Central manager App.
223 | it is usefull to see what is working in background or not...
224 |
225 |
226 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1CF2A8601E65A5C800DFA527 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF2A85F1E65A5C800DFA527 /* AppDelegate.swift */; };
11 | 1CF2A8621E65A5C800DFA527 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CF2A8611E65A5C800DFA527 /* ViewController.swift */; };
12 | 1CF2A8651E65A5C800DFA527 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1CF2A8631E65A5C800DFA527 /* Main.storyboard */; };
13 | 1CF2A8671E65A5C800DFA527 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1CF2A8661E65A5C800DFA527 /* Assets.xcassets */; };
14 | 1CF2A86A1E65A5C800DFA527 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1CF2A8681E65A5C800DFA527 /* LaunchScreen.storyboard */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXCopyFilesBuildPhase section */
18 | 1C812DA71E69AE4F000A599A /* Embed Frameworks */ = {
19 | isa = PBXCopyFilesBuildPhase;
20 | buildActionMask = 2147483647;
21 | dstPath = "";
22 | dstSubfolderSpec = 10;
23 | files = (
24 | );
25 | name = "Embed Frameworks";
26 | runOnlyForDeploymentPostprocessing = 0;
27 | };
28 | /* End PBXCopyFilesBuildPhase section */
29 |
30 | /* Begin PBXFileReference section */
31 | 1C812DA91E69AEBA000A599A /* LogViewer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LogViewer-Bridging-Header.h"; sourceTree = ""; };
32 | 1CF2A85C1E65A5C800DFA527 /* LogViewer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LogViewer.app; sourceTree = BUILT_PRODUCTS_DIR; };
33 | 1CF2A85F1E65A5C800DFA527 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
34 | 1CF2A8611E65A5C800DFA527 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
35 | 1CF2A8641E65A5C800DFA527 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
36 | 1CF2A8661E65A5C800DFA527 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
37 | 1CF2A8691E65A5C800DFA527 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
38 | 1CF2A86B1E65A5C800DFA527 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
39 | 1CF2A8711E65A6EA00DFA527 /* LogViewer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LogViewer.entitlements; sourceTree = ""; };
40 | /* End PBXFileReference section */
41 |
42 | /* Begin PBXFrameworksBuildPhase section */
43 | 1CF2A8591E65A5C800DFA527 /* Frameworks */ = {
44 | isa = PBXFrameworksBuildPhase;
45 | buildActionMask = 2147483647;
46 | files = (
47 | );
48 | runOnlyForDeploymentPostprocessing = 0;
49 | };
50 | /* End PBXFrameworksBuildPhase section */
51 |
52 | /* Begin PBXGroup section */
53 | 1CF2A8531E65A5C800DFA527 = {
54 | isa = PBXGroup;
55 | children = (
56 | 1CF2A85E1E65A5C800DFA527 /* LogViewer */,
57 | 1CF2A85D1E65A5C800DFA527 /* Products */,
58 | );
59 | sourceTree = "";
60 | };
61 | 1CF2A85D1E65A5C800DFA527 /* Products */ = {
62 | isa = PBXGroup;
63 | children = (
64 | 1CF2A85C1E65A5C800DFA527 /* LogViewer.app */,
65 | );
66 | name = Products;
67 | sourceTree = "";
68 | };
69 | 1CF2A85E1E65A5C800DFA527 /* LogViewer */ = {
70 | isa = PBXGroup;
71 | children = (
72 | 1CF2A8711E65A6EA00DFA527 /* LogViewer.entitlements */,
73 | 1CF2A85F1E65A5C800DFA527 /* AppDelegate.swift */,
74 | 1CF2A8611E65A5C800DFA527 /* ViewController.swift */,
75 | 1CF2A8631E65A5C800DFA527 /* Main.storyboard */,
76 | 1CF2A8661E65A5C800DFA527 /* Assets.xcassets */,
77 | 1CF2A8681E65A5C800DFA527 /* LaunchScreen.storyboard */,
78 | 1CF2A86B1E65A5C800DFA527 /* Info.plist */,
79 | 1C812DA91E69AEBA000A599A /* LogViewer-Bridging-Header.h */,
80 | );
81 | path = LogViewer;
82 | sourceTree = "";
83 | };
84 | /* End PBXGroup section */
85 |
86 | /* Begin PBXNativeTarget section */
87 | 1CF2A85B1E65A5C800DFA527 /* LogViewer */ = {
88 | isa = PBXNativeTarget;
89 | buildConfigurationList = 1CF2A86E1E65A5C800DFA527 /* Build configuration list for PBXNativeTarget "LogViewer" */;
90 | buildPhases = (
91 | 1CF2A8581E65A5C800DFA527 /* Sources */,
92 | 1CF2A8591E65A5C800DFA527 /* Frameworks */,
93 | 1CF2A85A1E65A5C800DFA527 /* Resources */,
94 | 1C812DA71E69AE4F000A599A /* Embed Frameworks */,
95 | );
96 | buildRules = (
97 | );
98 | dependencies = (
99 | );
100 | name = LogViewer;
101 | productName = LogViewer;
102 | productReference = 1CF2A85C1E65A5C800DFA527 /* LogViewer.app */;
103 | productType = "com.apple.product-type.application";
104 | };
105 | /* End PBXNativeTarget section */
106 |
107 | /* Begin PBXProject section */
108 | 1CF2A8541E65A5C800DFA527 /* Project object */ = {
109 | isa = PBXProject;
110 | attributes = {
111 | LastSwiftUpdateCheck = 0820;
112 | LastUpgradeCheck = 0820;
113 | ORGANIZATIONNAME = fr.ormaa;
114 | TargetAttributes = {
115 | 1CF2A85B1E65A5C800DFA527 = {
116 | CreatedOnToolsVersion = 8.2.1;
117 | DevelopmentTeam = QP9H83DB6M;
118 | LastSwiftMigration = 0820;
119 | ProvisioningStyle = Automatic;
120 | SystemCapabilities = {
121 | com.apple.ApplicationGroups.iOS = {
122 | enabled = 1;
123 | };
124 | };
125 | };
126 | };
127 | };
128 | buildConfigurationList = 1CF2A8571E65A5C800DFA527 /* Build configuration list for PBXProject "LogViewer" */;
129 | compatibilityVersion = "Xcode 3.2";
130 | developmentRegion = English;
131 | hasScannedForEncodings = 0;
132 | knownRegions = (
133 | en,
134 | Base,
135 | );
136 | mainGroup = 1CF2A8531E65A5C800DFA527;
137 | productRefGroup = 1CF2A85D1E65A5C800DFA527 /* Products */;
138 | projectDirPath = "";
139 | projectRoot = "";
140 | targets = (
141 | 1CF2A85B1E65A5C800DFA527 /* LogViewer */,
142 | );
143 | };
144 | /* End PBXProject section */
145 |
146 | /* Begin PBXResourcesBuildPhase section */
147 | 1CF2A85A1E65A5C800DFA527 /* Resources */ = {
148 | isa = PBXResourcesBuildPhase;
149 | buildActionMask = 2147483647;
150 | files = (
151 | 1CF2A86A1E65A5C800DFA527 /* LaunchScreen.storyboard in Resources */,
152 | 1CF2A8671E65A5C800DFA527 /* Assets.xcassets in Resources */,
153 | 1CF2A8651E65A5C800DFA527 /* Main.storyboard in Resources */,
154 | );
155 | runOnlyForDeploymentPostprocessing = 0;
156 | };
157 | /* End PBXResourcesBuildPhase section */
158 |
159 | /* Begin PBXSourcesBuildPhase section */
160 | 1CF2A8581E65A5C800DFA527 /* Sources */ = {
161 | isa = PBXSourcesBuildPhase;
162 | buildActionMask = 2147483647;
163 | files = (
164 | 1CF2A8621E65A5C800DFA527 /* ViewController.swift in Sources */,
165 | 1CF2A8601E65A5C800DFA527 /* AppDelegate.swift in Sources */,
166 | );
167 | runOnlyForDeploymentPostprocessing = 0;
168 | };
169 | /* End PBXSourcesBuildPhase section */
170 |
171 | /* Begin PBXVariantGroup section */
172 | 1CF2A8631E65A5C800DFA527 /* Main.storyboard */ = {
173 | isa = PBXVariantGroup;
174 | children = (
175 | 1CF2A8641E65A5C800DFA527 /* Base */,
176 | );
177 | name = Main.storyboard;
178 | sourceTree = "";
179 | };
180 | 1CF2A8681E65A5C800DFA527 /* LaunchScreen.storyboard */ = {
181 | isa = PBXVariantGroup;
182 | children = (
183 | 1CF2A8691E65A5C800DFA527 /* Base */,
184 | );
185 | name = LaunchScreen.storyboard;
186 | sourceTree = "";
187 | };
188 | /* End PBXVariantGroup section */
189 |
190 | /* Begin XCBuildConfiguration section */
191 | 1CF2A86C1E65A5C800DFA527 /* Debug */ = {
192 | isa = XCBuildConfiguration;
193 | buildSettings = {
194 | ALWAYS_SEARCH_USER_PATHS = NO;
195 | CLANG_ANALYZER_NONNULL = YES;
196 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
197 | CLANG_CXX_LIBRARY = "libc++";
198 | CLANG_ENABLE_MODULES = YES;
199 | CLANG_ENABLE_OBJC_ARC = YES;
200 | CLANG_WARN_BOOL_CONVERSION = YES;
201 | CLANG_WARN_CONSTANT_CONVERSION = YES;
202 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
203 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
204 | CLANG_WARN_EMPTY_BODY = YES;
205 | CLANG_WARN_ENUM_CONVERSION = YES;
206 | CLANG_WARN_INFINITE_RECURSION = YES;
207 | CLANG_WARN_INT_CONVERSION = YES;
208 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
209 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
210 | CLANG_WARN_UNREACHABLE_CODE = YES;
211 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
212 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
213 | COPY_PHASE_STRIP = NO;
214 | DEBUG_INFORMATION_FORMAT = dwarf;
215 | ENABLE_STRICT_OBJC_MSGSEND = YES;
216 | ENABLE_TESTABILITY = YES;
217 | GCC_C_LANGUAGE_STANDARD = gnu99;
218 | GCC_DYNAMIC_NO_PIC = NO;
219 | GCC_NO_COMMON_BLOCKS = YES;
220 | GCC_OPTIMIZATION_LEVEL = 0;
221 | GCC_PREPROCESSOR_DEFINITIONS = (
222 | "DEBUG=1",
223 | "$(inherited)",
224 | );
225 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
226 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
227 | GCC_WARN_UNDECLARED_SELECTOR = YES;
228 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
229 | GCC_WARN_UNUSED_FUNCTION = YES;
230 | GCC_WARN_UNUSED_VARIABLE = YES;
231 | IPHONEOS_DEPLOYMENT_TARGET = 10.2;
232 | MTL_ENABLE_DEBUG_INFO = YES;
233 | ONLY_ACTIVE_ARCH = YES;
234 | SDKROOT = iphoneos;
235 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
236 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
237 | };
238 | name = Debug;
239 | };
240 | 1CF2A86D1E65A5C800DFA527 /* Release */ = {
241 | isa = XCBuildConfiguration;
242 | buildSettings = {
243 | ALWAYS_SEARCH_USER_PATHS = NO;
244 | CLANG_ANALYZER_NONNULL = YES;
245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
246 | CLANG_CXX_LIBRARY = "libc++";
247 | CLANG_ENABLE_MODULES = YES;
248 | CLANG_ENABLE_OBJC_ARC = YES;
249 | CLANG_WARN_BOOL_CONVERSION = YES;
250 | CLANG_WARN_CONSTANT_CONVERSION = YES;
251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
252 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
253 | CLANG_WARN_EMPTY_BODY = YES;
254 | CLANG_WARN_ENUM_CONVERSION = YES;
255 | CLANG_WARN_INFINITE_RECURSION = YES;
256 | CLANG_WARN_INT_CONVERSION = YES;
257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
258 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
259 | CLANG_WARN_UNREACHABLE_CODE = YES;
260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
261 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
262 | COPY_PHASE_STRIP = NO;
263 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
264 | ENABLE_NS_ASSERTIONS = NO;
265 | ENABLE_STRICT_OBJC_MSGSEND = YES;
266 | GCC_C_LANGUAGE_STANDARD = gnu99;
267 | GCC_NO_COMMON_BLOCKS = YES;
268 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
269 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
270 | GCC_WARN_UNDECLARED_SELECTOR = YES;
271 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
272 | GCC_WARN_UNUSED_FUNCTION = YES;
273 | GCC_WARN_UNUSED_VARIABLE = YES;
274 | IPHONEOS_DEPLOYMENT_TARGET = 10.2;
275 | MTL_ENABLE_DEBUG_INFO = NO;
276 | SDKROOT = iphoneos;
277 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
278 | VALIDATE_PRODUCT = YES;
279 | };
280 | name = Release;
281 | };
282 | 1CF2A86F1E65A5C800DFA527 /* Debug */ = {
283 | isa = XCBuildConfiguration;
284 | buildSettings = {
285 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
286 | CLANG_ENABLE_MODULES = YES;
287 | CODE_SIGN_ENTITLEMENTS = LogViewer/LogViewer.entitlements;
288 | DEVELOPMENT_TEAM = QP9H83DB6M;
289 | FRAMEWORK_SEARCH_PATHS = (
290 | "$(inherited)",
291 | "$(PROJECT_DIR)",
292 | );
293 | INFOPLIST_FILE = LogViewer/Info.plist;
294 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
295 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
296 | PRODUCT_BUNDLE_IDENTIFIER = fr.ormaa.LogViewer;
297 | PRODUCT_NAME = "$(TARGET_NAME)";
298 | SWIFT_OBJC_BRIDGING_HEADER = "LogViewer/LogViewer-Bridging-Header.h";
299 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
300 | SWIFT_VERSION = 3.0;
301 | };
302 | name = Debug;
303 | };
304 | 1CF2A8701E65A5C800DFA527 /* Release */ = {
305 | isa = XCBuildConfiguration;
306 | buildSettings = {
307 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
308 | CLANG_ENABLE_MODULES = YES;
309 | CODE_SIGN_ENTITLEMENTS = LogViewer/LogViewer.entitlements;
310 | DEVELOPMENT_TEAM = QP9H83DB6M;
311 | FRAMEWORK_SEARCH_PATHS = (
312 | "$(inherited)",
313 | "$(PROJECT_DIR)",
314 | );
315 | INFOPLIST_FILE = LogViewer/Info.plist;
316 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
317 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
318 | PRODUCT_BUNDLE_IDENTIFIER = fr.ormaa.LogViewer;
319 | PRODUCT_NAME = "$(TARGET_NAME)";
320 | SWIFT_OBJC_BRIDGING_HEADER = "LogViewer/LogViewer-Bridging-Header.h";
321 | SWIFT_VERSION = 3.0;
322 | };
323 | name = Release;
324 | };
325 | /* End XCBuildConfiguration section */
326 |
327 | /* Begin XCConfigurationList section */
328 | 1CF2A8571E65A5C800DFA527 /* Build configuration list for PBXProject "LogViewer" */ = {
329 | isa = XCConfigurationList;
330 | buildConfigurations = (
331 | 1CF2A86C1E65A5C800DFA527 /* Debug */,
332 | 1CF2A86D1E65A5C800DFA527 /* Release */,
333 | );
334 | defaultConfigurationIsVisible = 0;
335 | defaultConfigurationName = Release;
336 | };
337 | 1CF2A86E1E65A5C800DFA527 /* Build configuration list for PBXNativeTarget "LogViewer" */ = {
338 | isa = XCConfigurationList;
339 | buildConfigurations = (
340 | 1CF2A86F1E65A5C800DFA527 /* Debug */,
341 | 1CF2A8701E65A5C800DFA527 /* Release */,
342 | );
343 | defaultConfigurationIsVisible = 0;
344 | defaultConfigurationName = Release;
345 | };
346 | /* End XCConfigurationList section */
347 | };
348 | rootObject = 1CF2A8541E65A5C800DFA527 /* Project object */;
349 | }
350 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer.xcodeproj/project.xcworkspace/xcuserdata/ormaa.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer.xcodeproj/xcuserdata/ormaa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer.xcodeproj/xcuserdata/ormaa.xcuserdatad/xcschemes/LogViewer.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 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer.xcodeproj/xcuserdata/ormaa.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | LogViewer.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 1CF2A85B1E65A5C800DFA527
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // LogViewer
4 | //
5 | // Created by Olivier Robin on 28/02/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 |
12 |
13 |
14 | @UIApplicationMain
15 | class AppDelegate: UIResponder, UIApplicationDelegate {
16 |
17 | var window: UIWindow?
18 |
19 |
20 |
21 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
22 | // Override point for customization after application launch.
23 |
24 |
25 | return true
26 | }
27 |
28 | func applicationWillResignActive(_ application: UIApplication) {
29 | // 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.
30 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
31 | }
32 |
33 | func applicationDidEnterBackground(_ application: UIApplication) {
34 | // 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.
35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
36 | print("applicationDidEnterBackground")
37 | }
38 |
39 | func applicationWillEnterForeground(_ application: UIApplication) {
40 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
41 | }
42 |
43 | func applicationDidBecomeActive(_ application: UIApplication) {
44 | // 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.
45 | }
46 |
47 | func applicationWillTerminate(_ application: UIApplication) {
48 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
49 | }
50 |
51 |
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "NotificationIcon@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "NotificationIcon@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-Small.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-Small@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-Small@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "57x57",
47 | "idiom" : "iphone",
48 | "filename" : "Icon.png",
49 | "scale" : "1x"
50 | },
51 | {
52 | "size" : "57x57",
53 | "idiom" : "iphone",
54 | "filename" : "Icon@2x.png",
55 | "scale" : "2x"
56 | },
57 | {
58 | "size" : "60x60",
59 | "idiom" : "iphone",
60 | "filename" : "Icon-60@2x.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "60x60",
65 | "idiom" : "iphone",
66 | "filename" : "Icon-60@3x.png",
67 | "scale" : "3x"
68 | },
69 | {
70 | "size" : "20x20",
71 | "idiom" : "ipad",
72 | "filename" : "NotificationIcon~ipad.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "20x20",
77 | "idiom" : "ipad",
78 | "filename" : "NotificationIcon~ipad@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "29x29",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-Small.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "29x29",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-Small@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "40x40",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-40.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "40x40",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-40@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "50x50",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-Small-50.png",
109 | "scale" : "1x"
110 | },
111 | {
112 | "size" : "50x50",
113 | "idiom" : "ipad",
114 | "filename" : "Icon-Small-50@2x.png",
115 | "scale" : "2x"
116 | },
117 | {
118 | "size" : "72x72",
119 | "idiom" : "ipad",
120 | "filename" : "Icon-72.png",
121 | "scale" : "1x"
122 | },
123 | {
124 | "size" : "72x72",
125 | "idiom" : "ipad",
126 | "filename" : "Icon-72@2x.png",
127 | "scale" : "2x"
128 | },
129 | {
130 | "size" : "76x76",
131 | "idiom" : "ipad",
132 | "filename" : "Icon-76.png",
133 | "scale" : "1x"
134 | },
135 | {
136 | "size" : "76x76",
137 | "idiom" : "ipad",
138 | "filename" : "Icon-76@2x.png",
139 | "scale" : "2x"
140 | },
141 | {
142 | "size" : "83.5x83.5",
143 | "idiom" : "ipad",
144 | "filename" : "Icon-83.5@2x.png",
145 | "scale" : "2x"
146 | }
147 | ],
148 | "info" : {
149 | "version" : 1,
150 | "author" : "xcode"
151 | }
152 | }
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-40.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-72.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-72@2x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-76.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-Small-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-Small-50.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/Icon@2x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/NotificationIcon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/NotificationIcon@2x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/NotificationIcon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/NotificationIcon@3x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ormaa/Bluetooth-LE-IOS-Swift/f41e5563f8b9831a1e0b88deaa4c43e20cca52a2/logViewer/LogViewer/LogViewer/Assets.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/Base.lproj/LaunchScreen.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 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/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 |
40 |
52 |
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 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/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 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationLandscapeLeft
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/LogViewer-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 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/LogViewer.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.application-groups
6 |
7 | group.fr.ormaa
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/logViewer/LogViewer/LogViewer/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // LogViewer
4 | //
5 | // Created by Olivier Robin on 28/02/2017.
6 | // Copyright © 2017 fr.ormaa. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 |
12 | class ViewController: UIViewController {
13 |
14 |
15 |
16 | @IBOutlet weak var logTextView: UITextView!
17 |
18 | @IBOutlet weak var time: UILabel!
19 |
20 | var refreshLogs: Timer?
21 | let shared = UserDefaults(suiteName: "group.fr.ormaa")
22 | var logStr: String? = ""
23 |
24 |
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 |
28 | refreshLogs = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.refresh), userInfo: nil, repeats: true)
29 |
30 | }
31 |
32 | override func didReceiveMemoryWarning() {
33 | super.didReceiveMemoryWarning()
34 | // Dispose of any resources that can be recreated.
35 | }
36 |
37 |
38 | func refresh() {
39 |
40 | logStr = shared!.string(forKey: "group.fr.ormaa.central.log")
41 | let date = getDate()
42 |
43 |
44 | DispatchQueue.main.async {
45 | self.time.text = date //String(describing: Date())
46 |
47 | if self.logStr != nil {
48 | let str = self.logStr
49 | self.logTextView.text = str
50 | }
51 | else {
52 | self.logTextView.text = "nil"
53 | }
54 |
55 | }
56 | }
57 |
58 | func getDate() -> String {
59 | let date = Date()
60 | let formatter = DateFormatter()
61 | formatter.timeStyle = .medium
62 | formatter.dateStyle = .short
63 | formatter.timeZone = TimeZone.autoupdatingCurrent //.current
64 | formatter.dateFormat = "y-MM-dd -- H:m:ss.SSSS"
65 | let dateStr = formatter.string(from: date)
66 | //let dateValue = formatter.date(from: dateStr)?.addingTimeInterval(60)
67 | print("date: " + dateStr)
68 |
69 | return dateStr
70 | }
71 |
72 |
73 |
74 | @IBAction func clearClick(_ sender: Any) {
75 | shared?.removeObject(forKey: "group.fr.ormaa.log")
76 |
77 | }
78 |
79 | }
80 |
81 |
--------------------------------------------------------------------------------