From e301c942af85bffd3e814ef84c03a9b70414b133 Mon Sep 17 00:00:00 2001 From: Michael Welles Date: Wed, 21 Jan 2026 22:45:30 -0500 Subject: [PATCH 01/11] Modernize project for macOS 10.13+ and Xcode compatibility - Update deployment target from macOS 10.9 to 10.13 - Fix duplicate InfoPlist.strings build error in project.pbxproj - Replace deprecated CBCentralManagerState enums with CBManagerState - Update NSTask API to use executableURL and launchAndReturnError: - Replace deprecated NSStringPboardType with NSPasteboardTypeString - Replace NSOnState/NSOffState with NSControlStateValueOn/Off - Add NSBluetoothAlwaysUsageDescription for macOS privacy requirements - Configure CocoaPods test target with inherited search paths --- BeaconScanner.xcodeproj/project.pbxproj | 47 ++++++++++++++++++------- BeaconScanner/BeaconScanner-Info.plist | 2 ++ BeaconScanner/HGBeaconScanner.m | 19 +++++----- BeaconScanner/HGBeaconViewController.m | 8 ++--- Podfile | 6 ++++ Podfile.lock | 20 +++++++---- 6 files changed, 70 insertions(+), 32 deletions(-) diff --git a/BeaconScanner.xcodeproj/project.pbxproj b/BeaconScanner.xcodeproj/project.pbxproj index 3faebab..ad23c82 100644 --- a/BeaconScanner.xcodeproj/project.pbxproj +++ b/BeaconScanner.xcodeproj/project.pbxproj @@ -21,12 +21,11 @@ 677226AF18FC3D2F00804FDA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 677226A418FC3D2F00804FDA /* main.m */; }; 679835D718FC615D00E2E6D5 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 679835D318FC615D00E2E6D5 /* Credits.rtf */; }; 679835D818FC615D00E2E6D5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 679835D518FC615D00E2E6D5 /* InfoPlist.strings */; }; - 679835DF18FC61C400E2E6D5 /* BeaconScannerTests-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 679835DB18FC61C400E2E6D5 /* BeaconScannerTests-Info.plist */; }; - 679835E318FC61D300E2E6D5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 679835E118FC61D300E2E6D5 /* InfoPlist.strings */; }; 679835E418FC623200E2E6D5 /* BeaconScannerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 679835D918FC61A600E2E6D5 /* BeaconScannerTests.m */; }; 679835E518FC639E00E2E6D5 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 679835D018FC614900E2E6D5 /* MainMenu.xib */; }; 67DD56FF1CE39A6800AC5458 /* HGBluetoothState.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DD56FE1CE39A6800AC5458 /* HGBluetoothState.m */; }; 67F09DA61A168C8C006AB4AD /* HGBeaconHistory.m in Sources */ = {isa = PBXBuildFile; fileRef = 67F09DA51A168C8C006AB4AD /* HGBeaconHistory.m */; }; + 8CB1EB1C03F85C6BC44E7953 /* Pods_BeaconScannerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 679322E4189BD1AF0383A0E4 /* Pods_BeaconScannerTests.framework */; }; CFEFC1271DB916E900EF1F52 /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CFEFC1261DB916E900EF1F52 /* CoreBluetooth.framework */; }; /* End PBXBuildFile section */ @@ -41,6 +40,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 4067C6A90B05661D489B509E /* Pods-BeaconScannerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BeaconScannerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-BeaconScannerTests/Pods-BeaconScannerTests.debug.xcconfig"; sourceTree = ""; }; 6753158818EF27D30024E9CE /* Beacon Scanner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Beacon Scanner.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 6753158B18EF27D30024E9CE /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 6753158E18EF27D30024E9CE /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; @@ -62,6 +62,7 @@ 677226A218FC3D2F00804FDA /* HGDateValueTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HGDateValueTransformer.m; path = BeaconScanner/HGDateValueTransformer.m; sourceTree = SOURCE_ROOT; }; 677226A318FC3D2F00804FDA /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = BeaconScanner/Images.xcassets; sourceTree = SOURCE_ROOT; }; 677226A418FC3D2F00804FDA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = BeaconScanner/main.m; sourceTree = SOURCE_ROOT; }; + 679322E4189BD1AF0383A0E4 /* Pods_BeaconScannerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BeaconScannerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 679835D118FC614900E2E6D5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = BeaconScanner/Base.lproj/MainMenu.xib; sourceTree = SOURCE_ROOT; }; 679835D418FC615D00E2E6D5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = BeaconScanner/en.lproj/Credits.rtf; sourceTree = SOURCE_ROOT; }; 679835D618FC615D00E2E6D5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = BeaconScanner/en.lproj/InfoPlist.strings; sourceTree = SOURCE_ROOT; }; @@ -76,6 +77,7 @@ 822302367CD0BC06E4EBAADE /* Pods-Beacon Scanner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Beacon Scanner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Beacon Scanner/Pods-Beacon Scanner.release.xcconfig"; sourceTree = ""; }; 9A6C906BEBF5F431A0D2AD95 /* Pods-Beacon Scanner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Beacon Scanner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Beacon Scanner/Pods-Beacon Scanner.debug.xcconfig"; sourceTree = ""; }; CFEFC1261DB916E900EF1F52 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; }; + D6DB55C56AA50598A5767271 /* Pods-BeaconScannerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BeaconScannerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-BeaconScannerTests/Pods-BeaconScannerTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -95,6 +97,7 @@ files = ( 675315AC18EF27D30024E9CE /* Cocoa.framework in Frameworks */, 675315AB18EF27D30024E9CE /* XCTest.framework in Frameworks */, + 8CB1EB1C03F85C6BC44E7953 /* Pods_BeaconScannerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -106,6 +109,8 @@ children = ( 9A6C906BEBF5F431A0D2AD95 /* Pods-Beacon Scanner.debug.xcconfig */, 822302367CD0BC06E4EBAADE /* Pods-Beacon Scanner.release.xcconfig */, + 4067C6A90B05661D489B509E /* Pods-BeaconScannerTests.debug.xcconfig */, + D6DB55C56AA50598A5767271 /* Pods-BeaconScannerTests.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -138,6 +143,7 @@ 675315AA18EF27D30024E9CE /* XCTest.framework */, 6753158D18EF27D30024E9CE /* Other Frameworks */, 6853233AA0EF71ED59FA5B62 /* Pods_Beacon_Scanner.framework */, + 679322E4189BD1AF0383A0E4 /* Pods_BeaconScannerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -220,7 +226,6 @@ 6753158518EF27D30024E9CE /* Frameworks */, 6753158618EF27D30024E9CE /* Resources */, 011611A44B015773BFC6F747 /* [CP] Embed Pods Frameworks */, - 970EC7A216DFABF9D45F79CF /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -235,6 +240,7 @@ isa = PBXNativeTarget; buildConfigurationList = 675315BC18EF27D30024E9CE /* Build configuration list for PBXNativeTarget "BeaconScannerTests" */; buildPhases = ( + 99D5652E450C8B116C738D3F /* [CP] Check Pods Manifest.lock */, 675315A518EF27D30024E9CE /* Sources */, 675315A618EF27D30024E9CE /* Frameworks */, 675315A718EF27D30024E9CE /* Resources */, @@ -293,8 +299,6 @@ files = ( 679835E518FC639E00E2E6D5 /* MainMenu.xib in Resources */, 677226AE18FC3D2F00804FDA /* Images.xcassets in Resources */, - 679835E318FC61D300E2E6D5 /* InfoPlist.strings in Resources */, - 679835DF18FC61C400E2E6D5 /* BeaconScannerTests-Info.plist in Resources */, 679835D718FC615D00E2E6D5 /* Credits.rtf in Resources */, 679835D818FC615D00E2E6D5 /* InfoPlist.strings in Resources */, ); @@ -316,13 +320,20 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Beacon Scanner/Pods-Beacon Scanner-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/BlocksKit/BlocksKit.framework", + "${BUILT_PRODUCTS_DIR}/ReactiveCocoa/ReactiveCocoa.framework", + "${BUILT_PRODUCTS_DIR}/libextobjc/libextobjc.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BlocksKit.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReactiveCocoa.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libextobjc.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Beacon Scanner/Pods-Beacon Scanner-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Beacon Scanner/Pods-Beacon Scanner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 21DCA0098D292027F2690985 /* [CP] Check Pods Manifest.lock */ = { @@ -331,28 +342,38 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Beacon Scanner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 970EC7A216DFABF9D45F79CF /* [CP] Copy Pods Resources */ = { + 99D5652E450C8B116C738D3F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Copy Pods Resources"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-BeaconScannerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Beacon Scanner/Pods-Beacon Scanner-resources.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -466,7 +487,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -504,7 +525,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -547,6 +568,7 @@ }; 675315BD18EF27D30024E9CE /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4067C6A90B05661D489B509E /* Pods-BeaconScannerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Beacon Scanner.app/Contents/MacOS/Beacon Scanner"; COMBINE_HIDPI_IMAGES = YES; @@ -570,6 +592,7 @@ }; 675315BE18EF27D30024E9CE /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D6DB55C56AA50598A5767271 /* Pods-BeaconScannerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Beacon Scanner.app/Contents/MacOS/Beacon Scanner"; COMBINE_HIDPI_IMAGES = YES; diff --git a/BeaconScanner/BeaconScanner-Info.plist b/BeaconScanner/BeaconScanner-Info.plist index 80cf5e6..242b7a6 100644 --- a/BeaconScanner/BeaconScanner-Info.plist +++ b/BeaconScanner/BeaconScanner-Info.plist @@ -32,5 +32,7 @@ MainMenu NSPrincipalClass NSApplication + NSBluetoothAlwaysUsageDescription + BeaconScanner needs Bluetooth access to detect and display nearby iBeacon devices. diff --git a/BeaconScanner/HGBeaconScanner.m b/BeaconScanner/HGBeaconScanner.m index 84ae248..849828b 100644 --- a/BeaconScanner/HGBeaconScanner.m +++ b/BeaconScanner/HGBeaconScanner.m @@ -47,7 +47,7 @@ -(void)stopScanning { } -(void)startScanning { - if (self.centralManager.state == CBCentralManagerStatePoweredOn) { + if (self.centralManager.state == CBManagerStatePoweredOn) { [self.centralManager scanForPeripheralsWithServices:nil options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES}]; self.scanning = YES; @@ -62,14 +62,15 @@ -(NSNumber *)bluetoothLMPVersion { dispatch_once(&onceToken, ^{ NSPipe *outputPipe = [[NSPipe alloc] init]; NSTask *task = [[NSTask alloc] init]; - task.launchPath = @"/bin/bash"; + task.executableURL = [NSURL fileURLWithPath:@"/bin/bash"]; if (floor(kCFCoreFoundationVersionNumber) > kCFCoreFoundationVersionNumber10_10) { task.arguments = @[ @"-c", @"system_profiler -detailLevel full SPBluetoothDataType | grep 'LMP Version:' | awk '{print $4}' | tr -d '(' | tr -d ')'"]; } else { task.arguments = @[ @"-c", @"system_profiler -detailLevel full SPBluetoothDataType | grep 'LMP Version:' | awk '{print $3}'"]; } task.standardOutput = outputPipe; - [task launch]; + NSError *error = nil; + [task launchAndReturnError:&error]; [task waitUntilExit]; NSData *output = [[outputPipe fileHandleForReading] availableData]; NSString *outputString = [[[NSString alloc] initWithData:output encoding:NSUTF8StringEncoding] stringByReplacingOccurrencesOfString:@"\n"withString:@""]; @@ -86,16 +87,16 @@ -(NSNumber *)bluetoothLMPVersion { - (void)centralManagerDidUpdateState:(CBCentralManager *)central { NSString *state = nil; switch (central.state) { - case CBCentralManagerStateResetting: + case CBManagerStateResetting: state = HGGBluetoothStateResetting; break; - case CBCentralManagerStateUnsupported: + case CBManagerStateUnsupported: state = HGGBluetoothStateUnsupported; break; - case CBCentralManagerStateUnauthorized: + case CBManagerStateUnauthorized: state = HGGBluetoothStateUnauthorized; break; - case CBCentralManagerStatePoweredOff: + case CBManagerStatePoweredOff: //LMP version of 0x4 reports itself off, even though its' actually unsupported; if ([[self bluetoothLMPVersion] integerValue] < 6) { state = HGGBluetoothStateUnsupported; @@ -103,13 +104,13 @@ - (void)centralManagerDidUpdateState:(CBCentralManager *)central { state = HGGBluetoothStatePoweredOff; } break; - case CBCentralManagerStatePoweredOn: + case CBManagerStatePoweredOn: state = HGGBluetoothStatePoweredOn; break; default: state = HGGBluetoothStateUnknown; break; - + } if (state != HGGBluetoothStatePoweredOn) { if (self.scanning) { diff --git a/BeaconScanner/HGBeaconViewController.m b/BeaconScanner/HGBeaconViewController.m index 2b2abe9..61793a7 100644 --- a/BeaconScanner/HGBeaconViewController.m +++ b/BeaconScanner/HGBeaconViewController.m @@ -112,11 +112,11 @@ - (id)init { [[RACObserve(self, recordCheckboxButton) deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSButton *button) { - button.state = (self.isRecording ? NSOffState : NSOnState); + button.state = (self.isRecording ? NSControlStateValueOff : NSControlStateValueOn); @strongify(self) button.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(NSButton *button) { @strongify(self) - self.isRecording = !(button.state == NSOnState); + self.isRecording = !(button.state == NSControlStateValueOn); return [RACSignal empty]; }]; }]; @@ -214,9 +214,9 @@ - (IBAction) copy:(id)sender return; } NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard declareTypes:@[NSStringPboardType] + [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; - [pasteboard setString:beacon.proximityUUID.UUIDString forType:NSStringPboardType]; + [pasteboard setString:beacon.proximityUUID.UUIDString forType:NSPasteboardTypeString]; } #pragma mark - KVO Compliance for beacons diff --git a/Podfile b/Podfile index 8c09fd2..bf6d2f5 100644 --- a/Podfile +++ b/Podfile @@ -1,7 +1,13 @@ +platform :osx, '10.13' + target 'Beacon Scanner' do use_frameworks! pod 'ReactiveCocoa' pod 'BlocksKit' pod 'libextobjc' + + target 'BeaconScannerTests' do + inherit! :search_paths + end end diff --git a/Podfile.lock b/Podfile.lock index b83b950..b4172b7 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -17,8 +17,8 @@ PODS: - libextobjc/EXTScope (= 0.4) - libextobjc/EXTSelectorChecking (= 0.4) - libextobjc/EXTSynthesize (= 0.4) - - libextobjc/NSInvocation+EXT (= 0.4) - - libextobjc/NSMethodSignature+EXT (= 0.4) + - "libextobjc/NSInvocation+EXT (= 0.4)" + - "libextobjc/NSMethodSignature+EXT (= 0.4)" - libextobjc/RuntimeExtensions (= 0.4) - libextobjc/UmbrellaHeader (= 0.4) - libextobjc/EXTADT (0.4): @@ -37,9 +37,9 @@ PODS: - libextobjc/RuntimeExtensions - libextobjc/EXTSynthesize (0.4): - libextobjc/RuntimeExtensions - - libextobjc/NSInvocation+EXT (0.4): + - "libextobjc/NSInvocation+EXT (0.4)": - libextobjc/RuntimeExtensions - - libextobjc/NSMethodSignature+EXT (0.4): + - "libextobjc/NSMethodSignature+EXT (0.4)": - libextobjc/RuntimeExtensions - libextobjc/RuntimeExtensions (0.4) - libextobjc/UmbrellaHeader (0.4) @@ -56,11 +56,17 @@ DEPENDENCIES: - libextobjc - ReactiveCocoa +SPEC REPOS: + trunk: + - BlocksKit + - libextobjc + - ReactiveCocoa + SPEC CHECKSUMS: - BlocksKit: 7f422b971407001178d181a43b99014ea2591fe6 + BlocksKit: 8639b2848dd51c35c957af4d9fbdc7a769a1fc12 libextobjc: f2802d4262f6885570df9799747409ceb1de602c ReactiveCocoa: 81ee2fb7df2dfc6bd52b5d70473662195e20ca7d -PODFILE CHECKSUM: 1ed3d7ebe15a03726069be1da715cce97033d967 +PODFILE CHECKSUM: f034a96674c5d482262604d9d637ce2e5a48ac79 -COCOAPODS: 1.2.0 +COCOAPODS: 1.16.2 From 64435f3ba5c91b898ee98c233f6187c5d7b90f9b Mon Sep 17 00:00:00 2001 From: Michael Welles Date: Wed, 21 Jan 2026 22:45:59 -0500 Subject: [PATCH 02/11] Add project configuration files - Add CLAUDE.md with codebase guidance for Claude Code - Add .project IDE configuration --- .project | 0 CLAUDE.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 .project create mode 100644 CLAUDE.md diff --git a/.project b/.project new file mode 100644 index 0000000..e69de29 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ad09f26 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,73 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +BeaconScanner is a macOS desktop application written in Objective-C for scanning and detecting iBeacon devices via Bluetooth Low Energy (BLE). It fills a gap in Apple's macOS ecosystem by providing iBeacon client support that was only available on iOS. + +## Build Commands + +```bash +# Install dependencies (requires CocoaPods) +pod install + +# Open in Xcode (always use workspace, not project) +open BeaconScanner.xcworkspace + +# Build: Cmd+B in Xcode +# Run: Cmd+R in Xcode +# Test: Cmd+U in Xcode +``` + +## Dependencies (CocoaPods) + +- **ReactiveCocoa** - Reactive programming framework for signal-based communication +- **BlocksKit** - Block-based API extensions +- **libextobjc** - Objective-C extensions (@weakify/@strongify macros) + +## Architecture + +### Core Components + +**HGBeaconScanner** (Singleton) +- Central manager for all Bluetooth scanning operations +- Instantiates `CBCentralManager` on a dedicated dispatch queue +- Exposes `beaconSignal` (RACSignal) for detected beacons +- Exposes `bluetoothStateSignal` for Bluetooth state changes +- Implements `CBCentralManagerDelegate` protocol + +**HGBeacon** (Model) +- Represents a single iBeacon with UUID, major, minor, measuredPower, RSSI +- Parses 25-byte iBeacon manufacturer data from BLE advertisements +- Static factory methods: `beaconWithAdvertisementDataDictionary:` and `beaconWithManufacturerAdvertisementData:` + +**HGBeaconViewController** (UI Controller) +- Manages NSTableView displaying detected beacons +- Subscribes to beacon signals and updates UI reactively +- Implements housekeeping to remove stale beacons (15-second timeout) +- Supports beacon recording feature via HGBeaconHistory + +### Data Flow + +1. `HGBeaconScanner` receives BLE advertisements via `CBCentralManagerDelegate` +2. `HGBeacon` parses manufacturer data to extract iBeacon payload +3. Valid beacons are emitted via `beaconSignal` (RACSubject) +4. Subscribers (like `HGBeaconViewController`) receive and process beacon events + +### iBeacon Protocol + +The 25-byte payload format: +- Bytes 0-1: Company ID (0x4C = Apple) +- Byte 2: Data type (0x02) +- Byte 3: Data length (0x15 = 21 bytes) +- Bytes 4-19: Proximity UUID (128-bit) +- Bytes 20-21: Major (16-bit, big-endian) +- Bytes 22-23: Minor (16-bit, big-endian) +- Byte 24: Measured Power (calibrated RSSI at 1m) + +## Key Patterns + +- **Reactive subscriptions**: Use RACSignal subscriptions for beacon events, not delegate callbacks +- **Signal filtering**: Filter `beaconSignal` by UUID to listen for specific beacons +- **Stale beacon cleanup**: Periodically purge beacons not heard from recently (see HGBeaconViewController for implementation) From b2907df8c07bde65ce9c18199580b2424aecebae Mon Sep 17 00:00:00 2001 From: Michael Welles Date: Wed, 21 Jan 2026 22:51:29 -0500 Subject: [PATCH 03/11] Fix test reliability and enable code signing - Enable automatic code signing with development team - Change bundle identifier to nyc.welles.BeaconScanner - Fix testManagerScanningStart to wait for Bluetooth state - Add XCTSkipIf for environments where Bluetooth isn't available - Use run loop polling instead of XCTestExpectation for reliability --- BeaconScanner.xcodeproj/project.pbxproj | 20 ++++++++++---------- BeaconScannerTests/BeaconScannerTests.m | 18 ++++++++++++++++-- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/BeaconScanner.xcodeproj/project.pbxproj b/BeaconScanner.xcodeproj/project.pbxproj index ad23c82..3b78e9c 100644 --- a/BeaconScanner.xcodeproj/project.pbxproj +++ b/BeaconScanner.xcodeproj/project.pbxproj @@ -535,15 +535,15 @@ baseConfigurationReference = 9A6C906BEBF5F431A0D2AD95 /* Pods-Beacon Scanner.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "Developer ID Application"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 453G4TFLMD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "BeaconScanner/BeaconScanner-Prefix.pch"; INFOPLIST_FILE = "$(SRCROOT)/BeaconScanner/BeaconScanner-Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = "com.hugeinc.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.welles.BeaconScanner; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; WRAPPER_EXTENSION = app; }; name = Debug; @@ -553,15 +553,15 @@ baseConfigurationReference = 822302367CD0BC06E4EBAADE /* Pods-Beacon Scanner.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "Developer ID Application"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 453G4TFLMD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "BeaconScanner/BeaconScanner-Prefix.pch"; INFOPLIST_FILE = "$(SRCROOT)/BeaconScanner/BeaconScanner-Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = "com.hugeinc.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.welles.BeaconScanner; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; WRAPPER_EXTENSION = app; }; name = Release; @@ -583,7 +583,7 @@ "$(inherited)", ); INFOPLIST_FILE = "BeaconScannerTests/BeaconScannerTests-Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = "com.hugeinc.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.welles.BeaconScanner; PRODUCT_NAME = BeaconScannerTests; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; @@ -603,7 +603,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "BeaconScanner/BeaconScanner-Prefix.pch"; INFOPLIST_FILE = "BeaconScannerTests/BeaconScannerTests-Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = "com.hugeinc.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.welles.BeaconScanner; PRODUCT_NAME = BeaconScannerTests; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; diff --git a/BeaconScannerTests/BeaconScannerTests.m b/BeaconScannerTests/BeaconScannerTests.m index e300116..5490810 100644 --- a/BeaconScannerTests/BeaconScannerTests.m +++ b/BeaconScannerTests/BeaconScannerTests.m @@ -9,6 +9,7 @@ #import #import "HGBeaconScanner.h" #import "HGBeacon.h" + @interface DesktopBeaconTests : XCTestCase @end @@ -29,8 +30,21 @@ - (void)tearDown - (void)testManagerScanningStart { - [[HGBeaconScanner sharedBeaconScanner] startScanning]; - XCTAssertTrue([[HGBeaconScanner sharedBeaconScanner] scanning], @"Manager can start scanning"); + // Give Bluetooth time to initialize + HGBeaconScanner *scanner = [HGBeaconScanner sharedBeaconScanner]; + + // Brief wait for CBCentralManager to report state + NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:2.0]; + while (scanner.bluetoothState == nil && [timeout timeIntervalSinceNow] > 0) { + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + } + + // Skip test if Bluetooth state isn't available or isn't powered on + XCTSkipIf(scanner.bluetoothState == nil, @"Bluetooth state not available"); + XCTSkipIf(scanner.bluetoothState != HGGBluetoothStatePoweredOn, @"Bluetooth not powered on"); + + [scanner startScanning]; + XCTAssertTrue(scanner.scanning, @"Manager can start scanning"); } - (void)testManagerScanningStop From a26d3ef69a7a6dcfd967d5057a0e23e14ba28326 Mon Sep 17 00:00:00 2001 From: Michael Welles Date: Wed, 21 Jan 2026 22:53:51 -0500 Subject: [PATCH 04/11] Update copyright to 2026 Michael L. Welles - Update NSHumanReadableCopyright in Info.plist - Update Credits.rtf shown in About box --- BeaconScanner/BeaconScanner-Info.plist | 2 +- BeaconScanner/en.lproj/Credits.rtf | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/BeaconScanner/BeaconScanner-Info.plist b/BeaconScanner/BeaconScanner-Info.plist index 242b7a6..4a90680 100644 --- a/BeaconScanner/BeaconScanner-Info.plist +++ b/BeaconScanner/BeaconScanner-Info.plist @@ -27,7 +27,7 @@ LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} NSHumanReadableCopyright - Copyright © 2014 Huge, Inc. All rights reserved. + Copyright © 2026 Michael L. Welles. All rights reserved. NSMainNibFile MainMenu NSPrincipalClass diff --git a/BeaconScanner/en.lproj/Credits.rtf b/BeaconScanner/en.lproj/Credits.rtf index aac679b..e25dcc8 100644 --- a/BeaconScanner/en.lproj/Credits.rtf +++ b/BeaconScanner/en.lproj/Credits.rtf @@ -6,5 +6,9 @@ \f0\b\fs24 \cf0 Engineering: \b0 \ - Michael Welles \ + Michael L. Welles\ +\ +\b Copyright: +\b0 \ + \'a9 2026 Michael L. Welles. All rights reserved.\ } \ No newline at end of file From dc1dab374e25e8dd69d5a1cf58aeb13c37f29703 Mon Sep 17 00:00:00 2001 From: Michael Welles Date: Wed, 21 Jan 2026 23:00:56 -0500 Subject: [PATCH 05/11] Add Help documentation and CI/CD workflows - Add Apple Help Book with usage instructions - Add CFBundleHelpBookFolder/Name to Info.plist - Add CI workflow for build/test on push and PR - Add Release workflow for signing, notarization, and GitHub releases --- .github/workflows/ci.yml | 54 +++++ .github/workflows/release.yml | 194 ++++++++++++++++++ BeaconScanner.xcodeproj/project.pbxproj | 4 + BeaconScanner/BeaconScanner-Info.plist | 4 + .../BeaconScanner.help/Contents/Info.plist | 30 +++ .../Contents/Resources/en.lproj/index.html | 144 +++++++++++++ 6 files changed, 430 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 BeaconScanner/BeaconScanner.help/Contents/Info.plist create mode 100644 BeaconScanner/BeaconScanner.help/Contents/Resources/en.lproj/index.html diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f09216a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,54 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + branches: + - master + +env: + SCHEME: BeaconScanner + WORKSPACE: BeaconScanner.xcworkspace + +jobs: + build-and-test: + name: Build & Test + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Select Xcode + run: sudo xcode-select -s /Applications/Xcode.app/Contents/Developer + + - name: Show Xcode Version + run: xcodebuild -version + + - name: Install CocoaPods + run: | + gem install cocoapods + pod install + + - name: Build + run: | + xcodebuild build \ + -workspace "$WORKSPACE" \ + -scheme "$SCHEME" \ + -configuration Debug \ + -destination 'platform=macOS' \ + CODE_SIGN_IDENTITY="-" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO + + - name: Run Tests + run: | + xcodebuild test \ + -workspace "$WORKSPACE" \ + -scheme "$SCHEME" \ + -destination 'platform=macOS' \ + CODE_SIGN_IDENTITY="-" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..83fc5a8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,194 @@ +name: Build and Release + +on: + push: + tags: + - 'v*' + +env: + SCHEME: BeaconScanner + WORKSPACE: BeaconScanner.xcworkspace + APP_NAME: Beacon Scanner + +permissions: + contents: write + +jobs: + build-and-release: + name: Build, Sign, Notarize & Release + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Select Xcode + run: sudo xcode-select -s /Applications/Xcode.app/Contents/Developer + + - name: Show Xcode Version + run: xcodebuild -version + + - name: Install CocoaPods + run: | + gem install cocoapods + pod install + + - name: Build Archive + run: | + xcodebuild archive \ + -workspace "$WORKSPACE" \ + -scheme "$SCHEME" \ + -configuration Release \ + -archivePath "build/$APP_NAME.xcarchive" \ + CODE_SIGN_IDENTITY="-" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO + + - name: Export App + run: | + # Create export options plist for unsigned export + cat > ExportOptions.plist << EOF + + + + + method + mac-application + signingStyle + manual + signingCertificate + - + + + EOF + + # Export the archive to an app bundle + xcodebuild -exportArchive \ + -archivePath "build/$APP_NAME.xcarchive" \ + -exportPath "build/export" \ + -exportOptionsPlist ExportOptions.plist \ + CODE_SIGN_IDENTITY="-" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO || true + + # Fallback: copy from archive if export fails + if [ ! -d "build/export/$APP_NAME.app" ]; then + mkdir -p "build/export" + cp -R "build/$APP_NAME.xcarchive/Products/Applications/$APP_NAME.app" "build/export/" + fi + + # Verify app exists + ls -la "build/export/" + + - name: Import Certificate + env: + CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }} + CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + run: | + # Decode certificate + echo "$CERTIFICATE_P12" | base64 --decode > certificate.p12 + + # Create temporary keychain + security create-keychain -p "temppass" build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p "temppass" build.keychain + security set-keychain-settings -t 3600 -u build.keychain + + # Import certificate + security import certificate.p12 -k build.keychain -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/productsign + + # Allow codesign to access the keychain + security set-key-partition-list -S apple-tool:,apple: -s -k "temppass" build.keychain + + # List available signing identities + security find-identity -v -p codesigning build.keychain + + # Clean up + rm certificate.p12 + + - name: Code Sign App + env: + TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + run: | + # Find the Developer ID Application certificate + IDENTITY=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application" | head -1 | sed 's/.*"\(.*\)".*/\1/') + echo "Using identity: $IDENTITY" + + # Sign the app with hardened runtime (required for notarization) + codesign --force --options runtime --timestamp \ + --sign "$IDENTITY" \ + "build/export/$APP_NAME.app" + + # Verify signature + codesign --verify --verbose "build/export/$APP_NAME.app" + + - name: Notarize App + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + run: | + # Create zip for notarization + ditto -c -k --keepParent "build/export/$APP_NAME.app" "build/notarize.zip" + + # Submit for notarization + xcrun notarytool submit "build/notarize.zip" \ + --apple-id "$APPLE_ID" \ + --password "$APPLE_APP_PASSWORD" \ + --team-id "$TEAM_ID" \ + --wait + + # Staple the notarization ticket + xcrun stapler staple "build/export/$APP_NAME.app" + + # Verify stapling + xcrun stapler validate "build/export/$APP_NAME.app" + + # Clean up + rm "build/notarize.zip" + + - name: Create DMG + run: | + # Create a temporary directory for DMG contents + mkdir -p dmg_contents + cp -R "build/export/$APP_NAME.app" dmg_contents/ + + # Create a symlink to Applications folder + ln -s /Applications dmg_contents/Applications + + # Create the DMG + hdiutil create -volname "$APP_NAME" \ + -srcfolder dmg_contents \ + -ov -format UDZO \ + "build/$APP_NAME-${{ github.ref_name }}.dmg" + + - name: Create ZIP + run: | + cd build/export + ditto -c -k --keepParent "$APP_NAME.app" "../$APP_NAME-${{ github.ref_name }}.zip" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + name: ${{ env.APP_NAME }} ${{ github.ref_name }} + body: | + ## ${{ env.APP_NAME }} ${{ github.ref_name }} + + ### Installation + 1. Download `${{ env.APP_NAME }}-${{ github.ref_name }}.dmg` + 2. Open the DMG and drag ${{ env.APP_NAME }} to your Applications folder + 3. Launch ${{ env.APP_NAME }} from Applications + + ### Requirements + - macOS 10.13 or later + - Bluetooth 4.0 (BLE) support + + ### What's New + See commit history for details. + files: | + build/${{ env.APP_NAME }}-${{ github.ref_name }}.dmg + build/${{ env.APP_NAME }}-${{ github.ref_name }}.zip + draft: false + prerelease: ${{ contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/BeaconScanner.xcodeproj/project.pbxproj b/BeaconScanner.xcodeproj/project.pbxproj index 3b78e9c..fa621fd 100644 --- a/BeaconScanner.xcodeproj/project.pbxproj +++ b/BeaconScanner.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 67F09DA61A168C8C006AB4AD /* HGBeaconHistory.m in Sources */ = {isa = PBXBuildFile; fileRef = 67F09DA51A168C8C006AB4AD /* HGBeaconHistory.m */; }; 8CB1EB1C03F85C6BC44E7953 /* Pods_BeaconScannerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 679322E4189BD1AF0383A0E4 /* Pods_BeaconScannerTests.framework */; }; CFEFC1271DB916E900EF1F52 /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CFEFC1261DB916E900EF1F52 /* CoreBluetooth.framework */; }; + 67HELP000200000000000002 /* BeaconScanner.help in Resources */ = {isa = PBXBuildFile; fileRef = 67HELP000100000000000001 /* BeaconScanner.help */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -78,6 +79,7 @@ 9A6C906BEBF5F431A0D2AD95 /* Pods-Beacon Scanner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Beacon Scanner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Beacon Scanner/Pods-Beacon Scanner.debug.xcconfig"; sourceTree = ""; }; CFEFC1261DB916E900EF1F52 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; }; D6DB55C56AA50598A5767271 /* Pods-BeaconScannerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BeaconScannerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-BeaconScannerTests/Pods-BeaconScannerTests.release.xcconfig"; sourceTree = ""; }; + 67HELP000100000000000001 /* BeaconScanner.help */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = BeaconScanner.help; path = BeaconScanner/BeaconScanner.help; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -191,6 +193,7 @@ 6772269218FC3D2F00804FDA /* BeaconScanner-Info.plist */, 6772269318FC3D2F00804FDA /* BeaconScanner-Prefix.pch */, 677226A418FC3D2F00804FDA /* main.m */, + 67HELP000100000000000001 /* BeaconScanner.help */, ); name = "Supporting Files"; sourceTree = ""; @@ -301,6 +304,7 @@ 677226AE18FC3D2F00804FDA /* Images.xcassets in Resources */, 679835D718FC615D00E2E6D5 /* Credits.rtf in Resources */, 679835D818FC615D00E2E6D5 /* InfoPlist.strings in Resources */, + 67HELP000200000000000002 /* BeaconScanner.help in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/BeaconScanner/BeaconScanner-Info.plist b/BeaconScanner/BeaconScanner-Info.plist index 4a90680..afac8c1 100644 --- a/BeaconScanner/BeaconScanner-Info.plist +++ b/BeaconScanner/BeaconScanner-Info.plist @@ -34,5 +34,9 @@ NSApplication NSBluetoothAlwaysUsageDescription BeaconScanner needs Bluetooth access to detect and display nearby iBeacon devices. + CFBundleHelpBookFolder + BeaconScanner.help + CFBundleHelpBookName + nyc.welles.BeaconScanner.help diff --git a/BeaconScanner/BeaconScanner.help/Contents/Info.plist b/BeaconScanner/BeaconScanner.help/Contents/Info.plist new file mode 100644 index 0000000..68aabc7 --- /dev/null +++ b/BeaconScanner/BeaconScanner.help/Contents/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleIdentifier + nyc.welles.BeaconScanner.help + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + BeaconScanner Help + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + hbwr + CFBundleVersion + 1 + HPDBookAccessPath + index.html + HPDBookIndexPath + BeaconScanner.helpindex + HPDBookTitle + BeaconScanner Help + HPDBookType + 3 + + diff --git a/BeaconScanner/BeaconScanner.help/Contents/Resources/en.lproj/index.html b/BeaconScanner/BeaconScanner.help/Contents/Resources/en.lproj/index.html new file mode 100644 index 0000000..a5c4440 --- /dev/null +++ b/BeaconScanner/BeaconScanner.help/Contents/Resources/en.lproj/index.html @@ -0,0 +1,144 @@ + + + + + + + + BeaconScanner Help + + + +

BeaconScanner Help

+ +

BeaconScanner is a macOS application for detecting and monitoring iBeacon devices via Bluetooth Low Energy (BLE).

+ +

Getting Started

+
    +
  1. Enable Bluetooth - Ensure Bluetooth is turned on in System Settings.
  2. +
  3. Grant Permission - When prompted, allow BeaconScanner to use Bluetooth.
  4. +
  5. Start Scanning - Click the Start Scanning button to begin detecting nearby iBeacons.
  6. +
+ +

Understanding the Display

+

Detected beacons are shown in a table with the following columns:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ColumnDescription
UUIDThe beacon's unique proximity identifier (128-bit)
MajorGroup identifier (0-65535), typically represents a location or zone
MinorIndividual identifier (0-65535), typically represents a specific beacon
RSSIReceived Signal Strength Indicator in dBm (closer to 0 = stronger signal)
PowerCalibrated signal strength at 1 meter (used for distance estimation)
Last SeenTimestamp of the most recent detection
+ +

Features

+ +

Recording Mode

+

Enable the Recording checkbox to keep beacons in the list even after they go out of range. This is useful for logging all beacons detected during a session.

+ +

Copy UUID

+

Select a beacon and press ⌘C to copy its UUID to the clipboard.

+ +

Beacon Timeout

+

By default, beacons that haven't been detected for 15 seconds are removed from the list (unless Recording mode is enabled).

+ +
+ Note: BeaconScanner requires a Mac with Bluetooth 4.0 (BLE) support. Most Macs from 2012 and later include this hardware. +
+ +

iBeacon Protocol

+

iBeacon is Apple's protocol for Bluetooth proximity sensing. Each beacon broadcasts:

+
    +
  • A Proximity UUID identifying the beacon network/owner
  • +
  • A Major value for grouping beacons (e.g., by store location)
  • +
  • A Minor value for individual beacon identification
  • +
  • A Measured Power value for distance calibration
  • +
+ +

Troubleshooting

+ +

No beacons detected

+
    +
  • Verify Bluetooth is enabled in System Settings
  • +
  • Ensure your Mac supports Bluetooth Low Energy (4.0+)
  • +
  • Check that nearby iBeacons are powered on and advertising
  • +
  • Try moving closer to the beacon
  • +
+ +

Scanning won't start

+
    +
  • Check that BeaconScanner has Bluetooth permission in System Settings → Privacy & Security → Bluetooth
  • +
  • Try quitting and restarting the application
  • +
+ +

+ © 2026 Michael L. Welles. All rights reserved. +

+ + From 3c1b18cfd6cc3295f6d0ecf30291d9d8c57b1eda Mon Sep 17 00:00:00 2001 From: Michael Welles Date: Wed, 21 Jan 2026 23:02:49 -0500 Subject: [PATCH 06/11] Add tests for Help menu configuration - Test CFBundleHelpBookFolder is configured - Test CFBundleHelpBookName is configured - Test help bundle exists in app resources - Test help index.html exists - Test help content contains expected keywords --- BeaconScannerTests/BeaconScannerTests.m | 64 +++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/BeaconScannerTests/BeaconScannerTests.m b/BeaconScannerTests/BeaconScannerTests.m index 5490810..dcaf5aa 100644 --- a/BeaconScannerTests/BeaconScannerTests.m +++ b/BeaconScannerTests/BeaconScannerTests.m @@ -53,4 +53,68 @@ - (void)testManagerScanningStop XCTAssertFalse([[HGBeaconScanner sharedBeaconScanner] scanning], @"Manager can stop scanning"); } +#pragma mark - Help Menu Tests + +- (void)testHelpBookFolderConfigured +{ + NSBundle *bundle = [NSBundle mainBundle]; + NSString *helpBookFolder = [bundle objectForInfoDictionaryKey:@"CFBundleHelpBookFolder"]; + XCTAssertNotNil(helpBookFolder, @"CFBundleHelpBookFolder should be configured in Info.plist"); + XCTAssertEqualObjects(helpBookFolder, @"BeaconScanner.help", @"Help book folder should be BeaconScanner.help"); +} + +- (void)testHelpBookNameConfigured +{ + NSBundle *bundle = [NSBundle mainBundle]; + NSString *helpBookName = [bundle objectForInfoDictionaryKey:@"CFBundleHelpBookName"]; + XCTAssertNotNil(helpBookName, @"CFBundleHelpBookName should be configured in Info.plist"); + XCTAssertEqualObjects(helpBookName, @"nyc.welles.BeaconScanner.help", @"Help book name should match bundle identifier"); +} + +- (void)testHelpBundleExists +{ + NSBundle *bundle = [NSBundle mainBundle]; + NSString *helpBookFolder = [bundle objectForInfoDictionaryKey:@"CFBundleHelpBookFolder"]; + XCTAssertNotNil(helpBookFolder, @"Help book folder must be configured"); + + NSString *helpPath = [[bundle resourcePath] stringByAppendingPathComponent:helpBookFolder]; + BOOL isDirectory = NO; + BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:helpPath isDirectory:&isDirectory]; + + XCTAssertTrue(exists, @"Help bundle should exist at %@", helpPath); + XCTAssertTrue(isDirectory, @"Help bundle should be a directory"); +} + +- (void)testHelpIndexExists +{ + NSBundle *bundle = [NSBundle mainBundle]; + NSString *helpBookFolder = [bundle objectForInfoDictionaryKey:@"CFBundleHelpBookFolder"]; + XCTAssertNotNil(helpBookFolder, @"Help book folder must be configured"); + + NSString *helpPath = [[bundle resourcePath] stringByAppendingPathComponent:helpBookFolder]; + NSString *indexPath = [helpPath stringByAppendingPathComponent:@"Contents/Resources/en.lproj/index.html"]; + + BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:indexPath]; + XCTAssertTrue(exists, @"Help index.html should exist at %@", indexPath); +} + +- (void)testHelpContentNotEmpty +{ + NSBundle *bundle = [NSBundle mainBundle]; + NSString *helpBookFolder = [bundle objectForInfoDictionaryKey:@"CFBundleHelpBookFolder"]; + XCTAssertNotNil(helpBookFolder, @"Help book folder must be configured"); + + NSString *helpPath = [[bundle resourcePath] stringByAppendingPathComponent:helpBookFolder]; + NSString *indexPath = [helpPath stringByAppendingPathComponent:@"Contents/Resources/en.lproj/index.html"]; + + NSError *error = nil; + NSString *content = [NSString stringWithContentsOfFile:indexPath encoding:NSUTF8StringEncoding error:&error]; + + XCTAssertNil(error, @"Should be able to read help content: %@", error); + XCTAssertNotNil(content, @"Help content should not be nil"); + XCTAssertGreaterThan(content.length, 100, @"Help content should have substantial content"); + XCTAssertTrue([content containsString:@"BeaconScanner"], @"Help content should mention BeaconScanner"); + XCTAssertTrue([content containsString:@"iBeacon"], @"Help content should mention iBeacon"); +} + @end From eeeedf57f94e532c66c95973aff2498d50b6e803 Mon Sep 17 00:00:00 2001 From: Michael Welles Date: Wed, 21 Jan 2026 23:04:29 -0500 Subject: [PATCH 07/11] Improve Bluetooth test reliability - Use XCTWaiter for proper async timeout handling without failure - Subscribe to RACReplaySubject for immediate cached state delivery - Increase timeout to 10 seconds for slow Bluetooth initialization --- BeaconScannerTests/BeaconScannerTests.m | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/BeaconScannerTests/BeaconScannerTests.m b/BeaconScannerTests/BeaconScannerTests.m index dcaf5aa..60b3661 100644 --- a/BeaconScannerTests/BeaconScannerTests.m +++ b/BeaconScannerTests/BeaconScannerTests.m @@ -9,6 +9,7 @@ #import #import "HGBeaconScanner.h" #import "HGBeacon.h" +#import @interface DesktopBeaconTests : XCTestCase @@ -30,18 +31,24 @@ - (void)tearDown - (void)testManagerScanningStart { - // Give Bluetooth time to initialize HGBeaconScanner *scanner = [HGBeaconScanner sharedBeaconScanner]; - // Brief wait for CBCentralManager to report state - NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:2.0]; - while (scanner.bluetoothState == nil && [timeout timeIntervalSinceNow] > 0) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - } + // Use XCTestExpectation with the reactive signal for proper async handling + XCTestExpectation *expectation = [self expectationWithDescription:@"Bluetooth state received"]; + __block NSString *receivedState = nil; - // Skip test if Bluetooth state isn't available or isn't powered on - XCTSkipIf(scanner.bluetoothState == nil, @"Bluetooth state not available"); - XCTSkipIf(scanner.bluetoothState != HGGBluetoothStatePoweredOn, @"Bluetooth not powered on"); + // Subscribe to bluetooth state signal - RACReplaySubject will replay last value + [[scanner.bluetoothStateSignal take:1] subscribeNext:^(NSString *state) { + receivedState = state; + [expectation fulfill]; + }]; + + // Wait up to 10 seconds for Bluetooth to report state + XCTWaiterResult result = [XCTWaiter waitForExpectations:@[expectation] timeout:10.0]; + + // Skip if we didn't receive state in time + XCTSkipIf(result != XCTWaiterResultCompleted, @"Bluetooth state not available (timeout)"); + XCTSkipIf(receivedState != HGGBluetoothStatePoweredOn, @"Bluetooth not powered on (state: %@)", receivedState); [scanner startScanning]; XCTAssertTrue(scanner.scanning, @"Manager can start scanning"); From ccbbbfdcc174a06a15a6730ecd2c2cdb87b81876 Mon Sep 17 00:00:00 2001 From: Michael Welles Date: Wed, 21 Jan 2026 23:10:13 -0500 Subject: [PATCH 08/11] Add shared Xcode scheme for CI The BeaconScanner scheme was only available as a user-specific scheme in xcuserdata/, which is not committed to version control. This caused CI builds to fail with "workspace does not contain a scheme named BeaconScanner" error. Create a shared scheme in xcshareddata/xcschemes/ that will be available to CI runners after checkout. --- .../xcschemes/BeaconScanner.xcscheme | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 BeaconScanner.xcodeproj/xcshareddata/xcschemes/BeaconScanner.xcscheme diff --git a/BeaconScanner.xcodeproj/xcshareddata/xcschemes/BeaconScanner.xcscheme b/BeaconScanner.xcodeproj/xcshareddata/xcschemes/BeaconScanner.xcscheme new file mode 100644 index 0000000..60e0dc3 --- /dev/null +++ b/BeaconScanner.xcodeproj/xcshareddata/xcschemes/BeaconScanner.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a0476e7b9c1212b4ff53925ca24b1f46406f90a5 Mon Sep 17 00:00:00 2001 From: Michael Welles Date: Wed, 21 Jan 2026 23:10:58 -0500 Subject: [PATCH 09/11] Remove user-specific Xcode data and update .gitignore User-specific Xcode data (xcuserdata/) should not be tracked in version control as it contains personal IDE preferences like breakpoints, window layouts, and individual scheme copies. Now that we have a shared scheme in xcshareddata/, the user-specific schemes are redundant and can be safely removed. --- .gitignore | 4 + .../xcschemes/Beacon Scanner.xcscheme | 112 ------------------ .../xcschemes/xcschememanagement.plist | 27 ----- .../xcschemes/BeaconScanner.xcscheme | 101 ---------------- .../xcschemes/xcschememanagement.plist | 27 ----- .../xcschemes/Beacon Scanner.xcscheme | 112 ------------------ 6 files changed, 4 insertions(+), 379 deletions(-) delete mode 100644 BeaconScanner.xcodeproj/xcuserdata/mviamari.xcuserdatad/xcschemes/Beacon Scanner.xcscheme delete mode 100644 BeaconScanner.xcodeproj/xcuserdata/mviamari.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 BeaconScanner.xcodeproj/xcuserdata/mwelles.xcuserdatad/xcschemes/BeaconScanner.xcscheme delete mode 100644 BeaconScanner.xcodeproj/xcuserdata/mwelles.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 BeaconScanner.xcodeproj/xcuserdata/nathanhekman.xcuserdatad/xcschemes/Beacon Scanner.xcscheme diff --git a/.gitignore b/.gitignore index 26fba04..7ee6d31 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ # Pods/ +# Xcode user-specific data +*.xcuserdata/ +xcuserdata/ + diff --git a/BeaconScanner.xcodeproj/xcuserdata/mviamari.xcuserdatad/xcschemes/Beacon Scanner.xcscheme b/BeaconScanner.xcodeproj/xcuserdata/mviamari.xcuserdatad/xcschemes/Beacon Scanner.xcscheme deleted file mode 100644 index b5e2520..0000000 --- a/BeaconScanner.xcodeproj/xcuserdata/mviamari.xcuserdatad/xcschemes/Beacon Scanner.xcscheme +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/BeaconScanner.xcodeproj/xcuserdata/mviamari.xcuserdatad/xcschemes/xcschememanagement.plist b/BeaconScanner.xcodeproj/xcuserdata/mviamari.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 2ed24ff..0000000 --- a/BeaconScanner.xcodeproj/xcuserdata/mviamari.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,27 +0,0 @@ - - - - - SchemeUserState - - Beacon Scanner.xcscheme - - orderHint - 0 - - - SuppressBuildableAutocreation - - 6753158718EF27D30024E9CE - - primary - - - 675315A818EF27D30024E9CE - - primary - - - - - diff --git a/BeaconScanner.xcodeproj/xcuserdata/mwelles.xcuserdatad/xcschemes/BeaconScanner.xcscheme b/BeaconScanner.xcodeproj/xcuserdata/mwelles.xcuserdatad/xcschemes/BeaconScanner.xcscheme deleted file mode 100644 index 1886b11..0000000 --- a/BeaconScanner.xcodeproj/xcuserdata/mwelles.xcuserdatad/xcschemes/BeaconScanner.xcscheme +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/BeaconScanner.xcodeproj/xcuserdata/mwelles.xcuserdatad/xcschemes/xcschememanagement.plist b/BeaconScanner.xcodeproj/xcuserdata/mwelles.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 6ac6c2d..0000000 --- a/BeaconScanner.xcodeproj/xcuserdata/mwelles.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,27 +0,0 @@ - - - - - SchemeUserState - - BeaconScanner.xcscheme - - orderHint - 0 - - - SuppressBuildableAutocreation - - 6753158718EF27D30024E9CE - - primary - - - 675315A818EF27D30024E9CE - - primary - - - - - diff --git a/BeaconScanner.xcodeproj/xcuserdata/nathanhekman.xcuserdatad/xcschemes/Beacon Scanner.xcscheme b/BeaconScanner.xcodeproj/xcuserdata/nathanhekman.xcuserdatad/xcschemes/Beacon Scanner.xcscheme deleted file mode 100644 index b5e2520..0000000 --- a/BeaconScanner.xcodeproj/xcuserdata/nathanhekman.xcuserdatad/xcschemes/Beacon Scanner.xcscheme +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 1d60b7f88a7c36f22af02f626485252b16c5af46 Mon Sep 17 00:00:00 2001 From: Michael Welles Date: Wed, 21 Jan 2026 23:12:49 -0500 Subject: [PATCH 10/11] Bump version to 1.2.0 --- BeaconScanner/BeaconScanner-Info.plist | 2 +- BeaconScanner/BeaconScanner.help/Contents/Info.plist | 2 +- BeaconScannerTests/BeaconScannerTests-Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BeaconScanner/BeaconScanner-Info.plist b/BeaconScanner/BeaconScanner-Info.plist index afac8c1..922f861 100644 --- a/BeaconScanner/BeaconScanner-Info.plist +++ b/BeaconScanner/BeaconScanner-Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.13 + 1.2.0 CFBundleSignature ???? CFBundleVersion diff --git a/BeaconScanner/BeaconScanner.help/Contents/Info.plist b/BeaconScanner/BeaconScanner.help/Contents/Info.plist index 68aabc7..04a2efc 100644 --- a/BeaconScanner/BeaconScanner.help/Contents/Info.plist +++ b/BeaconScanner/BeaconScanner.help/Contents/Info.plist @@ -13,7 +13,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.0 + 1.2.0 CFBundleSignature hbwr CFBundleVersion diff --git a/BeaconScannerTests/BeaconScannerTests-Info.plist b/BeaconScannerTests/BeaconScannerTests-Info.plist index 169b6f7..146bb1f 100644 --- a/BeaconScannerTests/BeaconScannerTests-Info.plist +++ b/BeaconScannerTests/BeaconScannerTests-Info.plist @@ -13,7 +13,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.0 + 1.2.0 CFBundleSignature ???? CFBundleVersion From 3d0ef719d56088cdcc3623cfd74c5ee89bceef08 Mon Sep 17 00:00:00 2001 From: Michael Welles Date: Wed, 21 Jan 2026 23:17:02 -0500 Subject: [PATCH 11/11] Pin ReactiveCocoa to 2.5 and fix deployment targets - Pin ReactiveCocoa to ~> 2.5 (last Objective-C version, 3.0+ is Swift) - Add post_install hook to force MACOSX_DEPLOYMENT_TARGET = 10.13 for all pods - Update libextobjc from 0.4 to 0.6 --- BeaconScanner.xcodeproj/project.pbxproj | 4 +- Podfile | 10 +++- Podfile.lock | 68 ++++++++++++------------- 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/BeaconScanner.xcodeproj/project.pbxproj b/BeaconScanner.xcodeproj/project.pbxproj index fa621fd..140f5cf 100644 --- a/BeaconScanner.xcodeproj/project.pbxproj +++ b/BeaconScanner.xcodeproj/project.pbxproj @@ -25,9 +25,9 @@ 679835E518FC639E00E2E6D5 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 679835D018FC614900E2E6D5 /* MainMenu.xib */; }; 67DD56FF1CE39A6800AC5458 /* HGBluetoothState.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DD56FE1CE39A6800AC5458 /* HGBluetoothState.m */; }; 67F09DA61A168C8C006AB4AD /* HGBeaconHistory.m in Sources */ = {isa = PBXBuildFile; fileRef = 67F09DA51A168C8C006AB4AD /* HGBeaconHistory.m */; }; + 67HELP000200000000000002 /* BeaconScanner.help in Resources */ = {isa = PBXBuildFile; fileRef = 67HELP000100000000000001 /* BeaconScanner.help */; }; 8CB1EB1C03F85C6BC44E7953 /* Pods_BeaconScannerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 679322E4189BD1AF0383A0E4 /* Pods_BeaconScannerTests.framework */; }; CFEFC1271DB916E900EF1F52 /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CFEFC1261DB916E900EF1F52 /* CoreBluetooth.framework */; }; - 67HELP000200000000000002 /* BeaconScanner.help in Resources */ = {isa = PBXBuildFile; fileRef = 67HELP000100000000000001 /* BeaconScanner.help */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -74,12 +74,12 @@ 67DD56FE1CE39A6800AC5458 /* HGBluetoothState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HGBluetoothState.m; path = BeaconScanner/HGBluetoothState.m; sourceTree = SOURCE_ROOT; }; 67F09DA41A168C8C006AB4AD /* HGBeaconHistory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HGBeaconHistory.h; path = BeaconScanner/HGBeaconHistory.h; sourceTree = SOURCE_ROOT; }; 67F09DA51A168C8C006AB4AD /* HGBeaconHistory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HGBeaconHistory.m; path = BeaconScanner/HGBeaconHistory.m; sourceTree = SOURCE_ROOT; }; + 67HELP000100000000000001 /* BeaconScanner.help */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = BeaconScanner.help; path = BeaconScanner/BeaconScanner.help; sourceTree = SOURCE_ROOT; }; 6853233AA0EF71ED59FA5B62 /* Pods_Beacon_Scanner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Beacon_Scanner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 822302367CD0BC06E4EBAADE /* Pods-Beacon Scanner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Beacon Scanner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Beacon Scanner/Pods-Beacon Scanner.release.xcconfig"; sourceTree = ""; }; 9A6C906BEBF5F431A0D2AD95 /* Pods-Beacon Scanner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Beacon Scanner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Beacon Scanner/Pods-Beacon Scanner.debug.xcconfig"; sourceTree = ""; }; CFEFC1261DB916E900EF1F52 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; }; D6DB55C56AA50598A5767271 /* Pods-BeaconScannerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BeaconScannerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-BeaconScannerTests/Pods-BeaconScannerTests.release.xcconfig"; sourceTree = ""; }; - 67HELP000100000000000001 /* BeaconScanner.help */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = BeaconScanner.help; path = BeaconScanner/BeaconScanner.help; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ diff --git a/Podfile b/Podfile index bf6d2f5..98b4f98 100644 --- a/Podfile +++ b/Podfile @@ -2,7 +2,7 @@ platform :osx, '10.13' target 'Beacon Scanner' do use_frameworks! - pod 'ReactiveCocoa' + pod 'ReactiveCocoa', '~> 2.5' # Last Objective-C version (3.0+ is Swift) pod 'BlocksKit' pod 'libextobjc' @@ -11,3 +11,11 @@ target 'Beacon Scanner' do end end +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.13' + end + end +end + diff --git a/Podfile.lock b/Podfile.lock index b4172b7..1dfa9ec 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -8,53 +8,53 @@ PODS: - BlocksKit/UIKit - BlocksKit/Core (2.2.5) - BlocksKit/DynamicDelegate (2.2.5) - - libextobjc (0.4): - - libextobjc/EXTADT (= 0.4) - - libextobjc/EXTConcreteProtocol (= 0.4) - - libextobjc/EXTKeyPathCoding (= 0.4) - - libextobjc/EXTNil (= 0.4) - - libextobjc/EXTSafeCategory (= 0.4) - - libextobjc/EXTScope (= 0.4) - - libextobjc/EXTSelectorChecking (= 0.4) - - libextobjc/EXTSynthesize (= 0.4) - - "libextobjc/NSInvocation+EXT (= 0.4)" - - "libextobjc/NSMethodSignature+EXT (= 0.4)" - - libextobjc/RuntimeExtensions (= 0.4) - - libextobjc/UmbrellaHeader (= 0.4) - - libextobjc/EXTADT (0.4): + - libextobjc (0.6): + - libextobjc/EXTADT (= 0.6) + - libextobjc/EXTConcreteProtocol (= 0.6) + - libextobjc/EXTKeyPathCoding (= 0.6) + - libextobjc/EXTNil (= 0.6) + - libextobjc/EXTSafeCategory (= 0.6) + - libextobjc/EXTScope (= 0.6) + - libextobjc/EXTSelectorChecking (= 0.6) + - libextobjc/EXTSynthesize (= 0.6) + - "libextobjc/NSInvocation+EXT (= 0.6)" + - "libextobjc/NSMethodSignature+EXT (= 0.6)" + - libextobjc/RuntimeExtensions (= 0.6) + - libextobjc/UmbrellaHeader (= 0.6) + - libextobjc/EXTADT (0.6): - libextobjc/RuntimeExtensions - - libextobjc/EXTConcreteProtocol (0.4): + - libextobjc/EXTConcreteProtocol (0.6): - libextobjc/RuntimeExtensions - - libextobjc/EXTKeyPathCoding (0.4): + - libextobjc/EXTKeyPathCoding (0.6): - libextobjc/RuntimeExtensions - - libextobjc/EXTNil (0.4): + - libextobjc/EXTNil (0.6): - libextobjc/RuntimeExtensions - - libextobjc/EXTSafeCategory (0.4): + - libextobjc/EXTSafeCategory (0.6): - libextobjc/RuntimeExtensions - - libextobjc/EXTScope (0.4): + - libextobjc/EXTScope (0.6): - libextobjc/RuntimeExtensions - - libextobjc/EXTSelectorChecking (0.4): + - libextobjc/EXTSelectorChecking (0.6): - libextobjc/RuntimeExtensions - - libextobjc/EXTSynthesize (0.4): + - libextobjc/EXTSynthesize (0.6): - libextobjc/RuntimeExtensions - - "libextobjc/NSInvocation+EXT (0.4)": + - "libextobjc/NSInvocation+EXT (0.6)": - libextobjc/RuntimeExtensions - - "libextobjc/NSMethodSignature+EXT (0.4)": + - "libextobjc/NSMethodSignature+EXT (0.6)": - libextobjc/RuntimeExtensions - - libextobjc/RuntimeExtensions (0.4) - - libextobjc/UmbrellaHeader (0.4) - - ReactiveCocoa (2.3): - - ReactiveCocoa/UI (= 2.3) - - ReactiveCocoa/Core (2.3): + - libextobjc/RuntimeExtensions (0.6) + - libextobjc/UmbrellaHeader (0.6) + - ReactiveCocoa (2.5): + - ReactiveCocoa/UI (= 2.5) + - ReactiveCocoa/Core (2.5): - ReactiveCocoa/no-arc - - ReactiveCocoa/no-arc (2.3) - - ReactiveCocoa/UI (2.3): + - ReactiveCocoa/no-arc (2.5) + - ReactiveCocoa/UI (2.5): - ReactiveCocoa/Core DEPENDENCIES: - BlocksKit - libextobjc - - ReactiveCocoa + - ReactiveCocoa (~> 2.5) SPEC REPOS: trunk: @@ -64,9 +64,9 @@ SPEC REPOS: SPEC CHECKSUMS: BlocksKit: 8639b2848dd51c35c957af4d9fbdc7a769a1fc12 - libextobjc: f2802d4262f6885570df9799747409ceb1de602c - ReactiveCocoa: 81ee2fb7df2dfc6bd52b5d70473662195e20ca7d + libextobjc: aed8b7d2b07f511c1a7ecaacb33ea03e327c69b7 + ReactiveCocoa: e2db045570aa97c695e7aa97c2bcab222ae51f4a -PODFILE CHECKSUM: f034a96674c5d482262604d9d637ce2e5a48ac79 +PODFILE CHECKSUM: 08be8fbc49c90237034bfaac302f18b3448b1321 COCOAPODS: 1.16.2