├── BLEDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── rick.smith.xcuserdatad │ └── xcschemes │ ├── BLEDemo.xcscheme │ └── xcschememanagement.plist ├── BLEDemo ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── MainViewController.swift └── ScanTableViewController.swift └── README.md /BLEDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 785098841D365ADD00976091 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 785098831D365ADD00976091 /* AppDelegate.swift */; }; 11 | 785098861D365ADD00976091 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 785098851D365ADD00976091 /* MainViewController.swift */; }; 12 | 785098891D365ADD00976091 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 785098871D365ADD00976091 /* Main.storyboard */; }; 13 | 7850988B1D365ADD00976091 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7850988A1D365ADD00976091 /* Assets.xcassets */; }; 14 | 7850988E1D365ADD00976091 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7850988C1D365ADD00976091 /* LaunchScreen.storyboard */; }; 15 | 785098961D365E8E00976091 /* ScanTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 785098951D365E8E00976091 /* ScanTableViewController.swift */; }; 16 | 785098991D36607500976091 /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 785098981D36607500976091 /* CoreBluetooth.framework */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 785098801D365ADD00976091 /* BLEDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BLEDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 785098831D365ADD00976091 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 22 | 785098851D365ADD00976091 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 23 | 785098881D365ADD00976091 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 24 | 7850988A1D365ADD00976091 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 25 | 7850988D1D365ADD00976091 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 26 | 7850988F1D365ADD00976091 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | 785098951D365E8E00976091 /* ScanTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanTableViewController.swift; sourceTree = ""; }; 28 | 785098981D36607500976091 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 7850987D1D365ADD00976091 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | 785098991D36607500976091 /* CoreBluetooth.framework in Frameworks */, 37 | ); 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXFrameworksBuildPhase section */ 41 | 42 | /* Begin PBXGroup section */ 43 | 785098771D365ADD00976091 = { 44 | isa = PBXGroup; 45 | children = ( 46 | 785098821D365ADD00976091 /* BLEDemo */, 47 | 785098811D365ADD00976091 /* Products */, 48 | 785098971D36607500976091 /* Frameworks */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | 785098811D365ADD00976091 /* Products */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | 785098801D365ADD00976091 /* BLEDemo.app */, 56 | ); 57 | name = Products; 58 | sourceTree = ""; 59 | }; 60 | 785098821D365ADD00976091 /* BLEDemo */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 785098831D365ADD00976091 /* AppDelegate.swift */, 64 | 785098851D365ADD00976091 /* MainViewController.swift */, 65 | 785098951D365E8E00976091 /* ScanTableViewController.swift */, 66 | 785098871D365ADD00976091 /* Main.storyboard */, 67 | 7850988A1D365ADD00976091 /* Assets.xcassets */, 68 | 7850988C1D365ADD00976091 /* LaunchScreen.storyboard */, 69 | 7850988F1D365ADD00976091 /* Info.plist */, 70 | ); 71 | path = BLEDemo; 72 | sourceTree = ""; 73 | }; 74 | 785098971D36607500976091 /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 785098981D36607500976091 /* CoreBluetooth.framework */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | /* End PBXGroup section */ 83 | 84 | /* Begin PBXNativeTarget section */ 85 | 7850987F1D365ADD00976091 /* BLEDemo */ = { 86 | isa = PBXNativeTarget; 87 | buildConfigurationList = 785098921D365ADD00976091 /* Build configuration list for PBXNativeTarget "BLEDemo" */; 88 | buildPhases = ( 89 | 7850987C1D365ADD00976091 /* Sources */, 90 | 7850987D1D365ADD00976091 /* Frameworks */, 91 | 7850987E1D365ADD00976091 /* Resources */, 92 | ); 93 | buildRules = ( 94 | ); 95 | dependencies = ( 96 | ); 97 | name = BLEDemo; 98 | productName = BLEDemo; 99 | productReference = 785098801D365ADD00976091 /* BLEDemo.app */; 100 | productType = "com.apple.product-type.application"; 101 | }; 102 | /* End PBXNativeTarget section */ 103 | 104 | /* Begin PBXProject section */ 105 | 785098781D365ADD00976091 /* Project object */ = { 106 | isa = PBXProject; 107 | attributes = { 108 | LastSwiftUpdateCheck = 0800; 109 | LastUpgradeCheck = 1000; 110 | ORGANIZATIONNAME = "Rick Smith"; 111 | TargetAttributes = { 112 | 7850987F1D365ADD00976091 = { 113 | CreatedOnToolsVersion = 8.0; 114 | DevelopmentTeamName = "AnalogFolk Ltd"; 115 | LastSwiftMigration = 0900; 116 | ProvisioningStyle = Manual; 117 | }; 118 | }; 119 | }; 120 | buildConfigurationList = 7850987B1D365ADD00976091 /* Build configuration list for PBXProject "BLEDemo" */; 121 | compatibilityVersion = "Xcode 3.2"; 122 | developmentRegion = English; 123 | hasScannedForEncodings = 0; 124 | knownRegions = ( 125 | en, 126 | Base, 127 | ); 128 | mainGroup = 785098771D365ADD00976091; 129 | productRefGroup = 785098811D365ADD00976091 /* Products */; 130 | projectDirPath = ""; 131 | projectRoot = ""; 132 | targets = ( 133 | 7850987F1D365ADD00976091 /* BLEDemo */, 134 | ); 135 | }; 136 | /* End PBXProject section */ 137 | 138 | /* Begin PBXResourcesBuildPhase section */ 139 | 7850987E1D365ADD00976091 /* Resources */ = { 140 | isa = PBXResourcesBuildPhase; 141 | buildActionMask = 2147483647; 142 | files = ( 143 | 7850988E1D365ADD00976091 /* LaunchScreen.storyboard in Resources */, 144 | 7850988B1D365ADD00976091 /* Assets.xcassets in Resources */, 145 | 785098891D365ADD00976091 /* Main.storyboard in Resources */, 146 | ); 147 | runOnlyForDeploymentPostprocessing = 0; 148 | }; 149 | /* End PBXResourcesBuildPhase section */ 150 | 151 | /* Begin PBXSourcesBuildPhase section */ 152 | 7850987C1D365ADD00976091 /* Sources */ = { 153 | isa = PBXSourcesBuildPhase; 154 | buildActionMask = 2147483647; 155 | files = ( 156 | 785098861D365ADD00976091 /* MainViewController.swift in Sources */, 157 | 785098841D365ADD00976091 /* AppDelegate.swift in Sources */, 158 | 785098961D365E8E00976091 /* ScanTableViewController.swift in Sources */, 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | /* End PBXSourcesBuildPhase section */ 163 | 164 | /* Begin PBXVariantGroup section */ 165 | 785098871D365ADD00976091 /* Main.storyboard */ = { 166 | isa = PBXVariantGroup; 167 | children = ( 168 | 785098881D365ADD00976091 /* Base */, 169 | ); 170 | name = Main.storyboard; 171 | sourceTree = ""; 172 | }; 173 | 7850988C1D365ADD00976091 /* LaunchScreen.storyboard */ = { 174 | isa = PBXVariantGroup; 175 | children = ( 176 | 7850988D1D365ADD00976091 /* Base */, 177 | ); 178 | name = LaunchScreen.storyboard; 179 | sourceTree = ""; 180 | }; 181 | /* End PBXVariantGroup section */ 182 | 183 | /* Begin XCBuildConfiguration section */ 184 | 785098901D365ADD00976091 /* Debug */ = { 185 | isa = XCBuildConfiguration; 186 | buildSettings = { 187 | ALWAYS_SEARCH_USER_PATHS = NO; 188 | CLANG_ANALYZER_NONNULL = YES; 189 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 190 | CLANG_CXX_LIBRARY = "libc++"; 191 | CLANG_ENABLE_MODULES = YES; 192 | CLANG_ENABLE_OBJC_ARC = YES; 193 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 194 | CLANG_WARN_BOOL_CONVERSION = YES; 195 | CLANG_WARN_COMMA = YES; 196 | CLANG_WARN_CONSTANT_CONVERSION = YES; 197 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 198 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 199 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 200 | CLANG_WARN_EMPTY_BODY = YES; 201 | CLANG_WARN_ENUM_CONVERSION = YES; 202 | CLANG_WARN_INFINITE_RECURSION = YES; 203 | CLANG_WARN_INT_CONVERSION = YES; 204 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 205 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 206 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 207 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 208 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 209 | CLANG_WARN_STRICT_PROTOTYPES = YES; 210 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 211 | CLANG_WARN_UNREACHABLE_CODE = YES; 212 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 213 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 214 | COPY_PHASE_STRIP = NO; 215 | DEBUG_INFORMATION_FORMAT = dwarf; 216 | ENABLE_STRICT_OBJC_MSGSEND = YES; 217 | ENABLE_TESTABILITY = YES; 218 | GCC_C_LANGUAGE_STANDARD = gnu99; 219 | GCC_DYNAMIC_NO_PIC = NO; 220 | GCC_NO_COMMON_BLOCKS = YES; 221 | GCC_OPTIMIZATION_LEVEL = 0; 222 | GCC_PREPROCESSOR_DEFINITIONS = ( 223 | "DEBUG=1", 224 | "$(inherited)", 225 | ); 226 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 227 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 228 | GCC_WARN_UNDECLARED_SELECTOR = YES; 229 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 230 | GCC_WARN_UNUSED_FUNCTION = YES; 231 | GCC_WARN_UNUSED_VARIABLE = YES; 232 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 233 | MTL_ENABLE_DEBUG_INFO = YES; 234 | ONLY_ACTIVE_ARCH = YES; 235 | SDKROOT = iphoneos; 236 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 237 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 238 | SWIFT_VERSION = ""; 239 | TARGETED_DEVICE_FAMILY = "1,2"; 240 | }; 241 | name = Debug; 242 | }; 243 | 785098911D365ADD00976091 /* Release */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_ANALYZER_NONNULL = YES; 248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 249 | CLANG_CXX_LIBRARY = "libc++"; 250 | CLANG_ENABLE_MODULES = YES; 251 | CLANG_ENABLE_OBJC_ARC = YES; 252 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 253 | CLANG_WARN_BOOL_CONVERSION = YES; 254 | CLANG_WARN_COMMA = YES; 255 | CLANG_WARN_CONSTANT_CONVERSION = YES; 256 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 257 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 258 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 259 | CLANG_WARN_EMPTY_BODY = YES; 260 | CLANG_WARN_ENUM_CONVERSION = YES; 261 | CLANG_WARN_INFINITE_RECURSION = YES; 262 | CLANG_WARN_INT_CONVERSION = YES; 263 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 264 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 265 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 266 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 267 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 268 | CLANG_WARN_STRICT_PROTOTYPES = YES; 269 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 273 | COPY_PHASE_STRIP = NO; 274 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 275 | ENABLE_NS_ASSERTIONS = NO; 276 | ENABLE_STRICT_OBJC_MSGSEND = YES; 277 | GCC_C_LANGUAGE_STANDARD = gnu99; 278 | GCC_NO_COMMON_BLOCKS = YES; 279 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 280 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 281 | GCC_WARN_UNDECLARED_SELECTOR = YES; 282 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 283 | GCC_WARN_UNUSED_FUNCTION = YES; 284 | GCC_WARN_UNUSED_VARIABLE = YES; 285 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 286 | MTL_ENABLE_DEBUG_INFO = NO; 287 | SDKROOT = iphoneos; 288 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 289 | SWIFT_VERSION = ""; 290 | TARGETED_DEVICE_FAMILY = "1,2"; 291 | VALIDATE_PRODUCT = YES; 292 | }; 293 | name = Release; 294 | }; 295 | 785098931D365ADD00976091 /* Debug */ = { 296 | isa = XCBuildConfiguration; 297 | buildSettings = { 298 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 299 | CODE_SIGN_IDENTITY = "iPhone Developer"; 300 | CODE_SIGN_STYLE = Manual; 301 | DEVELOPMENT_TEAM = ""; 302 | INFOPLIST_FILE = BLEDemo/Info.plist; 303 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 304 | PRODUCT_BUNDLE_IDENTIFIER = RickRedSix.BLEDemo; 305 | PRODUCT_NAME = "$(TARGET_NAME)"; 306 | PROVISIONING_PROFILE_SPECIFIER = ""; 307 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 308 | SWIFT_VERSION = 4.0; 309 | }; 310 | name = Debug; 311 | }; 312 | 785098941D365ADD00976091 /* Release */ = { 313 | isa = XCBuildConfiguration; 314 | buildSettings = { 315 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 316 | CODE_SIGN_IDENTITY = "iPhone Developer"; 317 | CODE_SIGN_STYLE = Manual; 318 | DEVELOPMENT_TEAM = ""; 319 | INFOPLIST_FILE = BLEDemo/Info.plist; 320 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 321 | PRODUCT_BUNDLE_IDENTIFIER = RickRedSix.BLEDemo; 322 | PRODUCT_NAME = "$(TARGET_NAME)"; 323 | PROVISIONING_PROFILE_SPECIFIER = ""; 324 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 325 | SWIFT_VERSION = 4.0; 326 | }; 327 | name = Release; 328 | }; 329 | /* End XCBuildConfiguration section */ 330 | 331 | /* Begin XCConfigurationList section */ 332 | 7850987B1D365ADD00976091 /* Build configuration list for PBXProject "BLEDemo" */ = { 333 | isa = XCConfigurationList; 334 | buildConfigurations = ( 335 | 785098901D365ADD00976091 /* Debug */, 336 | 785098911D365ADD00976091 /* Release */, 337 | ); 338 | defaultConfigurationIsVisible = 0; 339 | defaultConfigurationName = Release; 340 | }; 341 | 785098921D365ADD00976091 /* Build configuration list for PBXNativeTarget "BLEDemo" */ = { 342 | isa = XCConfigurationList; 343 | buildConfigurations = ( 344 | 785098931D365ADD00976091 /* Debug */, 345 | 785098941D365ADD00976091 /* Release */, 346 | ); 347 | defaultConfigurationIsVisible = 0; 348 | defaultConfigurationName = Release; 349 | }; 350 | /* End XCConfigurationList section */ 351 | }; 352 | rootObject = 785098781D365ADD00976091 /* Project object */; 353 | } 354 | -------------------------------------------------------------------------------- /BLEDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BLEDemo.xcodeproj/xcuserdata/rick.smith.xcuserdatad/xcschemes/BLEDemo.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 | -------------------------------------------------------------------------------- /BLEDemo.xcodeproj/xcuserdata/rick.smith.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | BLEDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 7850987F1D365ADD00976091 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BLEDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // BLEDemo 4 | // 5 | // Created by Rick Smith on 13/07/2016. 6 | // Copyright © 2016 Rick Smith. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | private func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /BLEDemo/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" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /BLEDemo/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 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /BLEDemo/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 | 26 | 32 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 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 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /BLEDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /BLEDemo/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.swift 3 | // CompassCompanion 4 | // 5 | // Created by Rick Smith on 04/07/2016. 6 | // Copyright © 2016 Rick Smith. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreBluetooth 11 | 12 | class MainViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate { 13 | var manager:CBCentralManager? = nil 14 | var mainPeripheral:CBPeripheral? = nil 15 | var mainCharacteristic:CBCharacteristic? = nil 16 | 17 | let BLEService = "DFB0" 18 | let BLECharacteristic = "DFB1" 19 | 20 | @IBOutlet weak var recievedMessageText: UILabel! 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | manager = CBCentralManager(delegate: self, queue: nil); 26 | 27 | customiseNavigationBar() 28 | } 29 | 30 | func customiseNavigationBar () { 31 | 32 | self.navigationItem.rightBarButtonItem = nil 33 | 34 | let rightButton = UIButton() 35 | 36 | if (mainPeripheral == nil) { 37 | rightButton.setTitle("Scan", for: []) 38 | rightButton.setTitleColor(UIColor.blue, for: []) 39 | rightButton.frame = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 60, height: 30)) 40 | rightButton.addTarget(self, action: #selector(self.scanButtonPressed), for: .touchUpInside) 41 | } else { 42 | rightButton.setTitle("Disconnect", for: []) 43 | rightButton.setTitleColor(UIColor.blue, for: []) 44 | rightButton.frame = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 100, height: 30)) 45 | rightButton.addTarget(self, action: #selector(self.disconnectButtonPressed), for: .touchUpInside) 46 | } 47 | 48 | let rightBarButton = UIBarButtonItem() 49 | rightBarButton.customView = rightButton 50 | self.navigationItem.rightBarButtonItem = rightBarButton 51 | 52 | } 53 | 54 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 55 | 56 | if (segue.identifier == "scan-segue") { 57 | let scanController : ScanTableViewController = segue.destination as! ScanTableViewController 58 | 59 | //set the manager's delegate to the scan view so it can call relevant connection methods 60 | manager?.delegate = scanController 61 | scanController.manager = manager 62 | scanController.parentView = self 63 | } 64 | 65 | } 66 | 67 | // MARK: Button Methods 68 | @objc func scanButtonPressed() { 69 | performSegue(withIdentifier: "scan-segue", sender: nil) 70 | } 71 | 72 | @objc func disconnectButtonPressed() { 73 | //this will call didDisconnectPeripheral, but if any other apps are using the device it will not immediately disconnect 74 | manager?.cancelPeripheralConnection(mainPeripheral!) 75 | } 76 | 77 | @IBAction func sendButtonPressed(_ sender: AnyObject) { 78 | let helloWorld = "Hello World!" 79 | let dataToSend = helloWorld.data(using: String.Encoding.utf8) 80 | 81 | if (mainPeripheral != nil) { 82 | mainPeripheral?.writeValue(dataToSend!, for: mainCharacteristic!, type: CBCharacteristicWriteType.withoutResponse) 83 | } else { 84 | print("haven't discovered device yet") 85 | } 86 | } 87 | 88 | // MARK: - CBCentralManagerDelegate Methods 89 | func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { 90 | mainPeripheral = nil 91 | customiseNavigationBar() 92 | print("Disconnected" + peripheral.name!) 93 | } 94 | 95 | 96 | func centralManagerDidUpdateState(_ central: CBCentralManager) { 97 | print(central.state) 98 | } 99 | 100 | // MARK: CBPeripheralDelegate Methods 101 | func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { 102 | 103 | for service in peripheral.services! { 104 | 105 | print("Service found with UUID: " + service.uuid.uuidString) 106 | 107 | //device information service 108 | if (service.uuid.uuidString == "180A") { 109 | peripheral.discoverCharacteristics(nil, for: service) 110 | } 111 | 112 | //GAP (Generic Access Profile) for Device Name 113 | // This replaces the deprecated CBUUIDGenericAccessProfileString 114 | if (service.uuid.uuidString == "1800") { 115 | peripheral.discoverCharacteristics(nil, for: service) 116 | } 117 | 118 | //Bluno Service 119 | if (service.uuid.uuidString == BLEService) { 120 | peripheral.discoverCharacteristics(nil, for: service) 121 | } 122 | 123 | } 124 | } 125 | 126 | func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { 127 | 128 | //get device name 129 | if (service.uuid.uuidString == "1800") { 130 | 131 | for characteristic in service.characteristics! { 132 | 133 | if (characteristic.uuid.uuidString == "2A00") { 134 | peripheral.readValue(for: characteristic) 135 | print("Found Device Name Characteristic") 136 | } 137 | 138 | } 139 | 140 | } 141 | 142 | if (service.uuid.uuidString == "180A") { 143 | 144 | for characteristic in service.characteristics! { 145 | 146 | if (characteristic.uuid.uuidString == "2A29") { 147 | peripheral.readValue(for: characteristic) 148 | print("Found a Device Manufacturer Name Characteristic") 149 | } else if (characteristic.uuid.uuidString == "2A23") { 150 | peripheral.readValue(for: characteristic) 151 | print("Found System ID") 152 | } 153 | 154 | } 155 | 156 | } 157 | 158 | if (service.uuid.uuidString == BLEService) { 159 | 160 | for characteristic in service.characteristics! { 161 | 162 | if (characteristic.uuid.uuidString == BLECharacteristic) { 163 | //we'll save the reference, we need it to write data 164 | mainCharacteristic = characteristic 165 | 166 | //Set Notify is useful to read incoming data async 167 | peripheral.setNotifyValue(true, for: characteristic) 168 | print("Found Bluno Data Characteristic") 169 | } 170 | 171 | } 172 | 173 | } 174 | 175 | } 176 | 177 | func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { 178 | 179 | 180 | if (characteristic.uuid.uuidString == "2A00") { 181 | //value for device name recieved 182 | let deviceName = characteristic.value 183 | print(deviceName ?? "No Device Name") 184 | } else if (characteristic.uuid.uuidString == "2A29") { 185 | //value for manufacturer name recieved 186 | let manufacturerName = characteristic.value 187 | print(manufacturerName ?? "No Manufacturer Name") 188 | } else if (characteristic.uuid.uuidString == "2A23") { 189 | //value for system ID recieved 190 | let systemID = characteristic.value 191 | print(systemID ?? "No System ID") 192 | } else if (characteristic.uuid.uuidString == BLECharacteristic) { 193 | //data recieved 194 | if(characteristic.value != nil) { 195 | let stringValue = String(data: characteristic.value!, encoding: String.Encoding.utf8)! 196 | 197 | recievedMessageText.text = stringValue 198 | } 199 | } 200 | 201 | 202 | } 203 | 204 | } 205 | 206 | -------------------------------------------------------------------------------- /BLEDemo/ScanTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScanTableViewController.swift 3 | // BLEDemo 4 | // 5 | // Created by Rick Smith on 13/07/2016. 6 | // Copyright © 2016 Rick Smith. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreBluetooth 11 | 12 | class ScanTableViewController: UITableViewController, CBCentralManagerDelegate { 13 | 14 | var peripherals:[CBPeripheral] = [] 15 | var manager:CBCentralManager? = nil 16 | var parentView:MainViewController? = nil 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | } 21 | 22 | override func viewDidAppear(_ animated: Bool) { 23 | scanBLEDevices() 24 | } 25 | 26 | // MARK: - Table view data source 27 | 28 | override func numberOfSections(in tableView: UITableView) -> Int { 29 | return 1 30 | } 31 | 32 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 33 | // #warning Incomplete implementation, return the number of rows 34 | return peripherals.count 35 | } 36 | 37 | 38 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 39 | let cell = tableView.dequeueReusableCell(withIdentifier: "scanTableCell", for: indexPath) 40 | let peripheral = peripherals[indexPath.row] 41 | cell.textLabel?.text = peripheral.name 42 | 43 | return cell 44 | } 45 | 46 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 47 | let peripheral = peripherals[indexPath.row] 48 | 49 | manager?.connect(peripheral, options: nil) 50 | } 51 | 52 | // MARK: BLE Scanning 53 | func scanBLEDevices() { 54 | //manager?.scanForPeripherals(withServices: [CBUUID.init(string: parentView!.BLEService)], options: nil) 55 | 56 | //if you pass nil in the first parameter, then scanForPeriperals will look for any devices. 57 | manager?.scanForPeripherals(withServices: nil, options: nil) 58 | 59 | //stop scanning after 3 seconds 60 | DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { 61 | self.stopScanForBLEDevices() 62 | } 63 | } 64 | 65 | func stopScanForBLEDevices() { 66 | manager?.stopScan() 67 | } 68 | 69 | // MARK: - CBCentralManagerDelegate Methods 70 | func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { 71 | if(!peripherals.contains(peripheral)) { 72 | peripherals.append(peripheral) 73 | } 74 | 75 | self.tableView.reloadData() 76 | } 77 | 78 | // func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : AnyObject], rssi RSSI: NSNumber) { 79 | // 80 | // if(!peripherals.contains(peripheral)) { 81 | // peripherals.append(peripheral) 82 | // } 83 | // 84 | // self.tableView.reloadData() 85 | // } 86 | 87 | func centralManagerDidUpdateState(_ central: CBCentralManager) { 88 | print(central.state) 89 | } 90 | 91 | func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { 92 | 93 | //pass reference to connected peripheral to parent view 94 | parentView?.mainPeripheral = peripheral 95 | peripheral.delegate = parentView 96 | peripheral.discoverServices(nil) 97 | 98 | //set the manager's delegate view to parent so it can call relevant disconnect methods 99 | manager?.delegate = parentView 100 | parentView?.customiseNavigationBar() 101 | 102 | if let navController = self.navigationController { 103 | navController.popViewController(animated: true) 104 | } 105 | 106 | print("Connected to " + peripheral.name!) 107 | } 108 | 109 | func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { 110 | print(error!) 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BLE4.0-iOS-Swift-Demo 2 | A demo app for talking to a Bluetooth 4.0 device, specifically the Bluno Beetle (http://www.dfrobot.com/index.php?route=product/product&product_id=1259) 3 | 4 | The tutorial for this demo app can be found here (https://medium.com/@rickredsix/getting-started-with-ios-swift-and-bluetooth-4-0-for-wearables-hardware-4661b1992bca) 5 | --------------------------------------------------------------------------------