├── 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 | --------------------------------------------------------------------------------