├── .gitignore ├── .swift-version ├── EasyBleManager.podspec ├── EasyBleManager.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ ├── IDEWorkspaceChecks.plist │ └── WorkspaceSettings.xcsettings ├── EasyBleManager ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist └── ViewController.swift ├── LICENSE ├── README.md └── Source ├── EasyBleConfig.swift ├── EasyBleDefines.swift ├── EasyBleDevice.swift ├── EasyBleHelper.swift └── EasyBleManager.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | xcuserdata 3 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 2 | -------------------------------------------------------------------------------- /EasyBleManager.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "EasyBleManager" 3 | s.version = "1.0.1" 4 | s.summary = "iOS蓝牙4.0 for Swift" 5 | s.description = "1 蓝牙扫描/超时扫描 2 蓝牙连接/超时连接 3 读取数据/写入数据 " 6 | s.homepage = "https://github.com/howard0103/EasyBleManager.git" 7 | s.license = "MIT" 8 | s.author = { "howard" => "344185723@qq.com" } 9 | s.platform = :ios, "8.0" 10 | s.source = { :git => "https://github.com/howard0103/EasyBleManager.git", :tag => "#{s.version}" } 11 | s.source_files = "Source" 12 | s.exclude_files = "Classes/Exclude" 13 | end 14 | -------------------------------------------------------------------------------- /EasyBleManager.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 622595AC2217DF6F00579CD6 /* EasyBleHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 622595AB2217DF6F00579CD6 /* EasyBleHelper.swift */; }; 11 | 62703EF32206C59500C50804 /* EasyBleConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62703EF22206C59500C50804 /* EasyBleConfig.swift */; }; 12 | 62CAEE7A22041D4C00850E10 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62CAEE7922041D4C00850E10 /* AppDelegate.swift */; }; 13 | 62CAEE7C22041D4C00850E10 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62CAEE7B22041D4C00850E10 /* ViewController.swift */; }; 14 | 62CAEE7F22041D4C00850E10 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 62CAEE7D22041D4C00850E10 /* Main.storyboard */; }; 15 | 62CAEE8122041D4C00850E10 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 62CAEE8022041D4C00850E10 /* Assets.xcassets */; }; 16 | 62CAEE8422041D4C00850E10 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 62CAEE8222041D4C00850E10 /* LaunchScreen.storyboard */; }; 17 | 62CAEE8D22041E0300850E10 /* EasyBleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62CAEE8C22041E0300850E10 /* EasyBleManager.swift */; }; 18 | 62CAEE9222044C4100850E10 /* EasyBleDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62CAEE9122044C4100850E10 /* EasyBleDevice.swift */; }; 19 | 62CBD5DB22058BB8008C5A1F /* EasyBleDefines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62CBD5DA22058BB8008C5A1F /* EasyBleDefines.swift */; }; 20 | 62CBD5DF22059FF2008C5A1F /* EasyBleManager.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 62CBD5DE22059FF2008C5A1F /* EasyBleManager.podspec */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 622595AB2217DF6F00579CD6 /* EasyBleHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EasyBleHelper.swift; sourceTree = ""; }; 25 | 62703EF22206C59500C50804 /* EasyBleConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EasyBleConfig.swift; sourceTree = ""; }; 26 | 62CAEE7622041D4C00850E10 /* EasyBleManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EasyBleManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 62CAEE7922041D4C00850E10 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 28 | 62CAEE7B22041D4C00850E10 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 29 | 62CAEE7E22041D4C00850E10 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 30 | 62CAEE8022041D4C00850E10 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | 62CAEE8322041D4C00850E10 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | 62CAEE8522041D4C00850E10 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 62CAEE8C22041E0300850E10 /* EasyBleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EasyBleManager.swift; sourceTree = ""; }; 34 | 62CAEE9122044C4100850E10 /* EasyBleDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EasyBleDevice.swift; sourceTree = ""; }; 35 | 62CBD5DA22058BB8008C5A1F /* EasyBleDefines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EasyBleDefines.swift; sourceTree = ""; }; 36 | 62CBD5DE22059FF2008C5A1F /* EasyBleManager.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = EasyBleManager.podspec; sourceTree = SOURCE_ROOT; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 62CAEE7322041D4C00850E10 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | 62CAEE6D22041D4C00850E10 = { 51 | isa = PBXGroup; 52 | children = ( 53 | 62CAEE7822041D4C00850E10 /* EasyBleManager */, 54 | 62CAEE7722041D4C00850E10 /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | 62CAEE7722041D4C00850E10 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 62CAEE7622041D4C00850E10 /* EasyBleManager.app */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | 62CAEE7822041D4C00850E10 /* EasyBleManager */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 62CBD5DE22059FF2008C5A1F /* EasyBleManager.podspec */, 70 | 62CAEE8B22041DF100850E10 /* Source */, 71 | 62CAEE7922041D4C00850E10 /* AppDelegate.swift */, 72 | 62CAEE7B22041D4C00850E10 /* ViewController.swift */, 73 | 62CAEE7D22041D4C00850E10 /* Main.storyboard */, 74 | 62CAEE8022041D4C00850E10 /* Assets.xcassets */, 75 | 62CAEE8222041D4C00850E10 /* LaunchScreen.storyboard */, 76 | 62CAEE8522041D4C00850E10 /* Info.plist */, 77 | ); 78 | path = EasyBleManager; 79 | sourceTree = ""; 80 | }; 81 | 62CAEE8B22041DF100850E10 /* Source */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 62CBD5DA22058BB8008C5A1F /* EasyBleDefines.swift */, 85 | 62703EF22206C59500C50804 /* EasyBleConfig.swift */, 86 | 62CAEE8C22041E0300850E10 /* EasyBleManager.swift */, 87 | 62CAEE9122044C4100850E10 /* EasyBleDevice.swift */, 88 | 622595AB2217DF6F00579CD6 /* EasyBleHelper.swift */, 89 | ); 90 | path = Source; 91 | sourceTree = SOURCE_ROOT; 92 | }; 93 | /* End PBXGroup section */ 94 | 95 | /* Begin PBXNativeTarget section */ 96 | 62CAEE7522041D4C00850E10 /* EasyBleManager */ = { 97 | isa = PBXNativeTarget; 98 | buildConfigurationList = 62CAEE8822041D4C00850E10 /* Build configuration list for PBXNativeTarget "EasyBleManager" */; 99 | buildPhases = ( 100 | 62CAEE7222041D4C00850E10 /* Sources */, 101 | 62CAEE7322041D4C00850E10 /* Frameworks */, 102 | 62CAEE7422041D4C00850E10 /* Resources */, 103 | ); 104 | buildRules = ( 105 | ); 106 | dependencies = ( 107 | ); 108 | name = EasyBleManager; 109 | productName = BleManager; 110 | productReference = 62CAEE7622041D4C00850E10 /* EasyBleManager.app */; 111 | productType = "com.apple.product-type.application"; 112 | }; 113 | /* End PBXNativeTarget section */ 114 | 115 | /* Begin PBXProject section */ 116 | 62CAEE6E22041D4C00850E10 /* Project object */ = { 117 | isa = PBXProject; 118 | attributes = { 119 | LastSwiftUpdateCheck = 1010; 120 | LastUpgradeCheck = 1010; 121 | ORGANIZATIONNAME = howard; 122 | TargetAttributes = { 123 | 62CAEE7522041D4C00850E10 = { 124 | CreatedOnToolsVersion = 10.1; 125 | SystemCapabilities = { 126 | com.apple.BackgroundModes = { 127 | enabled = 1; 128 | }; 129 | }; 130 | }; 131 | }; 132 | }; 133 | buildConfigurationList = 62CAEE7122041D4C00850E10 /* Build configuration list for PBXProject "EasyBleManager" */; 134 | compatibilityVersion = "Xcode 9.3"; 135 | developmentRegion = en; 136 | hasScannedForEncodings = 0; 137 | knownRegions = ( 138 | en, 139 | Base, 140 | ); 141 | mainGroup = 62CAEE6D22041D4C00850E10; 142 | productRefGroup = 62CAEE7722041D4C00850E10 /* Products */; 143 | projectDirPath = ""; 144 | projectRoot = ""; 145 | targets = ( 146 | 62CAEE7522041D4C00850E10 /* EasyBleManager */, 147 | ); 148 | }; 149 | /* End PBXProject section */ 150 | 151 | /* Begin PBXResourcesBuildPhase section */ 152 | 62CAEE7422041D4C00850E10 /* Resources */ = { 153 | isa = PBXResourcesBuildPhase; 154 | buildActionMask = 2147483647; 155 | files = ( 156 | 62CAEE8422041D4C00850E10 /* LaunchScreen.storyboard in Resources */, 157 | 62CAEE8122041D4C00850E10 /* Assets.xcassets in Resources */, 158 | 62CBD5DF22059FF2008C5A1F /* EasyBleManager.podspec in Resources */, 159 | 62CAEE7F22041D4C00850E10 /* Main.storyboard in Resources */, 160 | ); 161 | runOnlyForDeploymentPostprocessing = 0; 162 | }; 163 | /* End PBXResourcesBuildPhase section */ 164 | 165 | /* Begin PBXSourcesBuildPhase section */ 166 | 62CAEE7222041D4C00850E10 /* Sources */ = { 167 | isa = PBXSourcesBuildPhase; 168 | buildActionMask = 2147483647; 169 | files = ( 170 | 622595AC2217DF6F00579CD6 /* EasyBleHelper.swift in Sources */, 171 | 62CAEE7C22041D4C00850E10 /* ViewController.swift in Sources */, 172 | 62CAEE9222044C4100850E10 /* EasyBleDevice.swift in Sources */, 173 | 62CAEE7A22041D4C00850E10 /* AppDelegate.swift in Sources */, 174 | 62CAEE8D22041E0300850E10 /* EasyBleManager.swift in Sources */, 175 | 62CBD5DB22058BB8008C5A1F /* EasyBleDefines.swift in Sources */, 176 | 62703EF32206C59500C50804 /* EasyBleConfig.swift in Sources */, 177 | ); 178 | runOnlyForDeploymentPostprocessing = 0; 179 | }; 180 | /* End PBXSourcesBuildPhase section */ 181 | 182 | /* Begin PBXVariantGroup section */ 183 | 62CAEE7D22041D4C00850E10 /* Main.storyboard */ = { 184 | isa = PBXVariantGroup; 185 | children = ( 186 | 62CAEE7E22041D4C00850E10 /* Base */, 187 | ); 188 | name = Main.storyboard; 189 | sourceTree = ""; 190 | }; 191 | 62CAEE8222041D4C00850E10 /* LaunchScreen.storyboard */ = { 192 | isa = PBXVariantGroup; 193 | children = ( 194 | 62CAEE8322041D4C00850E10 /* Base */, 195 | ); 196 | name = LaunchScreen.storyboard; 197 | sourceTree = ""; 198 | }; 199 | /* End PBXVariantGroup section */ 200 | 201 | /* Begin XCBuildConfiguration section */ 202 | 62CAEE8622041D4C00850E10 /* Debug */ = { 203 | isa = XCBuildConfiguration; 204 | buildSettings = { 205 | ALWAYS_SEARCH_USER_PATHS = NO; 206 | CLANG_ANALYZER_NONNULL = YES; 207 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 208 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 209 | CLANG_CXX_LIBRARY = "libc++"; 210 | CLANG_ENABLE_MODULES = YES; 211 | CLANG_ENABLE_OBJC_ARC = YES; 212 | CLANG_ENABLE_OBJC_WEAK = YES; 213 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 214 | CLANG_WARN_BOOL_CONVERSION = YES; 215 | CLANG_WARN_COMMA = YES; 216 | CLANG_WARN_CONSTANT_CONVERSION = YES; 217 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 218 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 219 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 220 | CLANG_WARN_EMPTY_BODY = YES; 221 | CLANG_WARN_ENUM_CONVERSION = YES; 222 | CLANG_WARN_INFINITE_RECURSION = YES; 223 | CLANG_WARN_INT_CONVERSION = YES; 224 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 225 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 226 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 227 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 228 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 229 | CLANG_WARN_STRICT_PROTOTYPES = YES; 230 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 231 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 232 | CLANG_WARN_UNREACHABLE_CODE = YES; 233 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 234 | CODE_SIGN_IDENTITY = "iPhone Developer"; 235 | COPY_PHASE_STRIP = NO; 236 | DEBUG_INFORMATION_FORMAT = dwarf; 237 | ENABLE_STRICT_OBJC_MSGSEND = YES; 238 | ENABLE_TESTABILITY = YES; 239 | GCC_C_LANGUAGE_STANDARD = gnu11; 240 | GCC_DYNAMIC_NO_PIC = NO; 241 | GCC_NO_COMMON_BLOCKS = YES; 242 | GCC_OPTIMIZATION_LEVEL = 0; 243 | GCC_PREPROCESSOR_DEFINITIONS = ( 244 | "DEBUG=1", 245 | "$(inherited)", 246 | ); 247 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 248 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 249 | GCC_WARN_UNDECLARED_SELECTOR = YES; 250 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 251 | GCC_WARN_UNUSED_FUNCTION = YES; 252 | GCC_WARN_UNUSED_VARIABLE = YES; 253 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 254 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 255 | MTL_FAST_MATH = YES; 256 | ONLY_ACTIVE_ARCH = YES; 257 | SDKROOT = iphoneos; 258 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 259 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 260 | }; 261 | name = Debug; 262 | }; 263 | 62CAEE8722041D4C00850E10 /* Release */ = { 264 | isa = XCBuildConfiguration; 265 | buildSettings = { 266 | ALWAYS_SEARCH_USER_PATHS = NO; 267 | CLANG_ANALYZER_NONNULL = YES; 268 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 269 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 270 | CLANG_CXX_LIBRARY = "libc++"; 271 | CLANG_ENABLE_MODULES = YES; 272 | CLANG_ENABLE_OBJC_ARC = YES; 273 | CLANG_ENABLE_OBJC_WEAK = YES; 274 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 275 | CLANG_WARN_BOOL_CONVERSION = YES; 276 | CLANG_WARN_COMMA = YES; 277 | CLANG_WARN_CONSTANT_CONVERSION = YES; 278 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 279 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 280 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 281 | CLANG_WARN_EMPTY_BODY = YES; 282 | CLANG_WARN_ENUM_CONVERSION = YES; 283 | CLANG_WARN_INFINITE_RECURSION = YES; 284 | CLANG_WARN_INT_CONVERSION = YES; 285 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 286 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 287 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 288 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 289 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 290 | CLANG_WARN_STRICT_PROTOTYPES = YES; 291 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 292 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 293 | CLANG_WARN_UNREACHABLE_CODE = YES; 294 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 295 | CODE_SIGN_IDENTITY = "iPhone Developer"; 296 | COPY_PHASE_STRIP = NO; 297 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 298 | ENABLE_NS_ASSERTIONS = NO; 299 | ENABLE_STRICT_OBJC_MSGSEND = YES; 300 | GCC_C_LANGUAGE_STANDARD = gnu11; 301 | GCC_NO_COMMON_BLOCKS = YES; 302 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 303 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 304 | GCC_WARN_UNDECLARED_SELECTOR = YES; 305 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 306 | GCC_WARN_UNUSED_FUNCTION = YES; 307 | GCC_WARN_UNUSED_VARIABLE = YES; 308 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 309 | MTL_ENABLE_DEBUG_INFO = NO; 310 | MTL_FAST_MATH = YES; 311 | SDKROOT = iphoneos; 312 | SWIFT_COMPILATION_MODE = wholemodule; 313 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 314 | VALIDATE_PRODUCT = YES; 315 | }; 316 | name = Release; 317 | }; 318 | 62CAEE8922041D4C00850E10 /* Debug */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 322 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 323 | CODE_SIGN_STYLE = Automatic; 324 | DEVELOPMENT_TEAM = 9FYK9BE8GT; 325 | INFOPLIST_FILE = EasyBleManager/Info.plist; 326 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 327 | LD_RUNPATH_SEARCH_PATHS = ( 328 | "$(inherited)", 329 | "@executable_path/Frameworks", 330 | ); 331 | PRODUCT_BUNDLE_IDENTIFIER = com.foreo.foreoAsian; 332 | PRODUCT_NAME = "$(TARGET_NAME)"; 333 | SWIFT_VERSION = 4.2; 334 | TARGETED_DEVICE_FAMILY = 1; 335 | }; 336 | name = Debug; 337 | }; 338 | 62CAEE8A22041D4C00850E10 /* Release */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 342 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 343 | CODE_SIGN_STYLE = Automatic; 344 | DEVELOPMENT_TEAM = 9FYK9BE8GT; 345 | INFOPLIST_FILE = EasyBleManager/Info.plist; 346 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 347 | LD_RUNPATH_SEARCH_PATHS = ( 348 | "$(inherited)", 349 | "@executable_path/Frameworks", 350 | ); 351 | PRODUCT_BUNDLE_IDENTIFIER = com.foreo.foreoAsian; 352 | PRODUCT_NAME = "$(TARGET_NAME)"; 353 | SWIFT_VERSION = 4.2; 354 | TARGETED_DEVICE_FAMILY = 1; 355 | }; 356 | name = Release; 357 | }; 358 | /* End XCBuildConfiguration section */ 359 | 360 | /* Begin XCConfigurationList section */ 361 | 62CAEE7122041D4C00850E10 /* Build configuration list for PBXProject "EasyBleManager" */ = { 362 | isa = XCConfigurationList; 363 | buildConfigurations = ( 364 | 62CAEE8622041D4C00850E10 /* Debug */, 365 | 62CAEE8722041D4C00850E10 /* Release */, 366 | ); 367 | defaultConfigurationIsVisible = 0; 368 | defaultConfigurationName = Release; 369 | }; 370 | 62CAEE8822041D4C00850E10 /* Build configuration list for PBXNativeTarget "EasyBleManager" */ = { 371 | isa = XCConfigurationList; 372 | buildConfigurations = ( 373 | 62CAEE8922041D4C00850E10 /* Debug */, 374 | 62CAEE8A22041D4C00850E10 /* Release */, 375 | ); 376 | defaultConfigurationIsVisible = 0; 377 | defaultConfigurationName = Release; 378 | }; 379 | /* End XCConfigurationList section */ 380 | }; 381 | rootObject = 62CAEE6E22041D4C00850E10 /* Project object */; 382 | } 383 | -------------------------------------------------------------------------------- /EasyBleManager.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /EasyBleManager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /EasyBleManager.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /EasyBleManager/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // EasyBleManager 4 | // 5 | // Created by Howard on 2019/2/1. 6 | // Copyright © 2019 Howard. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. 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 | -------------------------------------------------------------------------------- /EasyBleManager/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 | } -------------------------------------------------------------------------------- /EasyBleManager/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /EasyBleManager/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 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /EasyBleManager/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 | 36 | 48 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /EasyBleManager/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | UIBackgroundModes 24 | 25 | bluetooth-central 26 | bluetooth-peripheral 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | 40 | UISupportedInterfaceOrientations~ipad 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationPortraitUpsideDown 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /EasyBleManager/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // EasyBleManager 4 | // 5 | // Created by Howard on 2019/2/1. 6 | // Copyright © 2019 Howard. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let DeviceVersion = "2A28" 12 | let DeviceBattery = "2A19" 13 | 14 | class ViewController: UIViewController { 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | } 18 | 19 | @IBAction func connectBle(_ sender: UIButton) { 20 | //获取连接上的设备 21 | let connectedDevice = EasyBleManager.shareInstance.connectedDevice() 22 | if connectedDevice == nil { 23 | //蓝牙变化时回调 24 | EasyBleManager.shareInstance.bleStateChangeBlock = {(state) in 25 | print("蓝牙状态:\(state)") 26 | } 27 | //配置可扫描到的设备名称 28 | EasyBleConfig.acceptableDeviceNames = ["LUNA 3"] 29 | //配置设备可发现的serviceUUIDs 30 | EasyBleConfig.acceptableDeviceServiceUUIDs = ["180A"] 31 | //开启调试日志信息 32 | EasyBleConfig.enableLog = true 33 | //扫描超时加调 34 | EasyBleManager.shareInstance.bleScanTimeoutBlock = { 35 | print("扫描超时") 36 | } 37 | //连接超时加调 38 | EasyBleManager.shareInstance.bleConnectTimeoutBlock = { 39 | print("连接超时") 40 | } 41 | //扫描成功回调 42 | EasyBleManager.shareInstance.bleScanSuccessBlock = {(device) in 43 | EasyBleManager.shareInstance.connectDevice(device) 44 | EasyBleManager.shareInstance.stopScan() 45 | } 46 | //设备连接成功回调,但设备这时还不能直接去读写 47 | EasyBleManager.shareInstance.bleConnectSuccessBlock = { 48 | (_) in 49 | } 50 | //此时设备已经准备就绪,随时可以读写操作 51 | EasyBleManager.shareInstance.deviceReadyBlock = {(_) in 52 | } 53 | //检查蓝牙是否可用 54 | if EasyBleManager.shareInstance.isBleEnable { 55 | //开始扫描设备 56 | EasyBleManager.shareInstance.scanForDevices() 57 | } else { 58 | print("蓝牙不可用") 59 | } 60 | 61 | } else { 62 | print("设备已连接上了") 63 | } 64 | } 65 | //读取设备信息 66 | @IBAction func readData(_ sender: Any) { 67 | let bleDevice = EasyBleManager.shareInstance.connectedDevice() 68 | bleDevice?.readDeviceInfo(DeviceVersion, complete: { (value) in 69 | var versionString = "" 70 | if value != nil { 71 | let versionHexString = EasyBleHelper.hexString(data: value!) 72 | versionString = EasyBleHelper.stringFromHexString(hex: versionHexString) ?? "" 73 | } 74 | print("设备版本号:\(versionString)") 75 | }) 76 | 77 | bleDevice?.readDeviceInfo(DeviceBattery, complete: { (value) in 78 | var battery = 0 79 | if value != nil { 80 | let batteryHexString = EasyBleHelper.hexString(data: value!) 81 | battery = EasyBleHelper.numberFromHexString(hex: batteryHexString)?.intValue ?? 0 82 | } 83 | print("设备电量:\(battery)") 84 | }) 85 | } 86 | //向设备写入数据 87 | @IBAction func writeData(_ sender: Any) { 88 | let bleDevice = EasyBleManager.shareInstance.connectedDevice() 89 | let bytes: [UInt8] = [0x10] 90 | bleDevice?.writeDevice(DeviceBattery, bytes: bytes) { (success) in 91 | if success { 92 | print("写入成功") 93 | } else { 94 | print("写入失败") 95 | } 96 | } 97 | } 98 | 99 | } 100 | 101 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Howard 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EasyBleManager 2 | iOS蓝牙模块(Ble4.0)Swift版本 3 | 4 | ## 背景 5 | swift项目中简单快速的集成iOS蓝牙模块 6 | 7 | ## 功能 8 | - 同步获取蓝牙状态,使用更加的灵活和便捷 9 | - 扫描设备和连接设备 10 | - 可配置指定的设备名称、设备可被发现的Service 11 | - 扫描和连接超时设置 12 | - 添加设备准备就绪状态,设备连接成功后,并不能直接读写操作,要等设备准备就绪后,就随时可以读写操作 13 | - 方便简单的读写操作 14 | - 开启和关闭调试日志 15 | 16 | ## 要求 17 | - iOS 8.0+ 18 | - Swift 4.0+ 19 | 20 | ## 安装 21 | #### CocoaPods 22 | `Podfile`: 23 | 24 | ```ruby 25 | source 'https://github.com/CocoaPods/Specs.git' 26 | platform :ios, '8.0' 27 | use_frameworks! 28 | pod 'EasyBleManager' 29 | ``` 30 | 31 | 然后运行: 32 | ```bash 33 | $ pod install 34 | ``` 35 | 36 | ## 使用 37 | #### 导入头文件 38 | ```swift 39 | import EasyBleManager 40 | ``` 41 | #### 具体用法 42 | 配置需要操作的特性uuid 43 | ```swift 44 | let DeviceVersion = "XXXX" 45 | let DeviceMode = "XXXX" 46 | ``` 47 | 48 | 检查蓝牙是否可用 49 | ```swift 50 | if EasyBleManager.shareInstance.isBleEnable { 51 | print("蓝牙可用") 52 | } else { 53 | print("蓝牙不可用") 54 | } 55 | 56 | ``` 57 | 获取连接上的设备 58 | ```swift 59 | let connectedDevice = EasyBleManager.shareInstance.connectedDevice() 60 | ``` 61 | 62 | 系统蓝牙状态变化时回调 63 | ```swift 64 | EasyBleManager.shareInstance.bleStateChangeBlock = {(state) in 65 | print("蓝牙状态:\(state)") 66 | } 67 | ``` 68 | 69 | 开启调试日志信息 70 | ```swift 71 | EasyBleConfig.enableLog = true //默认未启动调试日志 72 | ``` 73 | 74 | 配置可扫描到的设备名称/设备可被发现的Service 75 | ```swift 76 | EasyBleConfig.acceptableDeviceNames = ["XXXX"] //默认接受所有设备 77 | EasyBleConfig.acceptableDeviceServiceUUIDs = ["XXXX"] //默认发现设备所有的Service 78 | ``` 79 | 80 | 扫描超时回调 81 | ```swift 82 | EasyBleManager.shareInstance.bleScanTimeoutBlock = { 83 | print("扫描超时") 84 | } 85 | ``` 86 | 87 | 连接超时回调 88 | ```swift 89 | EasyBleManager.shareInstance.bleConnectTimeoutBlock = { 90 | print("连接超时") 91 | } 92 | ``` 93 | 94 | 扫描成功回调 95 | ```swift 96 | EasyBleManager.shareInstance.bleScanSuccessBlock = {(_) in 97 | print("扫描设备成功") 98 | } 99 | ``` 100 | 101 | 设备连接成功回调,此时设备还不能直接去读写操作 102 | ```swift 103 | EasyBleManager.shareInstance.bleConnectSuccessBlock = {(_) in 104 | print("设备连接成功") 105 | } 106 | ``` 107 | 108 | 设备准备就绪回调,此时可以读写操作 109 | ```swift 110 | EasyBleManager.shareInstance.deviceReadyBlock = {(_) in 111 | print("设备已经准备就绪成功") 112 | } 113 | ``` 114 | 115 | 扫描设备/停止扫描 116 | ```swift 117 | EasyBleManager.shareInstance.scanForDevices()//扫描设备 118 | EasyBleManager.shareInstance.stopScan()//停止扫描 119 | ``` 120 | 121 | 连接设备 122 | ```swift 123 | EasyBleManager.shareInstance.connectDevice(device) 124 | ``` 125 | 126 | 读取数据 127 | ```swift 128 | device?.readDeviceInfo("设备版本号特性uuid", complete: { (value) in 129 | var versionString = "" 130 | if value != nil { 131 | versionString = String.init(data: value!, encoding: String.Encoding.utf8) ?? "" 132 | } 133 | print("设备版本号:\(versionString)") 134 | }) 135 | ``` 136 | 137 | 写入数据 138 | ```swift 139 | let bytes: [UInt8] = [0x10] 140 | device?.writeDevice("设备特性uuid", bytes: bytes) { (success) in 141 | if success { 142 | print("写入成功") 143 | } else { 144 | print("写入失败") 145 | } 146 | } 147 | ``` 148 | 149 | ## 最后 150 | 使用过程中如果有任何问题和建议都可以随时联系我,我的邮箱 344185723@qq.com 151 | 愿大家都可以开开心心的写代码! 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /Source/EasyBleConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EasyBleConfig.swift 3 | // EasyBleManager 4 | // 5 | // Created by Howard on 2019/2/3. 6 | // Copyright © 2019 Howard. All rights reserved. 7 | // 8 | 9 | 10 | import UIKit 11 | 12 | open class EasyBleConfig: NSObject { 13 | //是否启用日志,默认未启用 14 | public static var enableLog: Bool = false 15 | //限定扫描到设备的名字 16 | public static var acceptableDeviceNames: [String]? 17 | //限定可发现的设备serviceUUIDs 18 | public static var acceptableDeviceServiceUUIDs: [String]? 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Source/EasyBleDefines.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BleDefines.swift 3 | // EasyBleManager 4 | // 5 | // Created by Howard on 2019/2/2. 6 | // Copyright © 2019 Howard. All rights reserved. 7 | // 8 | 9 | 10 | import Foundation 11 | 12 | let defaultScanTimeoutInterval = 60.0 13 | let defaultConnectTimeoutInterval = 60.0 14 | 15 | func debug_log(_ msg: String) { 16 | if EasyBleConfig.enableLog { 17 | print(msg) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/EasyBleDevice.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EasyBleDevice.swift 3 | // EasyBleManager 4 | // 5 | // Created by Howard on 2019/2/1. 6 | // Copyright © 2019 Howard. All rights reserved. 7 | // 8 | 9 | 10 | import UIKit 11 | import CoreBluetooth 12 | 13 | //蓝牙设备协议 14 | protocol EasyBleDeviceDelegate: NSObjectProtocol { 15 | func deviceDidBecomeReady(_ device: EasyBleDevice) 16 | } 17 | 18 | //蓝牙设备 19 | open class EasyBleDevice: NSObject { 20 | //MARK:公共属性 21 | public var name: String? 22 | public var peripheral: CBPeripheral? 23 | 24 | var delegate: EasyBleDeviceDelegate? 25 | //私有属性 26 | private var needDiscoverServices: NSMutableArray? 27 | private var allCharacteristics: NSMutableArray! 28 | private var readBlockDic: [String: ReadDeviceInfoBlock] = [:] 29 | private var writeBlockDic: [String: WriteDeviceBlock] = [:] 30 | 31 | init( _ peripheral: CBPeripheral?) { 32 | self.name = peripheral?.name 33 | self.peripheral = peripheral 34 | self.allCharacteristics = NSMutableArray() 35 | } 36 | } 37 | 38 | //MARK:-公共方法 39 | extension EasyBleDevice { 40 | //读取设备信息 41 | public func readDeviceInfo(_ uuid: String, complete:@escaping (_ object: Data?) -> Void) { 42 | guard let peripheral = self.peripheral else { return } 43 | let readInfoBlock = {(object: Data?) in 44 | complete(object) 45 | } 46 | readBlockDic[uuid] = readInfoBlock 47 | let characteristic = self.characteristicWithUUID(uuid) 48 | guard let myCharacteristic = characteristic else { 49 | complete(nil) 50 | debug_log("没有发现该特性") 51 | return 52 | } 53 | peripheral.readValue(for: myCharacteristic) 54 | } 55 | //写入数据 56 | public func writeDevice(_ uuid: String, bytes: [UInt8], writeDeviceBlock: @escaping WriteDeviceBlock) { 57 | guard let peripheral = self.peripheral else { return } 58 | writeBlockDic[uuid] = writeDeviceBlock 59 | let characteristic = self.characteristicWithUUID(uuid) 60 | guard let myCharacteristic = characteristic else { 61 | debug_log("没有发现该特性") 62 | return 63 | } 64 | let data = Data(bytes: bytes, count: bytes.count) 65 | peripheral.writeValue(data, for: myCharacteristic, type: CBCharacteristicWriteType.withResponse) 66 | } 67 | } 68 | 69 | //MARK:-私有方法 70 | extension EasyBleDevice { 71 | //根据characteristicUUID获取characteristic 72 | private func characteristicWithUUID(_ uuid: String) -> CBCharacteristic? { 73 | for characteristic in self.allCharacteristics { 74 | let bleCharacteristic = characteristic as! CBCharacteristic 75 | if bleCharacteristic.uuid.uuidString == uuid { 76 | return bleCharacteristic 77 | } 78 | } 79 | return nil 80 | } 81 | } 82 | 83 | //MARK:-蓝牙设备协议 84 | extension EasyBleDevice: CBPeripheralDelegate { 85 | //发现设备服务协议 86 | public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { 87 | debug_log("发现服务") 88 | guard let services = peripheral.services else { return } 89 | needDiscoverServices = NSMutableArray(array: services) 90 | for service in services { 91 | peripheral.discoverCharacteristics(nil, for: service) 92 | } 93 | } 94 | //发现服务下的特性值协议 95 | public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { 96 | debug_log("发现特性") 97 | if needDiscoverServices != nil { 98 | needDiscoverServices?.remove(service) 99 | if needDiscoverServices!.count <= 0 { 100 | debug_log("设备准备就绪") 101 | let bleDevice = EasyBleDevice(peripheral) 102 | self.delegate?.deviceDidBecomeReady(bleDevice) 103 | } 104 | } 105 | //把发现的所有特性保存起来 106 | guard let characteristics = service.characteristics else { return } 107 | self.allCharacteristics.addObjects(from: characteristics) 108 | } 109 | //特性值读取成功协议 110 | public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { 111 | let value = characteristic.value 112 | let uuid = characteristic.uuid.uuidString 113 | let readDeviceInfoBlock = readBlockDic[uuid] 114 | readDeviceInfoBlock?(value) 115 | } 116 | //写入成功协议 117 | public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { 118 | var success: Bool = true 119 | if error != nil { 120 | success = false 121 | } 122 | let uuid = characteristic.uuid.uuidString 123 | let writeDeviceBlock = writeBlockDic[uuid] 124 | if writeDeviceBlock != nil { 125 | writeDeviceBlock!(success) 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Source/EasyBleHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EasyBleHelper.swift 3 | // EasyBleManager 4 | // 5 | // Created by Howard on 2019/2/13. 6 | // Copyright © 2019 Howard. All rights reserved. 7 | // 8 | 9 | 10 | import UIKit 11 | 12 | class EasyBleHelper: NSObject { 13 | //data转hexstring 14 | static func hexString(data: Data) -> String { 15 | return data.withUnsafeBytes { (bytes: UnsafePointer) -> String in 16 | let buffer = UnsafeBufferPointer(start: bytes, count: data.count) 17 | return buffer.map{String(format: "%02hhx", $0)}.reduce("", {$0 + $1}) 18 | 19 | } 20 | } 21 | //hextstring转string 22 | static func stringFromHexString(hex: String) -> String? { 23 | var hex = hex 24 | var data = Data() 25 | while hex.count > 0 { 26 | let c: String = String(hex[hex.startIndex.. NSNumber? { 37 | var number: uint = 0 38 | let scanner = Scanner(string: hex) 39 | scanner.scanLocation = 0 40 | scanner.scanHexInt32(&number) 41 | return NSNumber(value: number) 42 | } 43 | //hexData转mac地址 44 | static func macAddressFromHexData(hexData: Data) -> String? { 45 | var hexStr = EasyBleHelper.hexString(data: hexData) 46 | for index in [2, 5, 8, 11, 14] { 47 | hexStr.insert(":", at: hexStr.index(hexStr.startIndex, offsetBy: index)) 48 | } 49 | return hexStr 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/EasyBleManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EasyBleManager.swift 3 | // EasyBleManager 4 | // 5 | // Created by Howard on 2019/2/1. 6 | // Copyright © 2019 Howard. All rights reserved. 7 | // 8 | 9 | 10 | import UIKit 11 | import CoreBluetooth 12 | 13 | //MARK:-定义 14 | //蓝牙状态 15 | public enum BleState { 16 | case PowerOff 17 | case PowerOn 18 | } 19 | 20 | public typealias ReadDeviceInfoBlock = (_ object: Data?) -> Void //获取设备信息block 21 | public typealias WriteDeviceBlock = (_ success: Bool) -> Void //写入数据block 22 | 23 | //MARK:-蓝牙控制类 24 | open class EasyBleManager: NSObject { 25 | public static let shareInstance = EasyBleManager() 26 | //MARK:公共属性和方法 27 | //系统蓝牙状态改变 28 | public var bleStateChangeBlock:((_ state: BleState) -> Void)? 29 | //扫描到设备后回调 30 | public var bleScanSuccessBlock:((_ device: EasyBleDevice) -> Void)? 31 | //扫描超时回调 32 | public var bleScanTimeoutBlock:(() -> Void)? 33 | //连接设备成功后回调 34 | public var bleConnectSuccessBlock:((_ device: EasyBleDevice) -> Void)? 35 | //连接超时回调 36 | public var bleConnectTimeoutBlock:(() -> Void)? 37 | //设备所有特性已经获取完毕,可以随时读写操作 38 | public var deviceReadyBlock:((_ device: EasyBleDevice) -> Void)? 39 | //扫描超时时间 40 | public var scanTimeoutInterval: TimeInterval! 41 | //连接超时时间 42 | public var connectTimeoutInterval: TimeInterval! 43 | //蓝牙是否可用 44 | public private(set) var isBleEnable: Bool = false 45 | 46 | //MARK:私有属性 47 | private var group: DispatchGroup? 48 | private var bleEnableBlock:((_ enable: Bool) -> Void)? 49 | private var state: BleState = .PowerOff 50 | private var scanTimeoutTimer: Timer? 51 | private var connectTimeroutTimer: Timer? 52 | private var currentManager: CBCentralManager? 53 | private var currentPeripheral: CBPeripheral? 54 | private var willConnectDevices: NSMutableArray! 55 | private var connectedDevices: NSMutableArray! 56 | 57 | override init() { 58 | super.init() 59 | self.scanTimeoutInterval = defaultScanTimeoutInterval 60 | self.connectTimeoutInterval = defaultConnectTimeoutInterval 61 | self.willConnectDevices = NSMutableArray() 62 | self.connectedDevices = NSMutableArray() 63 | group = DispatchGroup() 64 | group!.enter() 65 | let queue = DispatchQueue(label: "com.howard.queue") 66 | self.currentManager = CBCentralManager(delegate: self, queue: queue, options: nil) 67 | group!.wait() 68 | } 69 | } 70 | 71 | //MARK:-公共方法 72 | extension EasyBleManager { 73 | //扫描设备 74 | public func scanForDevices() { 75 | if self.currentManager != nil && self.isBleEnable { 76 | self.invalidateTimer(self.scanTimeoutTimer) 77 | self.scanTimeoutTimer = Timer.scheduledTimer(timeInterval: self.scanTimeoutInterval, target: self, selector: #selector(timeoutScan), userInfo: nil, repeats: false) 78 | self.currentManager!.scanForPeripherals(withServices: nil, options: nil) 79 | } 80 | } 81 | //连接设备 82 | public func connectDevice(_ device: EasyBleDevice) { 83 | guard let peripheral = device.peripheral else { return } 84 | if self.currentManager != nil && self.isBleEnable { 85 | self.addDeviceToConnectQueue(device) 86 | self.currentManager!.connect(peripheral, options: nil) 87 | } 88 | } 89 | //获取已连接上的设备 90 | public func connectedDevice() -> EasyBleDevice? { 91 | return connectedDevices?.firstObject as? EasyBleDevice 92 | } 93 | //停止扫描 94 | public func stopScan() { 95 | if self.currentManager != nil { 96 | self.currentManager!.stopScan() 97 | } 98 | self.invalidateTimer(self.scanTimeoutTimer) 99 | } 100 | } 101 | 102 | //MARK:-私有方法 103 | extension EasyBleManager { 104 | //更新蓝牙状态 105 | private func updateManagerState(_ central: CBCentralManager?) { 106 | guard let central = central else { return } 107 | switch central.state { 108 | case .poweredOn: 109 | self.state = .PowerOn 110 | default: 111 | self.state = .PowerOff 112 | } 113 | self.isBleEnable = (self.state == .PowerOn) 114 | } 115 | //取消定时器 116 | private func invalidateTimer(_ timer: Timer?) { 117 | var myTimer = timer 118 | myTimer?.invalidate() 119 | myTimer = nil 120 | } 121 | //扫描超时回调 122 | @objc private func timeoutScan() { 123 | self.currentManager?.stopScan() 124 | self.invalidateTimer(self.scanTimeoutTimer) 125 | if bleScanTimeoutBlock != nil { 126 | self.bleScanTimeoutBlock!() 127 | } 128 | } 129 | //连接超时回调 130 | @objc private func timeoutConnect(timer: Timer) { 131 | let device = timer.userInfo as? EasyBleDevice 132 | guard let bleDevice = device else { return } 133 | guard let peripheral = bleDevice.peripheral else { return } 134 | self.currentManager?.cancelPeripheralConnection(peripheral) 135 | self.removeDeviceFromConnectQueue(bleDevice) 136 | if bleConnectTimeoutBlock != nil { 137 | self.bleConnectTimeoutBlock!() 138 | } 139 | } 140 | //添加设备到连接队列 141 | private func addDeviceToConnectQueue(_ device: EasyBleDevice) { 142 | device.delegate = self 143 | self.invalidateTimer(self.connectTimeroutTimer) 144 | DispatchQueue.main.async(execute: { 145 | self.connectTimeroutTimer = Timer.scheduledTimer(timeInterval: self.connectTimeoutInterval, target: self, selector: #selector(self.timeoutConnect(timer:)), userInfo: device, repeats: false) 146 | }) 147 | self.willConnectDevices.add(device) 148 | } 149 | //从连接队列里移除设备 150 | private func removeDeviceFromConnectQueue(_ device: EasyBleDevice?) { 151 | guard let device = device else { return } 152 | self.invalidateTimer(self.connectTimeroutTimer) 153 | self.willConnectDevices.remove(device) 154 | } 155 | //根据peripheral获取Device 156 | private func deviceWithPeripheral(_ peripheral: CBPeripheral) -> EasyBleDevice? { 157 | for device in self.willConnectDevices { 158 | let bleDevice = device as! EasyBleDevice 159 | if bleDevice.peripheral == peripheral { 160 | return bleDevice 161 | } 162 | } 163 | return nil 164 | } 165 | } 166 | 167 | //MARK:-蓝牙协议 168 | extension EasyBleManager: CBCentralManagerDelegate { 169 | //蓝牙状态变化协议 170 | public func centralManagerDidUpdateState(_ central: CBCentralManager) { 171 | self.updateManagerState(central) 172 | if self.bleStateChangeBlock != nil { 173 | self.bleStateChangeBlock!(self.state) 174 | } 175 | if group != nil { 176 | group!.leave() 177 | group = nil 178 | } 179 | } 180 | //扫描成功协议 181 | public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { 182 | debug_log("发现设备:\(peripheral.name ?? "")") 183 | if let acceptableDeviceNames = EasyBleConfig.acceptableDeviceNames { 184 | if !(acceptableDeviceNames.contains(peripheral.name ?? "")) { 185 | return 186 | } 187 | } 188 | let bleDevice = EasyBleDevice(peripheral) 189 | if self.bleScanSuccessBlock != nil { 190 | self.bleScanSuccessBlock!(bleDevice) 191 | } 192 | } 193 | //连接成功协议 194 | public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { 195 | debug_log("连接成功") 196 | self.currentPeripheral = peripheral 197 | let bleDevice = self.deviceWithPeripheral(peripheral) 198 | guard let device = bleDevice else { return } 199 | self.removeDeviceFromConnectQueue(device) 200 | self.connectedDevices.add(device) 201 | peripheral.delegate = device 202 | var services: [CBUUID]? 203 | if let serviceUUIDs = EasyBleConfig.acceptableDeviceServiceUUIDs { 204 | if !(serviceUUIDs.isEmpty) { 205 | services = [CBUUID]() 206 | for uuid in serviceUUIDs { 207 | let cbUUID = CBUUID(string: uuid) 208 | services?.append(cbUUID) 209 | } 210 | } 211 | } 212 | peripheral.discoverServices(services) 213 | if self.bleConnectSuccessBlock != nil{ 214 | self.bleConnectSuccessBlock!(device) 215 | } 216 | } 217 | //连接失败协议 218 | public func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { 219 | debug_log("连接失败") 220 | let bleDevice = self.deviceWithPeripheral(peripheral) 221 | guard let device = bleDevice else { return } 222 | self.removeDeviceFromConnectQueue(device) 223 | self.connectedDevices.remove(device) 224 | } 225 | } 226 | 227 | //MARK:-EasyBleDeviceDelegate 228 | extension EasyBleManager: EasyBleDeviceDelegate { 229 | func deviceDidBecomeReady(_ device: EasyBleDevice) { 230 | if deviceReadyBlock != nil { 231 | deviceReadyBlock!(device) 232 | } 233 | } 234 | 235 | 236 | } 237 | --------------------------------------------------------------------------------