├── ios-beacon-tools.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── ios-beacon-tools-Bridging-Header.h ├── ios-beacon-tools ├── RNLURLBeaconCompressor.h ├── main.m ├── RNLSignalMeasurement.m ├── RNLBeaconTableViewController.h ├── RNLSignalMeasurement.h ├── AppDelegate.h ├── RNLAppDelegate.h ├── RNLBeaconTracker.h ├── RNLLocationTracker.h ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── RNLBeaconScanner.h ├── RNLBeacon+Distance.h ├── RNLBeaconParser.h ├── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.xib ├── Info.plist ├── RNLURLBeaconCompressor.m ├── RNLBeacon.h ├── RNLAppDelegate.m ├── AppDelegate.m ├── RNLBeaconTracker.m ├── Main.storyboard ├── RNLBeacon+Distance.m ├── RNLLocationTracker.m ├── RNLBeaconTableViewController.m ├── RNLBeaconScanner.m ├── RNLBeacon.m └── RNLBeaconParser.m ├── ios-beacon-toolsTests ├── Info.plist └── ios_beacon_toolsTests.m ├── sample.swift ├── README.md ├── .gitignore └── LICENSE /ios-beacon-tools.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios-beacon-tools-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #include "RNLBeaconScanner.h" 6 | #include "RNLURLBeaconCompressor.h" 7 | #include "RNLBeacon+Distance.h" 8 | -------------------------------------------------------------------------------- /ios-beacon-tools.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLURLBeaconCompressor.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNLURLBeaconCompressor.h 3 | // iBeaconLoc 4 | // 5 | // Created by David Young on 1/28/16. 6 | // Copyright © 2016 RadiusNetworks. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RNLURLBeaconCompressor : NSObject 12 | + (NSString *)URLStringFromEddystoneURLIdentifier:(NSString *)identifier; 13 | @end 14 | -------------------------------------------------------------------------------- /ios-beacon-tools/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ios-beacon-tools 4 | // 5 | // Created by David Young on 9/8/15. 6 | // Copyright (c) 2015 Altbeacon.org. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ios-beacon-toolsTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.altbeacon.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios-beacon-toolsTests/ios_beacon_toolsTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ios_beacon_toolsTests.m 3 | // ios-beacon-toolsTests 4 | // 5 | // Created by David Young on 9/8/15. 6 | // Copyright (c) 2015 Altbeacon.org. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface ios_beacon_toolsTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation ios_beacon_toolsTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLSignalMeasurement.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import "RNLSignalMeasurement.h" 27 | 28 | @implementation RNLSignalMeasurement 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeaconTableViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | #import 26 | 27 | @interface RNLBeaconTableViewController : UITableViewController 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLSignalMeasurement.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import 27 | 28 | @interface RNLSignalMeasurement : NSObject 29 | @property NSNumber *rssi; 30 | @property NSDate *timestamp; 31 | @end 32 | -------------------------------------------------------------------------------- /ios-beacon-tools/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import 27 | 28 | @interface AppDelegate : UIResponder 29 | 30 | @property (strong, nonatomic) UIWindow *window; 31 | 32 | 33 | @end 34 | 35 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLAppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import 27 | 28 | @interface RNLAppDelegate : UIResponder 29 | 30 | @property (strong, nonatomic) UIWindow *window; 31 | 32 | @end 33 | 34 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeaconTracker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | #import 26 | 27 | @interface RNLBeaconTracker : NSObject 28 | @property (readonly) NSArray *trackedBeacons; 29 | -(void) updateWithRangedBeacons: (NSArray *) beacons; 30 | @end 31 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLLocationTracker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author James Nebeker 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import 27 | #import "RNLBeaconTracker.h" 28 | #import "RNLBeacon.h" 29 | 30 | @interface RNLLocationTracker : NSObject 31 | @property (readonly) RNLBeacon *closestBeacon; 32 | @property Boolean useCoreLocationRanging; 33 | @property RNLBeaconTracker *beaconTracker; 34 | 35 | + (id)sharedLocationTracker; 36 | - (void)didRangeBeaconsInRegion:(NSArray *)beacons; 37 | 38 | @end -------------------------------------------------------------------------------- /ios-beacon-tools/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeaconScanner.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author Scott Yoder 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import 27 | #import 28 | #import "RNLBeacon.h" 29 | 30 | @interface RNLBeaconScanner : NSObject 31 | 32 | + (instancetype) sharedBeaconScanner; 33 | 34 | - (void) startScanning; 35 | - (void) stopScanning; 36 | - (NSNumber *) calibratedRSSIFor: (RNLBeacon *)beacon; 37 | - (NSArray *) trackedBeacons; 38 | 39 | @property Boolean debugEnabled; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeacon+Distance.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author James Nebeker 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import 27 | #import "RNLSignalMeasurement.h" 28 | #import "RNLBeacon.h" 29 | #import 30 | 31 | @interface RNLBeacon (Distance) 32 | 33 | @property NSMutableArray *signalMeasurements; 34 | @property NSDate *lastDetected; 35 | @property NSDate *lastCalculated; 36 | @property NSNumber *runningAverageRssi; 37 | @property (readonly) CLLocationAccuracy distance; 38 | 39 | -(void) applyRssiMeasurements: (RNLBeacon *)beacon; 40 | +(double) secondsToAverage; 41 | +(void) secondsToAverage: (double) seconds; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeaconParser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import 27 | #import 28 | #import "RNLBeacon.h" 29 | 30 | @interface RNLBeaconParser : NSObject 31 | -(BOOL) setBeaconLayout: (NSString *)beaconLayout error:(NSError **)errorPtr; 32 | -(RNLBeacon *) fromScanData: (NSData *)scanData withRssi: (NSNumber *) rssi forDevice: (CBPeripheral *)device serviceUuid: (NSNumber *) serviceUuid; 33 | -(RNLBeacon *) fromScanData: (NSData *)scanData withRssi: (NSNumber *) rssi forDevice: (CBPeripheral *)device serviceUuid: (NSNumber *) serviceUuid withBeacon: (RNLBeacon *)beacon; 34 | @end 35 | -------------------------------------------------------------------------------- /ios-beacon-tools/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /sample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // sample.swift 3 | // ios-beacon-tools 4 | // 5 | // Created by David G. Young on 7/19/18. 6 | // Copyright © 2018 Altbeacon.org. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Sample { 12 | var scanner: RNLBeaconScanner? 13 | func sample() { 14 | scanner = RNLBeaconScanner.shared() 15 | scanner?.startScanning() 16 | 17 | // Execute this code periodically (every second or so) to view the beacons detected 18 | if let detectedBeacons = scanner?.trackedBeacons() as? [RNLBeacon] { 19 | for beacon in detectedBeacons { 20 | if (beacon.beaconTypeCode.intValue == 0xbeac) { 21 | // this is an AltBeacon 22 | NSLog("Detected AltBeacon id1: %@ id2: %@ id3: %@", beacon.id1, beacon.id2, beacon.id3) 23 | } 24 | else if (beacon.beaconTypeCode.intValue == 0x00 && beacon.serviceUuid.intValue == 0xFEAA) { 25 | // this is eddystone uid 26 | NSLog("Detected EDDYSTONE-UID with namespace %@ instance %@", beacon.id1, beacon.id2) 27 | } 28 | else if (beacon.beaconTypeCode.intValue == 0x10 && beacon.serviceUuid.intValue == 0xFEAA) { 29 | // this is eddystone url 30 | NSLog("Detected EDDYSTONE-URL with %@", RNLURLBeaconCompressor.urlString(fromEddystoneURLIdentifier: beacon.id1)) 31 | } 32 | else { 33 | NSLog("Some other beacon detectd") 34 | // some other beacon type 35 | } 36 | NSLog("The beacon is about %.1f meters away", beacon.distance) 37 | 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ios-beacon-tools/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.altbeacon.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | NSBluetoothAlwaysUsageDescription 47 | Bluetooth is required to find Beacons 48 | 49 | 50 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLURLBeaconCompressor.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNLURLBeaconCompressor.m 3 | // iBeaconLoc 4 | // 5 | // Created by David Young on 1/28/16. 6 | // Copyright © 2016 RadiusNetworks. All rights reserved. 7 | // 8 | 9 | #import "RNLURLBeaconCompressor.h" 10 | #import "RNLBeacon.h" 11 | @implementation RNLURLBeaconCompressor 12 | 13 | + (NSString *)URLStringFromEddystoneURLIdentifier:(NSString *)identifier { 14 | NSData *data = [RNLBeacon dataFromIdentifier: identifier]; 15 | 16 | return [RNLURLBeaconCompressor URLStringFromEddystoneURLData:data]; 17 | } 18 | 19 | 20 | + (NSString *)URLStringFromEddystoneURLData:(NSData *)URLData 21 | { 22 | NSMutableString *URLString; 23 | for (int i = 0; i < URLData.length; i++) 24 | { 25 | NSArray *prefixes = @[@"http://www.", @"https://www.", @"http://", @"https://"]; 26 | NSArray *expansions = @[@".com/", @".org/", @".edu/", @".net/", @".info/", @".biz/", @".gov/", @".com", @".org", @".edu", @".net", @".info", @".biz", @".gov"]; 27 | 28 | Byte aByte; 29 | [URLData getBytes:&aByte range:NSMakeRange(i, 1)]; 30 | NSInteger index = aByte; 31 | if (i == 0) 32 | { 33 | // this is the prefix byte 34 | if (index < prefixes.count) 35 | { 36 | URLString = [NSMutableString stringWithString:prefixes[index]]; 37 | } 38 | else 39 | { 40 | return nil; 41 | } 42 | } 43 | else if ( index <= 13) 44 | { 45 | [URLString appendString:expansions[index]]; 46 | } 47 | else if ((index >= 14) && (index <= 32)) 48 | { 49 | // this is a reserved value 50 | return nil; 51 | } 52 | else if ((index >= 127) && (index <= 255)) 53 | { 54 | // this is a reserved value 55 | return nil; 56 | } 57 | else 58 | { 59 | [URLString appendString:[[NSString alloc]initWithBytes:&aByte length:1 encoding:NSUTF8StringEncoding]]; 60 | } 61 | 62 | } 63 | 64 | return URLString; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeacon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import 27 | #import 28 | 29 | @interface RNLBeacon : NSObject 30 | @property (strong, nonatomic) NSArray *identifiers; 31 | @property (strong, nonatomic) NSArray *dataFields; 32 | @property (strong, nonatomic) NSNumber *measuredPower; 33 | @property (strong, nonatomic) NSNumber *rssi; 34 | @property (strong, nonatomic) NSNumber *beaconTypeCode; 35 | @property (nonatomic) BOOL extraFrame; 36 | // This is the two byte manuracturer code, e.g. 0x0118 for Radius Networks 37 | // Only populated for manufacturer beacon types 38 | @property (strong, nonatomic) NSNumber *manufacturer; 39 | // This is the bluetooth device name transmitted in the scan response 40 | @property (strong, nonatomic) NSString *name; 41 | // The Bluetooth Service UUID. Only populated for service beacon types 42 | @property (strong, nonatomic) NSNumber *serviceUuid; 43 | @property (strong, nonatomic) id beaconObject; 44 | @property (readonly) NSString *id1; 45 | @property (readonly) NSString *id2; 46 | @property (readonly) NSString *id3; 47 | @property (readonly) double coreLocationAccuracy; 48 | // This is the Corebluetooth perhipheral identifier as a string 49 | @property (strong, nonatomic) NSString *bluetoothIdentifier; 50 | + (NSArray *) wrapCLBeacons:(NSArray *) clBeacons; 51 | + (RNLBeacon *) wrapCLBeacon:(CLBeacon *)clBeacons; 52 | - (BOOL) isEqualToBeacon: (RNLBeacon *)other; 53 | + (NSArray *) matchBeacons: (NSArray * ) trackedBeacons bluetoothIdentifier:(NSString *)rangingBluetoothIdentifier id1: (NSString *) id1 id2: (NSString *) id2 id3: (NSString *) id3; 54 | + (NSData *) dataFromIdentifier: (NSString*) identifier; 55 | @end 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOS Beacon Tools 2 | 3 | This project contains iOS utilities to scan for and decode beacons using AltBeacon and 4 | Eddystone formats that are not natively supported by iOS. 5 | 6 | This code detects AltBeacon, Eddystone-UID, Eddystone-URL, Eddystone-TLM and Eddystone-EID (without resolution) 7 | 8 | ## Overview 9 | 10 | By copying this code into your project, you can use the singleton `RNLBeaconScanner` class to 11 | scan for bluetooth beacon and then periodically read out the beacons that are detected. Detected 12 | beacons can be access from an array on the `scanner.trackedBeacons()` method. 13 | 14 | This list of detected beacons is updated internally as beacons come in, and the code provides no callbacks. You'll 15 | need to some way to run code periodically to read the list of detected beacons. 16 | 17 | ## Setup 18 | 19 | 1. Copy all the files in the beacon-tools folder into your project 20 | 2. If using Swift, add at least the following to your PROJECTNAME-Bridging-Header.h file. 21 | 22 | ```` 23 | #include "RNLBeaconScanner.h" 24 | #include "RNLURLBeaconCompressor.h" 25 | #include "RNLBeacon+Distance.h" 26 | ```` 27 | 28 | ## Example 29 | 30 | ``` 31 | class Sample { 32 | var scanner: RNLBeaconScanner? 33 | func sample() { 34 | scanner = RNLBeaconScanner.shared() 35 | scanner?.startScanning() 36 | 37 | // Execute this code periodically (every second or so) to view the beacons detected 38 | 39 | if let detectedBeacons = scanner?.trackedBeacons() as? [RNLBeacon] { 40 | for beacon in detectedBeacons { 41 | if (beacon.beaconTypeCode.intValue == 0xbeac) { 42 | // this is an AltBeacon 43 | NSLog("Detected AltBeacon id1: %@ id2: %@ id3: %@", beacon.id1, beacon.id2, beacon.id3) 44 | } 45 | else if (beacon.beaconTypeCode.intValue == 0x00 && beacon.serviceUuid.intValue == 0xFEAA) { 46 | // this is eddystone uid 47 | NSLog("Detected EDDYSTONE-UID with namespace %@ instance %@", beacon.id1, beacon.id2) 48 | } 49 | else if (beacon.beaconTypeCode.intValue == 0x10 && beacon.serviceUuid.intValue == 0xFEAA) { 50 | // this is eddystone url 51 | NSLog("Detected EDDYSTONE-URL with %@", RNLURLBeaconCompressor.urlString(fromEddystoneURLIdentifier: beacon.id1)) 52 | } 53 | else { 54 | // some other beacon type 55 | } 56 | } 57 | } 58 | } 59 | } 60 | ``` 61 | 62 | ## TODOs: 63 | 64 | 1. Add better documentation 65 | 2. Convert the code into Swift 66 | 3. Make this a framework so you don't have to copy the source 67 | 68 | ## License 69 | 70 | This code is open source under the Apache 2 license. See LICENSE file in this repo. 71 | 72 | ## Bugs? Problems? 73 | 74 | Open an issue on this project, or a question on StackOverflow.com 75 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLAppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | #import "RNLAppDelegate.h" 26 | #import "RNLLocationTracker.h" 27 | 28 | @interface RNLAppDelegate () 29 | 30 | @end 31 | 32 | @implementation RNLAppDelegate 33 | 34 | 35 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 36 | 37 | return YES; 38 | } 39 | 40 | - (void)applicationWillResignActive:(UIApplication *)application { 41 | // 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. 42 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 43 | } 44 | 45 | - (void)applicationDidEnterBackground:(UIApplication *)application { 46 | // 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. 47 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 48 | } 49 | 50 | - (void)applicationWillEnterForeground:(UIApplication *)application { 51 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 52 | } 53 | 54 | - (void)applicationDidBecomeActive:(UIApplication *)application { 55 | // 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. 56 | } 57 | 58 | - (void)applicationWillTerminate:(UIApplication *)application { 59 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /ios-beacon-tools/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import "AppDelegate.h" 27 | 28 | @interface AppDelegate () 29 | 30 | @end 31 | 32 | @implementation AppDelegate 33 | 34 | 35 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 36 | // Override point for customization after application launch. 37 | return YES; 38 | } 39 | 40 | - (void)applicationWillResignActive:(UIApplication *)application { 41 | // 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. 42 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 43 | } 44 | 45 | - (void)applicationDidEnterBackground:(UIApplication *)application { 46 | // 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. 47 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 48 | } 49 | 50 | - (void)applicationWillEnterForeground:(UIApplication *)application { 51 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 52 | } 53 | 54 | - (void)applicationDidBecomeActive:(UIApplication *)application { 55 | // 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. 56 | } 57 | 58 | - (void)applicationWillTerminate:(UIApplication *)application { 59 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeaconTracker.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import "RNLBeaconTracker.h" 27 | #import "RNLBeacon.h" 28 | #import "RNLBeacon+Distance.h" 29 | 30 | @implementation RNLBeaconTracker { 31 | NSMutableDictionary *_trackedBeaconDictionary; 32 | } 33 | static double const SECONDS_TO_TRACK = 5.0; 34 | 35 | - (id)init { 36 | if (self = [super init]) { 37 | _trackedBeaconDictionary = [[NSMutableDictionary alloc] init]; 38 | } 39 | return self; 40 | } 41 | 42 | -(NSArray *) trackedBeacons { 43 | // Adding synchronized becasue this method was getting EXC_BAD_ACCESS, perhaps on release of the 44 | // _trackedBeaconDictionary in the method below 45 | @synchronized(self) { 46 | return [_trackedBeaconDictionary allValues]; 47 | } 48 | } 49 | 50 | -(void) updateWithRangedBeacons: (NSArray *) beacons { 51 | NSDate *now = [[NSDate alloc] init]; 52 | NSMutableDictionary *newTrackedBeaconDictionary = [[NSMutableDictionary alloc] init]; 53 | // add all newly ranged beacons to the newTrackedBeaconDictionary 54 | for (RNLBeacon *rangedBeacon in beacons) { 55 | NSString *rangedBeaconKey = [self identifierStringForBeacon:rangedBeacon]; 56 | RNLBeacon *trackedRangedBeacon = [_trackedBeaconDictionary objectForKey:rangedBeaconKey]; 57 | [rangedBeacon applyRssiMeasurements: trackedRangedBeacon]; 58 | [newTrackedBeaconDictionary setObject: rangedBeacon forKey: rangedBeaconKey]; 59 | } 60 | // now go through and find any beacons we did not get in this ranging cycle, but that should 61 | // still be tracked, and add them to the newTrackedBeaconDictionary 62 | for (NSString *beaconKey in [_trackedBeaconDictionary allKeys]) { 63 | if ([newTrackedBeaconDictionary objectForKey:beaconKey] == Nil) { 64 | RNLBeacon *oldTrackedBeacon = [_trackedBeaconDictionary objectForKey:beaconKey]; 65 | // make sure we should still be tracking this beacon 66 | if ([now timeIntervalSinceDate: oldTrackedBeacon.lastDetected] < SECONDS_TO_TRACK) { 67 | [newTrackedBeaconDictionary setObject: oldTrackedBeacon forKey: beaconKey]; 68 | } 69 | else { 70 | NSLog(@"Stopping tracking: %@ because we have not seen it in %f seconds", beaconKey, SECONDS_TO_TRACK); 71 | } 72 | } 73 | } 74 | @synchronized(self) { 75 | _trackedBeaconDictionary = newTrackedBeaconDictionary; 76 | } 77 | } 78 | 79 | -(NSString *) identifierStringForBeacon: (RNLBeacon *) beacon { 80 | return [NSString stringWithFormat:@"%@ %@ %@", beacon.id1, beacon.id2, beacon.id3]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /ios-beacon-tools/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ######################### 2 | # .gitignore file for Xcode4 / OS X Source projects 3 | # 4 | # Version 2.0 5 | # For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects 6 | # 7 | # 2013 updates: 8 | # - fixed the broken "save personal Schemes" 9 | # 10 | # NB: if you are storing "built" products, this WILL NOT WORK, 11 | # and you should use a different .gitignore (or none at all) 12 | # This file is for SOURCE projects, where there are many extra 13 | # files that we want to exclude 14 | # 15 | ######################### 16 | 17 | # Ignore generate file 18 | CampaignKit/CKGeneratedVersion.h 19 | 20 | 21 | ##### 22 | # OS X temporary files that should never be committed 23 | 24 | .DS_Store 25 | *.swp 26 | *.lock 27 | profile 28 | 29 | 30 | #### 31 | # Xcode temporary files that should never be committed 32 | # 33 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this... 34 | 35 | *~.nib 36 | 37 | 38 | #### 39 | # Xcode build files - 40 | # 41 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" 42 | 43 | DerivedData/ 44 | 45 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" 46 | 47 | build/ 48 | 49 | 50 | ##### 51 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) 52 | # 53 | # This is complicated: 54 | # 55 | # SOMETIMES you need to put this file in version control. 56 | # Apple designed it poorly - if you use "custom executables", they are 57 | # saved in this file. 58 | # 99% of projects do NOT use those, so they do NOT want to version control this file. 59 | # ..but if you're in the 1%, comment out the line "*.pbxuser" 60 | 61 | *.pbxuser 62 | *.mode1v3 63 | *.mode2v3 64 | *.perspectivev3 65 | # NB: also, whitelist the default ones, some projects need to use these 66 | !default.pbxuser 67 | !default.mode1v3 68 | !default.mode2v3 69 | !default.perspectivev3 70 | 71 | 72 | #### 73 | # Xcode 4 - semi-personal settings 74 | # 75 | # 76 | # OPTION 1: --------------------------------- 77 | # throw away ALL personal settings (including custom schemes! 78 | # - unless they are "shared") 79 | # 80 | # NB: this is exclusive with OPTION 2 below 81 | xcuserdata 82 | 83 | # OPTION 2: --------------------------------- 84 | # get rid of ALL personal settings, but KEEP SOME OF THEM 85 | # - NB: you must manually uncomment the bits you want to keep 86 | # 87 | # NB: this is exclusive with OPTION 1 above 88 | # 89 | #xcuserdata/**/* 90 | 91 | # (requires option 2 above): Personal Schemes 92 | # 93 | #!xcuserdata/**/xcschemes/* 94 | 95 | #### 96 | # XCode 4 workspaces - more detailed 97 | # 98 | # Workspaces are important! They are a core feature of Xcode - don't exclude them :) 99 | # 100 | # Workspace layout is quite spammy. For reference: 101 | # 102 | # /(root)/ 103 | # /(project-name).xcodeproj/ 104 | # project.pbxproj 105 | # /project.xcworkspace/ 106 | # contents.xcworkspacedata 107 | # /xcuserdata/ 108 | # /(your name)/xcuserdatad/ 109 | # UserInterfaceState.xcuserstate 110 | # /xcsshareddata/ 111 | # /xcschemes/ 112 | # (shared scheme name).xcscheme 113 | # /xcuserdata/ 114 | # /(your name)/xcuserdatad/ 115 | # (private scheme).xcscheme 116 | # xcschememanagement.plist 117 | # 118 | # 119 | /*.xcworkspace/xcshareddata/* 120 | 121 | # Ignore all xccheckout files -dgy 122 | *.xccheckout 123 | 124 | #### 125 | # Xcode 4 - Deprecated classes 126 | # 127 | # Allegedly, if you manually "deprecate" your classes, they get moved here. 128 | # 129 | # We're using source-control, so this is a "feature" that we do not want! 130 | 131 | *.moved-aside 132 | 133 | 134 | #### 135 | # UNKNOWN: recommended by others, but I can't discover what these files are 136 | # 137 | # ...none. Everything is now explained. 138 | -------------------------------------------------------------------------------- /ios-beacon-tools/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeacon+Distance.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author James Nebeker 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import "RNLBeacon+Distance.h" 27 | #import 28 | #import "RNLBeaconScanner.h" 29 | 30 | @implementation RNLBeacon (Distance) 31 | 32 | static double _secondsToAverage = 5.0; 33 | 34 | @dynamic lastDetected; 35 | @dynamic lastCalculated; 36 | @dynamic signalMeasurements; 37 | @dynamic runningAverageRssi; 38 | 39 | - (void)setLastDetected:(NSDate *)date { 40 | objc_setAssociatedObject(self, @selector(lastDetected), date, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 41 | } 42 | 43 | - (id)lastDetected { 44 | return objc_getAssociatedObject(self, @selector(lastDetected)); 45 | } 46 | 47 | - (void)setLastCalculated:(NSDate *)date { 48 | objc_setAssociatedObject(self, @selector(lastCalculated), date, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 49 | } 50 | 51 | - (id)lastCalculated { 52 | return objc_getAssociatedObject(self, @selector(lastCalculated)); 53 | } 54 | 55 | - (void)setSignalMeasurements:(NSMutableArray *)array { 56 | objc_setAssociatedObject(self, @selector(signalMeasurements), array, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 57 | } 58 | 59 | - (id)signalMeasurements { 60 | return objc_getAssociatedObject(self, @selector(signalMeasurements)); 61 | } 62 | 63 | - (void)setRunningAverageRssi:(NSNumber *)rssi { 64 | objc_setAssociatedObject(self, @selector(runningAverageRssi), rssi, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 65 | } 66 | 67 | - (id)runningAverageRssi { 68 | return objc_getAssociatedObject(self, @selector(runningAverageRssi)); 69 | } 70 | 71 | -(void) applyRssiMeasurements:(RNLBeacon *)beacon{ 72 | if (self.signalMeasurements == nil) { 73 | self.signalMeasurements = [[NSMutableArray alloc] init]; 74 | } 75 | if (beacon == nil) { 76 | self.lastCalculated = [NSDate dateWithTimeIntervalSince1970:0l]; // Not since 1970 = never 77 | self.runningAverageRssi = [NSNumber numberWithDouble:(0.0)]; 78 | } 79 | else { 80 | for (RNLSignalMeasurement *measurement in beacon.signalMeasurements) { 81 | [self.signalMeasurements addObject: measurement]; 82 | } 83 | } 84 | [self addCurrentRssiMeasurement]; 85 | [self recalculate]; 86 | } 87 | 88 | -(void) addCurrentRssiMeasurement { 89 | NSDate *now = [[NSDate alloc] init]; 90 | self.lastDetected = now; 91 | // ingnore invalid measurements that aren't negative 92 | if ([self.rssi doubleValue] >= 0.0) { 93 | return; 94 | } 95 | RNLSignalMeasurement *measurement = [[RNLSignalMeasurement alloc] init]; 96 | measurement.rssi = self.rssi; 97 | measurement.timestamp = now; 98 | [self.signalMeasurements addObject: measurement]; 99 | } 100 | 101 | -(void) recalculate { 102 | NSDate *now = [[NSDate alloc] init]; 103 | double sum = 0.0; 104 | 105 | // remove any expired measurements 106 | NSMutableArray *newArray = [[NSMutableArray alloc] init]; 107 | for (RNLSignalMeasurement *signalMeasurement in self.signalMeasurements) { 108 | if ([now timeIntervalSinceDate: signalMeasurement.timestamp] <= _secondsToAverage) { 109 | [newArray addObject: signalMeasurement]; 110 | sum += [signalMeasurement.rssi doubleValue]; 111 | } 112 | } 113 | if (newArray.count > 0) { 114 | self.runningAverageRssi = [NSNumber numberWithDouble:(sum / newArray.count)]; 115 | } 116 | else { 117 | self.runningAverageRssi = [NSNumber numberWithDouble:(0.0)]; 118 | } 119 | self.signalMeasurements = newArray; 120 | } 121 | 122 | +(double) secondsToAverage { 123 | return _secondsToAverage; 124 | } 125 | 126 | +(void) secondsToAverage: (double) seconds { 127 | _secondsToAverage = seconds; 128 | } 129 | 130 | -(CLLocationAccuracy) distance { 131 | double distance; // value to return 132 | 133 | NSNumber *measuredPower = self.measuredPower; 134 | if (measuredPower == Nil) { 135 | measuredPower = [[RNLBeaconScanner sharedBeaconScanner] calibratedRSSIFor:self]; 136 | } 137 | if (measuredPower) { 138 | NSNumber *rssi = self.runningAverageRssi; 139 | distance = [RNLBeacon distanceForRSSI:[rssi doubleValue] forPower:[measuredPower intValue]]; 140 | } 141 | else { 142 | distance = -1.0; 143 | } 144 | return distance; 145 | } 146 | 147 | +(double) distanceForRSSI:(double)rssi forPower:(int)txPower { 148 | // use coefficient values from spreadsheet for iPhone 4S 149 | double coefficient1 = 2.922026; // multiplier 150 | double coefficient2 = 6.672908; // power 151 | double coefficient3 = -1.767203; // intercept 152 | 153 | if (rssi == 0) { 154 | return -1.0; // if we cannot determine accuracy, return -1.0 155 | } 156 | 157 | double ratio = rssi*1.0/txPower; 158 | double distance; 159 | 160 | if (ratio < 1.0) { 161 | distance = pow(ratio,10); 162 | } 163 | else { 164 | distance = (coefficient1)*pow(ratio,coefficient2) + coefficient3; 165 | } 166 | 167 | if (distance < 0.1) { 168 | NSLog(@"Low distance"); 169 | } 170 | 171 | return distance; 172 | } 173 | 174 | @end 175 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLLocationTracker.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author James Nebeker 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import "RNLLocationTracker.h" 27 | #import "RNLBeaconTracker.h" 28 | #import "RNLBeacon+Distance.h" 29 | #import "RNLBeaconScanner.h" 30 | 31 | @implementation RNLLocationTracker { 32 | NSDate *_timestampClosestBeaconFirstSeen; // class variable for timestamp 33 | NSDate *_timestampClosestBeaconLastSeen; 34 | NSDate *_timestampClosestBeaconCandidateLastSeen; 35 | RNLBeacon *_closestBeacon; 36 | RNLBeacon *_closestBeaconCandidate; 37 | } 38 | static RNLLocationTracker *sharedLocationTracker = nil; // static instance variable 39 | 40 | + (RNLLocationTracker *)sharedLocationTracker { 41 | if (sharedLocationTracker == nil) { 42 | sharedLocationTracker = [[super alloc] init]; 43 | } 44 | return sharedLocationTracker; 45 | } 46 | static double const MIN_SECS_BEFORE_CLOSEST_BEACON_SWITCH = 5.0; 47 | 48 | - (id)init { 49 | if (self = [super init]) { 50 | //[self registerForPKNotifications]; 51 | self.beaconTracker = [[RNLBeaconTracker alloc] init]; 52 | self.useCoreLocationRanging = YES; 53 | [RNLBeaconScanner sharedBeaconScanner]; // simply referncing it will make it spin up 54 | } 55 | return self; 56 | } 57 | 58 | - (void)dealloc { 59 | //[self unregisterForPKNotifications]; 60 | } 61 | 62 | 63 | - (RNLBeacon *) closestBeacon { 64 | [self closestBeaconTimeout]; 65 | return _closestBeacon; 66 | } 67 | 68 | - (void) closestBeaconTimeout { 69 | // clear closest beacon if it hasn't been seen in the last five seconds 70 | if (_timestampClosestBeaconLastSeen != nil && [_timestampClosestBeaconLastSeen timeIntervalSinceNow] < -5.0) { 71 | NSLog(@"Closest beacon timeout has expired, setting to nil"); 72 | _timestampClosestBeaconLastSeen = nil; 73 | _closestBeacon = Nil; 74 | } 75 | if (_timestampClosestBeaconCandidateLastSeen != nil && [_timestampClosestBeaconCandidateLastSeen timeIntervalSinceNow] < -5.0) { 76 | NSLog(@"Closest beacon candidate timeout has expired, setting to nil"); 77 | _timestampClosestBeaconCandidateLastSeen = nil; 78 | _closestBeaconCandidate = Nil; 79 | } 80 | } 81 | 82 | - (void)didRangeBeaconsInRegion:(NSArray *) beacons { 83 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 84 | [self.beaconTracker updateWithRangedBeacons: beacons]; 85 | for (RNLBeacon *trackedBeacon in self.beaconTracker.trackedBeacons) { 86 | double distance = trackedBeacon.coreLocationAccuracy; 87 | if (trackedBeacon.beaconObject != Nil && [trackedBeacon.beaconObject isKindOfClass:[CLBeacon class]]) { 88 | distance = ((CLBeacon *)trackedBeacon).accuracy; 89 | } 90 | double candidateDistance = _closestBeaconCandidate.coreLocationAccuracy; 91 | if (!self.useCoreLocationRanging) { 92 | // Check to see if the distance value is valid (i.e., not -1.0) 93 | if (trackedBeacon.distance >= 0.0) { 94 | distance = trackedBeacon.distance; 95 | } 96 | if (_closestBeaconCandidate.distance >= 0.0) { 97 | candidateDistance = _closestBeaconCandidate.distance; 98 | } 99 | } 100 | BOOL customDistance = false; 101 | if (trackedBeacon.distance != -1.0) { 102 | customDistance = true; 103 | } 104 | if (_closestBeaconCandidate == nil || candidateDistance > distance) { 105 | if (distance > 0) { 106 | _closestBeaconCandidate = trackedBeacon; 107 | _timestampClosestBeaconCandidateLastSeen = [NSDate date]; 108 | } 109 | } 110 | if ([trackedBeacon isEqual:_closestBeacon]) { 111 | _timestampClosestBeaconLastSeen = [NSDate date]; // set timestamp to now 112 | } 113 | } 114 | 115 | // pick the closest beacon with a hallId that is not Nil 116 | if (_closestBeaconCandidate != nil) { 117 | if (_closestBeacon == nil) { 118 | _closestBeacon = _closestBeaconCandidate; 119 | _timestampClosestBeaconFirstSeen = [NSDate date]; // set timestamp to now 120 | _timestampClosestBeaconLastSeen = _timestampClosestBeaconFirstSeen; 121 | NSLog(@"Closest beacon (id: %@) so far with accuracy: %f at time: %@", _closestBeaconCandidate.id3, _closestBeaconCandidate.coreLocationAccuracy, _timestampClosestBeaconFirstSeen); 122 | } 123 | else { 124 | if ([_timestampClosestBeaconFirstSeen timeIntervalSinceNow] < -MIN_SECS_BEFORE_CLOSEST_BEACON_SWITCH) { 125 | _closestBeacon = _closestBeaconCandidate; 126 | _timestampClosestBeaconFirstSeen = [NSDate date]; // set timestamp to now 127 | _timestampClosestBeaconLastSeen = _timestampClosestBeaconFirstSeen; 128 | NSLog(@"Closest beacon (id: %@) so far found with accuracy: %f at time: %@", _closestBeaconCandidate.id3, _closestBeaconCandidate.coreLocationAccuracy, _timestampClosestBeaconFirstSeen); 129 | } 130 | } 131 | } 132 | // check if the current closest beacon has expired 133 | [self closestBeaconTimeout]; 134 | }); 135 | } 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeaconTableViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | #import 26 | #import "RNLBeaconTableViewController.h" 27 | #import "RNLBeaconScanner.h" 28 | #import "RNLBeacon.h" 29 | #import "RNLBeacon+Distance.h" 30 | 31 | @interface RNLBeaconTableViewController() 32 | @property (strong, nonatomic) RNLBeaconScanner *beaconScanner; 33 | @property Boolean visible; 34 | @property NSTimer *reloadTimer; 35 | @property int beaconListSize; 36 | @property NSDate *beaconListLastUpdated; 37 | @property NSArray *sortedBeaconArray; 38 | @end 39 | 40 | @implementation RNLBeaconTableViewController 41 | - (void)viewDidLoad 42 | { 43 | [super viewDidLoad]; 44 | self.beaconListSize = 0; 45 | self.beaconListLastUpdated = Nil; 46 | self.beaconScanner = [RNLBeaconScanner sharedBeaconScanner]; 47 | [RNLBeacon secondsToAverage:20]; 48 | } 49 | 50 | - (void)viewWillDisappear:(BOOL)animated { 51 | [super viewWillDisappear:animated]; 52 | self.visible = NO; 53 | } 54 | 55 | - (void)viewWillAppear:(BOOL)animated { 56 | [super viewWillAppear:animated]; 57 | self.visible = YES; 58 | if (self.reloadTimer == Nil) { 59 | self.reloadTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self 60 | selector: @selector(refresh:) userInfo: nil repeats: YES]; 61 | } 62 | } 63 | 64 | - (void) refresh: (NSTimer*) t { 65 | dispatch_async(dispatch_get_main_queue(), ^{ [self.tableView reloadData]; }); 66 | } 67 | 68 | 69 | #pragma mark - Table view data source 70 | 71 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 72 | { 73 | return 1; 74 | } 75 | 76 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 77 | { 78 | return [self sortedBeacons].count; 79 | } 80 | 81 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 82 | { 83 | return nil; 84 | } 85 | 86 | - (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath { 87 | return 0; 88 | } 89 | 90 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 91 | return 60.0; 92 | } 93 | 94 | - (NSArray *)sortedBeacons { 95 | Boolean resortNeeded = NO; 96 | NSArray *trackedBeacons = self.beaconScanner.trackedBeacons; 97 | 98 | if (trackedBeacons.count != self.beaconListSize) { 99 | resortNeeded = YES; 100 | } 101 | else { 102 | NSDate *latestDate = Nil; 103 | for (RNLBeacon *trackedBeacon in trackedBeacons) { 104 | if (latestDate == Nil || [latestDate compare:trackedBeacon.lastDetected] == NSOrderedAscending) { 105 | latestDate = trackedBeacon.lastDetected; 106 | } 107 | } 108 | if (self.beaconListLastUpdated == Nil || [self.beaconListLastUpdated compare:latestDate] == NSOrderedAscending) { 109 | resortNeeded = YES; 110 | self.beaconListLastUpdated = latestDate; 111 | } 112 | } 113 | if (resortNeeded) { 114 | self.beaconListSize = trackedBeacons.count; 115 | NSArray *sortedArray; 116 | sortedArray = [trackedBeacons sortedArrayUsingComparator:^NSComparisonResult(RNLBeacon *a, RNLBeacon *b) { 117 | NSString *p1 = [NSString stringWithFormat:@"%@_%@_%@", a.id1, a.id2, a.id3]; 118 | NSString *p2 = [NSString stringWithFormat:@"%@_%@_%@", b.id1, b.id2, b.id3]; 119 | NSLog(@"Comparing %@ to %@",p1, p2); 120 | return [p1 compare: p2]; 121 | }]; 122 | self.sortedBeaconArray = sortedArray; 123 | } 124 | return self.sortedBeaconArray; 125 | } 126 | 127 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 128 | { 129 | RNLBeacon *beacon = Nil; 130 | 131 | @try { 132 | beacon = [[self sortedBeacons] objectAtIndex:indexPath.row]; 133 | } 134 | @catch (NSException *exception) { 135 | NSLog(@"%@", exception.reason); 136 | } 137 | 138 | UITableViewCell *cell; 139 | cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; 140 | if (cell == nil) { 141 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"]; 142 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 143 | cell.textLabel.font = [UIFont fontWithName: @"Arial" size: 13.0 ]; 144 | } 145 | cell.backgroundColor = [UIColor whiteColor]; 146 | cell.accessoryView = nil; // can attach image here 147 | if (beacon != Nil) { 148 | if ([beacon.beaconTypeCode intValue] == 0x00) { 149 | cell.textLabel.text = [NSString stringWithFormat:@"%@ (Eddystone-UID)", beacon.id1]; 150 | cell.detailTextLabel.text = [NSString stringWithFormat:@"RSSI: %@ Distance: %3.1f m\n%@", beacon.rssi, beacon.distance, beacon.id2]; 151 | cell.detailTextLabel.numberOfLines = -1; 152 | } 153 | else { 154 | cell.textLabel.text = beacon.id1; 155 | cell.detailTextLabel.text = [NSString stringWithFormat:@"Major: %@ Minor: %@", beacon.id2, beacon.id3]; 156 | cell.detailTextLabel.numberOfLines = 1; 157 | } 158 | } 159 | else { 160 | cell.textLabel.text = @""; 161 | cell.detailTextLabel.text = @""; 162 | cell.detailTextLabel.numberOfLines = 1; 163 | } 164 | return cell; 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeaconScanner.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author Scott Yoder 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import "RNLBeaconScanner.h" 27 | #import "RNLBeaconParser.h" 28 | #import "RNLBeaconTracker.h" 29 | #import "RNLBeacon.h" 30 | #import "RNLBeacon+Distance.h" 31 | 32 | #define SECONDS_BEFORE_DROPOFF 5 33 | 34 | @interface RNLBeaconScanner () 35 | @property (strong, nonatomic) CBCentralManager *cbManager; 36 | @property (nonatomic) BOOL scanning; 37 | @property (strong, nonatomic) NSArray *beaconParsers; 38 | @property (strong, nonatomic) RNLBeaconTracker *beaconTracker; 39 | @end 40 | 41 | @implementation RNLBeaconScanner 42 | 43 | + (instancetype)sharedBeaconScanner { 44 | static RNLBeaconScanner *sharedBeaconScanner = nil; 45 | if (sharedBeaconScanner == nil) { 46 | sharedBeaconScanner = [[RNLBeaconScanner alloc] init]; 47 | } 48 | return sharedBeaconScanner; 49 | } 50 | 51 | - (instancetype) init { 52 | self = [super init]; 53 | self.beaconParsers = [[NSMutableArray alloc] init]; 54 | RNLBeaconParser *altBeaconParser = [[RNLBeaconParser alloc] init]; 55 | [altBeaconParser setBeaconLayout:@"m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25" error: Nil ]; 56 | RNLBeaconParser *uidBeaconParser = [[RNLBeaconParser alloc] init]; 57 | [uidBeaconParser setBeaconLayout:@"s:0-1=feaa,m:2-2=00,p:3-3:-41,i:4-13,i:14-19" error: Nil]; 58 | RNLBeaconParser *urlBeaconParser = [[RNLBeaconParser alloc] init]; 59 | [urlBeaconParser setBeaconLayout:@"s:0-1=feaa,m:2-2=10,p:3-3:-41,i:4-20v" error: Nil]; 60 | RNLBeaconParser *eidBeaconParser = [[RNLBeaconParser alloc] init]; 61 | [eidBeaconParser setBeaconLayout:@"s:0-1=feaa,m:2-2=30,p:3-3:-41,i:4-11" error: Nil]; 62 | RNLBeaconParser *tlmBeaconParser = [[RNLBeaconParser alloc] init]; 63 | [tlmBeaconParser setBeaconLayout:@"x,s:0-1=feaa,m:2-2=20,d:3-3,d:4-5,d:6-7,d:8-11,d:12-15" error: Nil]; 64 | 65 | self.beaconParsers = @[ altBeaconParser, uidBeaconParser, urlBeaconParser, eidBeaconParser, tlmBeaconParser]; 66 | self.debugEnabled = NO; 67 | 68 | self.beaconTracker = [[RNLBeaconTracker alloc] init]; 69 | 70 | [self startScanning]; 71 | return self; 72 | } 73 | - (void) dealloc { 74 | [self stopScanning]; 75 | } 76 | 77 | - (void)startScanning { 78 | if (!self.cbManager) { 79 | self.cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()]; 80 | self.scanning = YES; 81 | } 82 | } 83 | 84 | - (void)stopScanning { 85 | [self.cbManager stopScan]; 86 | } 87 | 88 | - (NSArray *)trackedBeacons { 89 | return self.beaconTracker.trackedBeacons; 90 | } 91 | 92 | - (NSNumber *)calibratedRSSIFor:(RNLBeacon *)beacon { 93 | NSString *key = [NSString stringWithFormat:@"%@ %@ %@", beacon.id1, beacon.id2, beacon.id3]; 94 | for (RNLBeacon *trackedBeacon in self.trackedBeacons) { 95 | NSString *trackedBeaconKey = [NSString stringWithFormat:@"%@ %@ %@", trackedBeacon.id1, trackedBeacon.id2, trackedBeacon.id3]; 96 | if ([trackedBeaconKey isEqualToString:key]) { 97 | return trackedBeacon.rssi; 98 | } 99 | } 100 | return Nil; 101 | } 102 | 103 | - (void)centralManagerDidUpdateState:(CBCentralManager *)central { 104 | if (central.state == CBCentralManagerStatePoweredOn && self.scanning) { 105 | [self.cbManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @(YES)}]; 106 | } 107 | } 108 | 109 | - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { 110 | NSDictionary *serviceData = advertisementData[@"kCBAdvDataServiceData"]; 111 | 112 | RNLBeacon *beacon = Nil; 113 | NSData *adData = advertisementData[@"kCBAdvDataManufacturerData"]; 114 | 115 | for (RNLBeaconParser *beaconParser in self.beaconParsers) { 116 | if (adData) { 117 | if (self.debugEnabled) { 118 | NSLog(@"didDiscoverPeripheral with manufacturer data"); 119 | } 120 | beacon = [beaconParser fromScanData: adData withRssi: RSSI forDevice: peripheral serviceUuid: Nil]; 121 | } 122 | else if (serviceData != Nil) { 123 | if (self.debugEnabled) { 124 | NSLog(@"didDiscoverPeripheral with service data"); 125 | } 126 | for (NSObject *key in serviceData.allKeys) { 127 | NSString *uuidString = [(CBUUID *) key UUIDString]; 128 | NSScanner* scanner = [NSScanner scannerWithString: uuidString]; 129 | unsigned long long uuidLongLong; 130 | 131 | [scanner scanHexLongLong: &uuidLongLong]; 132 | NSNumber *uuidNumber = [NSNumber numberWithLongLong:uuidLongLong]; 133 | if (self.debugEnabled) { 134 | NSLog(@"Service data has length %lu", (unsigned long)((NSData *)[serviceData objectForKey:key]).length); 135 | } 136 | 137 | NSData *adServiceData = [serviceData objectForKey:key]; 138 | if (adServiceData) { 139 | beacon = [beaconParser fromScanData: adServiceData withRssi: RSSI forDevice: peripheral serviceUuid: uuidNumber]; 140 | } 141 | } 142 | } 143 | if (beacon != Nil) { 144 | break; 145 | } 146 | } 147 | 148 | if (beacon != Nil) { 149 | NSString *key = [NSString stringWithFormat:@"%@ %@ %@", beacon.id1, beacon.id2, beacon.id3]; 150 | [self.beaconTracker updateWithRangedBeacons: @[beacon]]; 151 | NSLog(@"Detected beacon: %@", key); 152 | } 153 | 154 | 155 | } 156 | 157 | @end 158 | 159 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeacon.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import "RNLBeacon.h" 27 | #import 28 | 29 | @implementation RNLBeacon 30 | -(NSString *) id1 { 31 | NSString *id = Nil; 32 | if (self.identifiers.count > 0) { 33 | id = [self.identifiers objectAtIndex:0]; 34 | } 35 | return id; 36 | } 37 | -(NSString *) id2 { 38 | NSString *id = Nil; 39 | if (self.identifiers.count > 1) { 40 | id = [self.identifiers objectAtIndex:1]; 41 | } 42 | return id; 43 | } 44 | -(NSString *) id3 { 45 | NSString *id = Nil; 46 | if (self.identifiers.count > 2) { 47 | id = [self.identifiers objectAtIndex:2]; 48 | } 49 | return id; 50 | } 51 | 52 | -(double) coreLocationAccuracy { 53 | if (self.beaconObject != Nil && [self.beaconObject isKindOfClass:[CLBeacon class]]) { 54 | return ((CLBeacon *)self.beaconObject).accuracy; 55 | } 56 | return -1.0; 57 | } 58 | + (NSArray *) wrapCLBeacons:(NSArray *) clBeacons { 59 | NSMutableArray *beacons = [[NSMutableArray alloc] init]; 60 | for (CLBeacon * clBeacon in clBeacons) { 61 | [beacons addObject: [RNLBeacon wrapCLBeacon: clBeacon]]; 62 | } 63 | return beacons; 64 | } 65 | 66 | + (RNLBeacon *) wrapCLBeacon:(CLBeacon *)clBeacon { 67 | RNLBeacon *beacon = [[RNLBeacon alloc] init]; 68 | beacon.beaconObject = clBeacon; 69 | beacon.rssi = [NSNumber numberWithInteger:clBeacon.rssi]; 70 | beacon.identifiers = @[[clBeacon.proximityUUID UUIDString],[clBeacon.major stringValue],[clBeacon.minor stringValue]]; 71 | beacon.measuredPower = nil; // cannot read from a CLBeacon 72 | beacon.manufacturer = @0x004c; 73 | beacon.beaconTypeCode = @0x0215; 74 | beacon.dataFields = @[]; 75 | return beacon; 76 | } 77 | 78 | - (BOOL) isEqualToBeacon: (RNLBeacon *)other { 79 | BOOL equal = true; 80 | // must have same number of identifiers 81 | if (self.identifiers.count != other.identifiers.count) { 82 | equal = false; 83 | } 84 | else { 85 | // All identifiers must match 86 | for (int i = 0; i < self.identifiers.count; i++) { 87 | if (![[self.identifiers objectAtIndex:i] isEqualToString:[other.identifiers objectAtIndex:i]]) { 88 | equal = false; 89 | break; 90 | } 91 | } 92 | } 93 | if (self.bluetoothIdentifier != nil && other.bluetoothIdentifier != nil) { 94 | if (![self.bluetoothIdentifier isEqualToString:other.bluetoothIdentifier]) { 95 | equal = false; 96 | } 97 | } 98 | else { 99 | if (self.bluetoothIdentifier != nil || other.bluetoothIdentifier != nil) { 100 | // if one is nil but not the other, then they are not the same 101 | equal = false; 102 | } 103 | } 104 | return equal; 105 | 106 | } 107 | 108 | + (NSData *) dataFromIdentifier: (NSString*) identifier { 109 | NSMutableData* data = [NSMutableData data]; 110 | if ([identifier containsString:@"-"]) { 111 | // convert uuid to regular hex string 112 | identifier = [identifier stringByReplacingOccurrencesOfString:@"-" withString:@""]; 113 | identifier = [NSString stringWithFormat:@"0x%@", identifier]; 114 | } 115 | 116 | if ([identifier hasPrefix:@"0x"]) { 117 | int bytes = (int) (identifier.length-2)/2; 118 | // convert hex number 119 | for (int i = 0; i < bytes; i++) { 120 | NSString *hexByte = [identifier substringWithRange: NSMakeRange(i*2+2,2)]; 121 | NSScanner* scanner = [NSScanner scannerWithString:hexByte]; 122 | unsigned int intValue; 123 | [scanner scanHexInt:&intValue]; 124 | [data appendBytes:&intValue length:1]; 125 | } 126 | } 127 | else { 128 | // convert int 0-65535 129 | int intIdentifier = [identifier intValue]; 130 | int highByte = intIdentifier >> 8; 131 | int lowByte = intIdentifier & 0xff; 132 | [data appendBytes:&highByte length: 1]; 133 | [data appendBytes:&lowByte length: 1]; 134 | } 135 | return data; 136 | } 137 | 138 | + (NSArray *) matchBeacons: (NSArray * ) beacons bluetoothIdentifier:(NSString *)bluetoothIdentifier id1: (NSString *) id1 id2: (NSString *) id2 id3: (NSString *) id3 { 139 | NSMutableArray * matchingBeacons = [[NSMutableArray alloc] init]; 140 | for (RNLBeacon *beacon in beacons) { 141 | BOOL match = YES; 142 | if (bluetoothIdentifier != nil) { 143 | if (beacon.bluetoothIdentifier != nil) { 144 | if (![bluetoothIdentifier isEqualToString:beacon.bluetoothIdentifier]) { 145 | match = NO; 146 | } 147 | } 148 | } 149 | if (id1 != nil) { 150 | if (beacon.id1 != nil) { 151 | if (![id1 isEqualToString:beacon.id1]) { 152 | match = NO; 153 | } 154 | } 155 | } 156 | if (id2 != nil) { 157 | if (beacon.id2 != nil) { 158 | if (![id2 isEqualToString:beacon.id2]) { 159 | match = NO; 160 | } 161 | } 162 | } 163 | if (id3 != nil) { 164 | if (beacon.id3 != nil) { 165 | if (![id3 isEqualToString:beacon.id3]) { 166 | match = NO; 167 | } 168 | } 169 | } 170 | if (match) { 171 | [matchingBeacons addObject: beacon]; 172 | } 173 | } 174 | return matchingBeacons; 175 | } 176 | 177 | - (id)copyWithZone:(NSZone *)zone { 178 | RNLBeacon *copy = [[RNLBeacon alloc] init]; 179 | 180 | if (self.beaconObject != nil) { 181 | copy.beaconObject = [self.beaconObject copyWithZone: nil]; 182 | } 183 | copy.identifiers = [self.identifiers copyWithZone:nil]; 184 | copy.dataFields = [self.identifiers copyWithZone:nil]; 185 | if (self.bluetoothIdentifier != nil) { 186 | copy.bluetoothIdentifier = [self.bluetoothIdentifier copyWithZone:nil]; 187 | } 188 | copy.manufacturer = self.manufacturer; 189 | copy.rssi = self.rssi; 190 | copy.beaconTypeCode = self.beaconTypeCode; 191 | copy.serviceUuid = self.serviceUuid; 192 | return copy; 193 | } 194 | 195 | @end 196 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2014 Radius Networks 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /ios-beacon-tools/RNLBeaconParser.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Radius Networks, Inc. 3 | * http://www.radiusnetworks.com 4 | * 5 | * @author David G. Young 6 | * 7 | * Licensed to the Apache Software Foundation (ASF) under one 8 | * or more contributor license agreements. See the NOTICE file 9 | * distributed with this work for additional information 10 | * regarding copyright ownership. The ASF licenses this file 11 | * to you under the Apache License, Version 2.0 (the 12 | * "License"); you may not use this file except in compliance 13 | * with the License. You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, 18 | * software distributed under the License is distributed on an 19 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | * KIND, either express or implied. See the License for the 21 | * specific language governing permissions and limitations 22 | * under the License. 23 | */ 24 | 25 | 26 | #import "RNLBeaconParser.h" 27 | 28 | @interface RNLBeaconParser() 29 | @property (strong, nonatomic) NSNumber *matchingBeaconTypeCode; 30 | @property (strong, nonatomic) NSMutableArray *identifierStartOffsets; 31 | @property (strong, nonatomic) NSMutableArray *identifierEndOffsets; 32 | @property (strong, nonatomic) NSMutableArray *identifierLittleEndianFlags; 33 | @property (strong, nonatomic) NSMutableArray *identifierVariableLengthFlags; 34 | @property (strong, nonatomic) NSMutableArray *dataStartOffsets; 35 | @property (strong, nonatomic) NSMutableArray *dataEndOffsets; 36 | @property (strong, nonatomic) NSMutableArray *dataLittleEndianFlags; 37 | @property (strong, nonatomic) NSNumber *matchingBeaconTypeCodeStartOffset; 38 | @property (strong, nonatomic) NSNumber *matchingBeaconTypeCodeEndOffset; 39 | @property (strong, nonatomic) NSNumber *powerStartOffset; 40 | @property (strong, nonatomic) NSNumber *powerEndOffset; 41 | @property (strong, nonatomic) NSNumber *powerCorrection; 42 | @property (strong, nonatomic) NSNumber *serviceUuidStartOffset; 43 | @property (strong, nonatomic) NSNumber *serviceUuidEndOffset; 44 | @property (strong, nonatomic) NSNumber *serviceUuid; 45 | @property (nonatomic) BOOL extraFrame; 46 | @end 47 | 48 | @implementation RNLBeaconParser { 49 | } 50 | 51 | static const NSString *I_PATTERN = @"i\\:(\\d+)\\-(\\d+)([blv]*)?"; 52 | static const NSString *M_PATTERN = @"m\\:(\\d+)-(\\d+)\\=([0-9A-F-a-f]+)"; 53 | static const NSString *D_PATTERN = @"d\\:(\\d+)\\-(\\d+)([bl]*)?"; 54 | static const NSString *P_PATTERN = @"p\\:(\\d+)\\-(\\d+)\\:?([\\-\\d]+)?"; 55 | static const NSString *S_PATTERN = @"s\\:(\\d+)-(\\d+)\\=([0-9A-Fa-f]+)"; 56 | static const NSString *X_PATTERN = @"x"; 57 | 58 | /** 59 | * Makes a new BeaconParser. Should normally be immediately followed by a call to #setLayout 60 | */ 61 | - (id)init { 62 | if (self = [super init]) { 63 | self.identifierStartOffsets = [[NSMutableArray alloc] init]; 64 | self.identifierEndOffsets = [[NSMutableArray alloc] init]; 65 | self.dataStartOffsets = [[NSMutableArray alloc] init]; 66 | self.dataEndOffsets = [[NSMutableArray alloc] init]; 67 | self.dataLittleEndianFlags = [[NSMutableArray alloc] init]; 68 | self.identifierLittleEndianFlags = [[NSMutableArray alloc] init]; 69 | self.identifierVariableLengthFlags = [[NSMutableArray alloc] init]; 70 | } 71 | return self; 72 | } 73 | 74 | /** 75 | *

Defines a beacon field parsing algorithm based on a string designating the zero-indexed 76 | * offsets to bytes within a BLE advertisement.

77 | * 78 | *

If you want to see examples of how other folks have set up BeaconParsers for different 79 | * kinds of beacons, try doing a Google search for "getBeaconParsers" (include the quotes in 80 | * the search.)

81 | * 82 | *

Four prefixes are allowed in the string:

83 | * 84 | *
 85 |  *   m - matching byte sequence for this beacon type to parse (exactly one required)
 86 |  *   i - identifier (at least one required, multiple allowed)
 87 |  *   p - power calibration field (exactly one required)
 88 |  *   d - data field (optional, multiple allowed)
 89 |  *   x - extra layout.  Signifies that the layout is secondary to a primary layout with the same
 90 |  *       matching byte sequence (or ServiceUuid).  Extra layouts do not require power or
 91 |  *       identifier fields and create Beacon objects without identifiers.
 92 |  * 
93 | * 94 | *

Each prefix is followed by a colon, then an inclusive decimal byte offset for the field from 95 | * the beginning of the advertisement. In the case of the m prefix, an = sign follows the byte 96 | * offset, followed by a big endian hex representation of the bytes that must be matched for 97 | * this beacon type. When multiple i or d entries exist in the string, they will be added in 98 | * order of definition to the identifier or data array for the beacon when parsing the beacon 99 | * advertisement. Terms are separated by commas.

100 | * 101 | *

All offsets from the start of the advertisement are relative to the first byte of the 102 | * two byte manufacturer code. The manufacturer code is therefore always at position 0-1

103 | * 104 | *

All data field and identifier expressions may be optionally suffixed with the letter l, which 105 | * indicates the field should be parsed as little endian. If not present, the field will be presumed 106 | * to be big endian. 107 | * 108 | *

If the expression cannot be parsed, a BeaconLayoutException is thrown.

109 | * 110 | *

Example of a parser string for AltBeacon:

111 | * 112 | * 113 | * "m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25" 114 | * 115 | * 116 | *

This signifies that the beacon type will be decoded when an advertisement is found with 117 | * 0xbeac in bytes 2-3, and a three-part identifier will be pulled out of bytes 4-19, bytes 118 | * 20-21 and bytes 22-23, respectively. A signed power calibration value will be pulled out of 119 | * byte 24, and a data field will be pulled out of byte 25.

120 | * 121 | * Note: bytes 0-1 of the BLE manufacturer advertisements are the two byte manufacturer code. 122 | * Generally you should not match on these two bytes when using a BeaconParser, because it will 123 | * limit your parser to matching only a transmitter made by a specific manufacturer. Software 124 | * and operating systems that scan for beacons typically ignore these two bytes, allowing beacon 125 | * manufacturers to use their own company code assigned by Bluetooth SIG. The default parser 126 | * implementation will already pull out this company code and store it in the 127 | * beacon.manufacturer field. Matcher expressions should therefore start with "m2-3:" followed 128 | * by the multi-byte hex value that signifies the beacon type. 129 | * 130 | */ 131 | -(BOOL) setBeaconLayout: (NSString *)beaconLayout error:(NSError **)errorPtr; { 132 | 133 | NSArray *terms = [beaconLayout componentsSeparatedByString:@","]; 134 | int errorCode = 0; 135 | NSString *errorString; 136 | 137 | for (NSString *term in terms) { 138 | Boolean found = NO; 139 | 140 | NSRange textRange = NSMakeRange(0, term.length); 141 | NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern: (NSString *)I_PATTERN options:0 error:nil]; 142 | NSArray* matches = [regex matchesInString:term options:0 range: textRange]; 143 | for (NSTextCheckingResult* match in matches) { 144 | found = YES; 145 | NSString *group1 = [term substringWithRange:[match rangeAtIndex:1]]; 146 | NSString *group2 = [term substringWithRange:[match rangeAtIndex:2]]; 147 | NSString *group3 = [term substringWithRange:[match rangeAtIndex:3]]; 148 | NSNumber *startOffset = [NSNumber numberWithLong:[group1 integerValue]]; 149 | NSNumber *endOffset = [NSNumber numberWithLong:[group2 integerValue]]; 150 | NSNumber *littleEndian = [NSNumber numberWithBool: [group3 isEqualToString:@"l"]]; 151 | NSNumber *variableLength = [NSNumber numberWithBool: [group3 isEqualToString:@"v"]]; 152 | [self.identifierLittleEndianFlags addObject: littleEndian]; 153 | [self.identifierStartOffsets addObject: startOffset]; 154 | [self.identifierEndOffsets addObject: endOffset]; 155 | [self.identifierVariableLengthFlags addObject: variableLength]; 156 | } 157 | 158 | regex = [NSRegularExpression regularExpressionWithPattern: (NSString *)X_PATTERN options:0 error:nil]; 159 | matches = [regex matchesInString:term options:0 range: textRange]; 160 | if (matches.count > 0) { 161 | found = YES; 162 | self.extraFrame = YES; 163 | } 164 | 165 | regex = [NSRegularExpression regularExpressionWithPattern: (NSString *)S_PATTERN options:0 error:nil]; 166 | matches = [regex matchesInString:term options:0 range: textRange]; 167 | for (NSTextCheckingResult* match in matches) { 168 | found = YES; 169 | NSString *group1 = [term substringWithRange:[match rangeAtIndex:1]]; 170 | NSString *group2 = [term substringWithRange:[match rangeAtIndex:2]]; 171 | NSString *group3 = [term substringWithRange:[match rangeAtIndex:3]]; 172 | self.serviceUuidStartOffset = [NSNumber numberWithLong:[group1 integerValue]]; 173 | self.serviceUuidEndOffset = [NSNumber numberWithLong:[group2 integerValue]]; 174 | self.serviceUuid = [NSNumber numberWithLong:[group3 integerValue]]; 175 | } 176 | 177 | regex = [NSRegularExpression regularExpressionWithPattern: (NSString *)D_PATTERN options:0 error:nil]; 178 | matches = [regex matchesInString:term options:0 range: textRange]; 179 | for (NSTextCheckingResult* match in matches) { 180 | found = YES; 181 | NSString *group1 = [term substringWithRange:[match rangeAtIndex:1]]; 182 | NSString *group2 = [term substringWithRange:[match rangeAtIndex:2]]; 183 | NSString *group3 = [term substringWithRange:[match rangeAtIndex:3]]; 184 | NSNumber *startOffset = [NSNumber numberWithLong:[group1 integerValue]]; 185 | NSNumber *endOffset = [NSNumber numberWithLong:[group2 integerValue]]; 186 | NSNumber *littleEndian = [NSNumber numberWithBool: [group3 isEqualToString:@"l"]]; 187 | [self.dataLittleEndianFlags addObject: littleEndian]; 188 | [self.dataStartOffsets addObject: startOffset]; 189 | [self.dataEndOffsets addObject: endOffset]; 190 | } 191 | 192 | regex = [NSRegularExpression regularExpressionWithPattern: (NSString *)P_PATTERN options:0 error:nil]; 193 | matches = [regex matchesInString:term options:0 range: textRange]; 194 | for (NSTextCheckingResult* match in matches) { 195 | found = YES; 196 | NSString *group1 = [term substringWithRange:[match rangeAtIndex:1]]; 197 | NSString *group2 = [term substringWithRange:[match rangeAtIndex:2]]; 198 | NSRange correctionRange =[match rangeAtIndex:3]; 199 | if (correctionRange.location != NSNotFound) { 200 | NSString *group3 = [term substringWithRange:correctionRange]; 201 | self.powerCorrection = [NSNumber numberWithLong:[group3 integerValue]]; 202 | } 203 | else { 204 | self.powerCorrection = @0; 205 | } 206 | self.powerStartOffset = [NSNumber numberWithLong:[group1 integerValue]]; 207 | self.powerEndOffset = [NSNumber numberWithLong:[group2 integerValue]]; 208 | } 209 | 210 | regex = [NSRegularExpression regularExpressionWithPattern: (NSString *)M_PATTERN options:0 error:nil]; 211 | matches = [regex matchesInString:term options:0 range: textRange]; 212 | for (NSTextCheckingResult* match in matches) { 213 | found = YES; 214 | NSString *group1 = [term substringWithRange:[match rangeAtIndex:1]]; 215 | NSString *group2 = [term substringWithRange:[match rangeAtIndex:2]]; 216 | NSString *group3 = [term substringWithRange:[match rangeAtIndex:3]]; 217 | self.matchingBeaconTypeCodeStartOffset = [NSNumber numberWithLong:[group1 integerValue]]; 218 | self.matchingBeaconTypeCodeEndOffset = [NSNumber numberWithLong:[group2 integerValue]]; 219 | unsigned code = 0; 220 | NSScanner *scanner = [NSScanner scannerWithString:group3]; 221 | [scanner scanHexInt:&code]; 222 | self.matchingBeaconTypeCode = [NSNumber numberWithUnsignedInt:code]; 223 | } 224 | if (!found) { 225 | NSLog(@"cannot parse term %@", term); 226 | errorCode = -1; 227 | errorString = NSLocalizedString(@"Cannot parse beacon layout term %@", term); 228 | } 229 | } 230 | if ((self.powerStartOffset == Nil || self.powerEndOffset == Nil) && self.extraFrame == NO) { 231 | errorCode = -2; 232 | errorString = NSLocalizedString(@"You must supply a power byte offset with a prefix of 'p'", @""); 233 | } 234 | if (self.serviceUuid == Nil && (self.matchingBeaconTypeCodeStartOffset == Nil || self.matchingBeaconTypeCodeEndOffset == Nil)) { 235 | errorCode = -3; 236 | errorString = NSLocalizedString(@"You must supply a matching beacon type expression with a prefix of 'm', or a service uuid expression with a prefix of 's'", @""); 237 | } 238 | if ((self.identifierStartOffsets.count == 0 || self.identifierEndOffsets.count == 0) && self.extraFrame == NO) { 239 | errorCode = -4; 240 | errorString = NSLocalizedString(@"You must supply at least one identifier offset withh a prefix of 'i'", @""); 241 | } 242 | if (errorPtr && errorCode < 0) { 243 | NSString *domain = @"org.altbeacon.beaconparser.ErrorDomain"; 244 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : errorString }; 245 | 246 | *errorPtr = [NSError errorWithDomain:domain 247 | code:errorCode 248 | userInfo:userInfo]; 249 | return NO; 250 | } 251 | 252 | return YES; 253 | } 254 | 255 | 256 | /** 257 | * Construct a Beacon from a Bluetooth LE packet collected by CoreBluetooth, 258 | * including the raw bluetooth device info 259 | * 260 | * param scanData The actual packet bytes 261 | * param rssi The measured signal strength of the packet 262 | * param device The bluetooth device that was detected 263 | * returns An instance of a Beacon 264 | */ 265 | -(RNLBeacon *) fromScanData: (NSData *)scanData withRssi: (NSNumber *) rssi forDevice: (CBPeripheral *)device serviceUuid:(NSNumber *) serviceUuid { 266 | return [self fromScanData: scanData withRssi: rssi forDevice: device serviceUuid: serviceUuid withBeacon:[[RNLBeacon alloc] init]]; 267 | } 268 | 269 | -(RNLBeacon *) fromScanData: (NSData *)scanData withRssi: (NSNumber *) rssi forDevice: (CBPeripheral *)device serviceUuid: (NSNumber *) serviceUuid withBeacon: (RNLBeacon *)beacon { 270 | 271 | BOOL patternFound = NO; 272 | const unsigned char *bytes = [scanData bytes]; 273 | 274 | int beaconTypeCodeLength = [self.matchingBeaconTypeCodeEndOffset intValue]-[self.matchingBeaconTypeCodeStartOffset intValue]+1; 275 | long matchingBeaconTypeCodeLong = [self.matchingBeaconTypeCode longValue]; 276 | unsigned char beaconTypeCodeBytes[4] = { 0, 0, 0, 0 }; 277 | if (beaconTypeCodeLength > 4) { 278 | NSLog(@"beacon type code is specified to be too long"); 279 | return Nil; 280 | } 281 | [self value: matchingBeaconTypeCodeLong toByteArray: beaconTypeCodeBytes withLength: beaconTypeCodeLength]; 282 | 283 | int startByte = 0; 284 | if (serviceUuid != Nil) { 285 | startByte = -2; // serviceUuids are stripped out of the data on iOS, so we have to adjust offsets 286 | } 287 | if ([self biggestOffset] +1 > scanData.length - startByte) { 288 | BOOL variableLength = NO; 289 | for (NSNumber *variableLengthFlag in self.identifierVariableLengthFlags) { 290 | if ([variableLengthFlag isEqual: @1]) { 291 | variableLength = YES; 292 | } 293 | } 294 | if (!variableLength) { 295 | return Nil; 296 | } 297 | } 298 | 299 | if (scanData.length < startByte+beaconTypeCodeLength+[self.matchingBeaconTypeCodeStartOffset intValue]) { 300 | // advertisement is too small 301 | } 302 | else { 303 | if ([self byteArray: bytes+startByte+[self.matchingBeaconTypeCodeStartOffset intValue] matchesByteArray: beaconTypeCodeBytes withLength: beaconTypeCodeLength]) { 304 | patternFound = YES; 305 | } 306 | } 307 | 308 | if (patternFound == NO) { 309 | // This is not a beacon 310 | return Nil; 311 | } 312 | 313 | beacon.extraFrame = self.extraFrame; 314 | beacon.name = device.name; 315 | beacon.rssi = rssi; 316 | beacon.beaconTypeCode = self.matchingBeaconTypeCode; 317 | beacon.measuredPower = [NSNumber numberWithInt: 0]; 318 | 319 | NSMutableArray *identifiers = [[NSMutableArray alloc] init]; 320 | 321 | for (int i = 0; i < self.identifierEndOffsets.count; i++) { 322 | int startOffset = [[self.identifierStartOffsets objectAtIndex: i] intValue]; 323 | int endOffset = [[self.identifierEndOffsets objectAtIndex: i] intValue]; 324 | BOOL littleEndian = [[self.identifierLittleEndianFlags objectAtIndex: i] boolValue]; 325 | BOOL variableLength = [[self.identifierVariableLengthFlags objectAtIndex: i] boolValue]; 326 | if (variableLength) { 327 | if (endOffset+startByte >= scanData.length) { 328 | endOffset = (int) scanData.length-1-startByte; 329 | } 330 | } 331 | int length = endOffset - startOffset +1; 332 | NSString *idString = [self formattedStringIdentiferFromByteArray: bytes+startOffset+startByte ofLength: length asLittleEndian:littleEndian]; 333 | [identifiers addObject:idString]; 334 | } 335 | beacon.identifiers = identifiers; 336 | 337 | int measuredPower = 0; 338 | if (self.powerStartOffset != nil) { // Don't parse measured power if it is not in format 339 | int startOffset = [self.powerStartOffset intValue]; 340 | int endOffset = [self.powerEndOffset intValue]; 341 | int length = endOffset-startOffset +1; 342 | NSString *powerString = [self formattedStringIdentiferFromByteArray:bytes+startOffset+startByte ofLength:length asLittleEndian:NO]; 343 | measuredPower = (int) [powerString integerValue]; 344 | // make sure it is a signed integer 345 | if (measuredPower > 127) { 346 | measuredPower -= 256; 347 | } 348 | measuredPower += [self.powerCorrection integerValue]; 349 | beacon.measuredPower = [NSNumber numberWithInt: measuredPower]; 350 | } 351 | 352 | 353 | NSMutableArray *dataFields = [[NSMutableArray alloc] init]; 354 | for (int i = 0; i < self.dataEndOffsets.count; i++) { 355 | int startOffset = [[self.dataStartOffsets objectAtIndex: i] intValue]; 356 | int endOffset = [[self.dataEndOffsets objectAtIndex: i] intValue]; 357 | int length = endOffset - startOffset +1; 358 | BOOL littleEndian = [[self.dataLittleEndianFlags objectAtIndex: i] boolValue]; 359 | NSString *idString = [self formattedStringIdentiferFromByteArray: bytes+startOffset+startByte ofLength: length asLittleEndian:littleEndian]; 360 | [dataFields addObject:idString]; 361 | } 362 | 363 | beacon.dataFields = dataFields; 364 | beacon.serviceUuid = serviceUuid; 365 | 366 | // We will not expose the bluetooth mac address because it gets spoofed on iOS and is of no value 367 | // but we will track the uuid as a bluetooth identifier proxy 368 | beacon.bluetoothIdentifier = [device.identifier UUIDString]; 369 | 370 | if (beacon.serviceUuid.intValue != -1) { 371 | NSString *manufacturerString = [self formattedStringIdentiferFromByteArray:bytes ofLength:2 asLittleEndian:NO]; 372 | beacon.manufacturer = [NSNumber numberWithLong: [manufacturerString integerValue]]; 373 | } 374 | else { 375 | beacon.manufacturer = @-1; 376 | } 377 | 378 | return beacon; 379 | } 380 | 381 | - (void) value: (long) value toByteArray: (unsigned char *)bytes withLength: (int) length { 382 | for (int i = 0; i < length; i++) { 383 | bytes[length-i-1] = (value >> i*8) & 0xFF; 384 | } 385 | } 386 | 387 | -(NSString *) hexStringFromData: (NSData *) data { 388 | return [self hexStringFromBytes: [data bytes] ofLength: data.length withSpaces: YES]; 389 | } 390 | 391 | -(NSString *) hexStringFromBytes: (const unsigned char *) bytes ofLength: (unsigned long) length withSpaces: (BOOL) addSpaces { 392 | NSString *str = @""; 393 | for (int i = 0; i < length; i++) { 394 | if (addSpaces) { 395 | str = [NSString stringWithFormat:@"%@ %02X", str, bytes[i]]; 396 | } 397 | else { 398 | str = [NSString stringWithFormat:@"%@%02X", str, bytes[i]]; 399 | } 400 | } 401 | return str; 402 | } 403 | 404 | -(BOOL) byteArray: (const unsigned char *)bytes1 matchesByteArray: (const unsigned char *) bytes2 withLength: (int) length { 405 | for (int i = 0; i < length; i++) { 406 | if (bytes1[i] != bytes2[i]) { 407 | return NO; 408 | } 409 | } 410 | return YES; 411 | } 412 | 413 | -(NSString *) formattedStringIdentiferFromByteArray: (const unsigned char *) byteArray ofLength: (int) length asLittleEndian: (BOOL) littleEndian { 414 | unsigned char* bytes = malloc(length * sizeof(unsigned char)); 415 | NSString *formattedIdentifier = Nil; 416 | 417 | if (littleEndian) { 418 | for (int i = 0; i < length; i++) { 419 | bytes[i] = byteArray[length-1-i]; 420 | } 421 | } 422 | else { 423 | for (int i = 0; i < length; i++) { 424 | bytes[i] = byteArray[i]; 425 | } 426 | } 427 | 428 | // We treat a 1-4 byte number as decimal string 429 | if (length < 5) { 430 | long number = 0l; 431 | for (int i = 0; i < length; i++) { 432 | long byteValue = (long) (bytes[length - i-1] & 0xff); 433 | long positionValue = 1 << i*8; 434 | long calculatedValue = (long) (byteValue * positionValue); 435 | number += calculatedValue; 436 | } 437 | formattedIdentifier = [NSString stringWithFormat:@"%ld", number]; 438 | } 439 | 440 | if (formattedIdentifier == Nil) { 441 | // We treat a 6+ byte number as a hex string 442 | NSString *hexString = [self hexStringFromBytes: bytes ofLength: length withSpaces: NO]; 443 | // And if it is a 16 byte number we add dashes to it to make it look like a standard UUID 444 | if (length == 16) { 445 | NSMutableString *guid = [NSMutableString stringWithString: hexString]; 446 | [guid insertString: @"-" atIndex: 8]; 447 | [guid insertString: @"-" atIndex: 13]; 448 | [guid insertString: @"-" atIndex: 18]; 449 | [guid insertString: @"-" atIndex: 23]; 450 | formattedIdentifier = guid; 451 | } 452 | else { 453 | formattedIdentifier = [NSString stringWithFormat:@"0x%@", hexString]; 454 | } 455 | } 456 | 457 | free(bytes); 458 | return formattedIdentifier; 459 | } 460 | 461 | -(int) biggestOffset { 462 | int biggestOffset = [self.matchingBeaconTypeCodeEndOffset intValue]; 463 | if ([self.powerEndOffset intValue] > biggestOffset) { 464 | biggestOffset = [self.powerEndOffset intValue]; 465 | } 466 | for (NSNumber *identifierEndOffset in self.identifierEndOffsets) { 467 | if ([identifierEndOffset intValue] > biggestOffset) { 468 | biggestOffset = [identifierEndOffset intValue]; 469 | } 470 | } 471 | for (NSNumber *dataEndOffset in self.dataEndOffsets) { 472 | if ([dataEndOffset intValue] > biggestOffset) { 473 | biggestOffset = [dataEndOffset intValue]; 474 | } 475 | } 476 | return biggestOffset; 477 | } 478 | 479 | @end 480 | 481 | -------------------------------------------------------------------------------- /ios-beacon-tools.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | EEAF8D92210153D3003E6402 /* sample.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAF8D91210153D3003E6402 /* sample.swift */; }; 11 | EEAF8D95210158AD003E6402 /* RNLURLBeaconCompressor.m in Sources */ = {isa = PBXBuildFile; fileRef = EEAF8D94210158AD003E6402 /* RNLURLBeaconCompressor.m */; }; 12 | EEAF8D96210158AD003E6402 /* RNLURLBeaconCompressor.m in Sources */ = {isa = PBXBuildFile; fileRef = EEAF8D94210158AD003E6402 /* RNLURLBeaconCompressor.m */; }; 13 | EEDCDB771B9F92FA00AE693C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDB761B9F92FA00AE693C /* main.m */; }; 14 | EEDCDB7A1B9F92FA00AE693C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDB791B9F92FA00AE693C /* AppDelegate.m */; }; 15 | EEDCDB801B9F92FA00AE693C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EEDCDB7E1B9F92FA00AE693C /* Main.storyboard */; }; 16 | EEDCDB821B9F92FA00AE693C /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EEDCDB811B9F92FA00AE693C /* Images.xcassets */; }; 17 | EEDCDB851B9F92FA00AE693C /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = EEDCDB831B9F92FA00AE693C /* LaunchScreen.xib */; }; 18 | EEDCDB911B9F92FA00AE693C /* ios_beacon_toolsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDB901B9F92FA00AE693C /* ios_beacon_toolsTests.m */; }; 19 | EEDCDBAF1B9F935500AE693C /* RNLAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDB9B1B9F935500AE693C /* RNLAppDelegate.m */; }; 20 | EEDCDBB01B9F935500AE693C /* RNLAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDB9B1B9F935500AE693C /* RNLAppDelegate.m */; }; 21 | EEDCDBB31B9F935500AE693C /* RNLBeaconTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDB9F1B9F935500AE693C /* RNLBeaconTracker.m */; }; 22 | EEDCDBB41B9F935500AE693C /* RNLBeaconTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDB9F1B9F935500AE693C /* RNLBeaconTracker.m */; }; 23 | EEDCDBB51B9F935500AE693C /* RNLLocationTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBA11B9F935500AE693C /* RNLLocationTracker.m */; }; 24 | EEDCDBB61B9F935500AE693C /* RNLLocationTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBA11B9F935500AE693C /* RNLLocationTracker.m */; }; 25 | EEDCDBB71B9F935500AE693C /* RNLSignalMeasurement.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBA31B9F935500AE693C /* RNLSignalMeasurement.m */; }; 26 | EEDCDBB81B9F935500AE693C /* RNLSignalMeasurement.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBA31B9F935500AE693C /* RNLSignalMeasurement.m */; }; 27 | EEDCDBB91B9F935500AE693C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EEDCDBA41B9F935500AE693C /* Main.storyboard */; }; 28 | EEDCDBBA1B9F935500AE693C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EEDCDBA41B9F935500AE693C /* Main.storyboard */; }; 29 | EEDCDBBB1B9F935500AE693C /* RNLBeacon+Distance.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBA61B9F935500AE693C /* RNLBeacon+Distance.m */; }; 30 | EEDCDBBC1B9F935500AE693C /* RNLBeacon+Distance.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBA61B9F935500AE693C /* RNLBeacon+Distance.m */; }; 31 | EEDCDBBD1B9F935500AE693C /* RNLBeaconScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBA81B9F935500AE693C /* RNLBeaconScanner.m */; }; 32 | EEDCDBBE1B9F935500AE693C /* RNLBeaconScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBA81B9F935500AE693C /* RNLBeaconScanner.m */; }; 33 | EEDCDBBF1B9F935500AE693C /* RNLBeaconParser.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBAA1B9F935500AE693C /* RNLBeaconParser.m */; }; 34 | EEDCDBC01B9F935500AE693C /* RNLBeaconParser.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBAA1B9F935500AE693C /* RNLBeaconParser.m */; }; 35 | EEDCDBC11B9F935500AE693C /* RNLBeacon.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBAC1B9F935500AE693C /* RNLBeacon.m */; }; 36 | EEDCDBC21B9F935500AE693C /* RNLBeacon.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBAC1B9F935500AE693C /* RNLBeacon.m */; }; 37 | EEDCDBC31B9F935500AE693C /* RNLBeaconTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBAE1B9F935500AE693C /* RNLBeaconTableViewController.m */; }; 38 | EEDCDBC41B9F935500AE693C /* RNLBeaconTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EEDCDBAE1B9F935500AE693C /* RNLBeaconTableViewController.m */; }; 39 | EEDCDBC61B9F93BD00AE693C /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = EEDCDBC51B9F93BD00AE693C /* LICENSE */; }; 40 | EEDCDBC71B9F93BD00AE693C /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = EEDCDBC51B9F93BD00AE693C /* LICENSE */; }; 41 | /* End PBXBuildFile section */ 42 | 43 | /* Begin PBXContainerItemProxy section */ 44 | EEDCDB8B1B9F92FA00AE693C /* PBXContainerItemProxy */ = { 45 | isa = PBXContainerItemProxy; 46 | containerPortal = EEDCDB691B9F92FA00AE693C /* Project object */; 47 | proxyType = 1; 48 | remoteGlobalIDString = EEDCDB701B9F92FA00AE693C; 49 | remoteInfo = "ios-beacon-tools"; 50 | }; 51 | /* End PBXContainerItemProxy section */ 52 | 53 | /* Begin PBXFileReference section */ 54 | EEAF8D90210153D3003E6402 /* ios-beacon-tools-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ios-beacon-tools-Bridging-Header.h"; sourceTree = ""; }; 55 | EEAF8D91210153D3003E6402 /* sample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = sample.swift; sourceTree = ""; }; 56 | EEAF8D93210158AD003E6402 /* RNLURLBeaconCompressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNLURLBeaconCompressor.h; sourceTree = ""; }; 57 | EEAF8D94210158AD003E6402 /* RNLURLBeaconCompressor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNLURLBeaconCompressor.m; sourceTree = ""; }; 58 | EEDCDB711B9F92FA00AE693C /* ios-beacon-tools.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ios-beacon-tools.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | EEDCDB751B9F92FA00AE693C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 60 | EEDCDB761B9F92FA00AE693C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 61 | EEDCDB781B9F92FA00AE693C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 62 | EEDCDB791B9F92FA00AE693C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 63 | EEDCDB7F1B9F92FA00AE693C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 64 | EEDCDB811B9F92FA00AE693C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 65 | EEDCDB841B9F92FA00AE693C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 66 | EEDCDB8A1B9F92FA00AE693C /* ios-beacon-toolsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ios-beacon-toolsTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | EEDCDB8F1B9F92FA00AE693C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | EEDCDB901B9F92FA00AE693C /* ios_beacon_toolsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ios_beacon_toolsTests.m; sourceTree = ""; }; 69 | EEDCDB9A1B9F935500AE693C /* RNLAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNLAppDelegate.h; sourceTree = ""; }; 70 | EEDCDB9B1B9F935500AE693C /* RNLAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNLAppDelegate.m; sourceTree = ""; }; 71 | EEDCDB9E1B9F935500AE693C /* RNLBeaconTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNLBeaconTracker.h; sourceTree = ""; }; 72 | EEDCDB9F1B9F935500AE693C /* RNLBeaconTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNLBeaconTracker.m; sourceTree = ""; }; 73 | EEDCDBA01B9F935500AE693C /* RNLLocationTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNLLocationTracker.h; sourceTree = ""; }; 74 | EEDCDBA11B9F935500AE693C /* RNLLocationTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNLLocationTracker.m; sourceTree = ""; }; 75 | EEDCDBA21B9F935500AE693C /* RNLSignalMeasurement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNLSignalMeasurement.h; sourceTree = ""; }; 76 | EEDCDBA31B9F935500AE693C /* RNLSignalMeasurement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNLSignalMeasurement.m; sourceTree = ""; }; 77 | EEDCDBA41B9F935500AE693C /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 78 | EEDCDBA51B9F935500AE693C /* RNLBeacon+Distance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RNLBeacon+Distance.h"; sourceTree = ""; }; 79 | EEDCDBA61B9F935500AE693C /* RNLBeacon+Distance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RNLBeacon+Distance.m"; sourceTree = ""; }; 80 | EEDCDBA71B9F935500AE693C /* RNLBeaconScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNLBeaconScanner.h; sourceTree = ""; }; 81 | EEDCDBA81B9F935500AE693C /* RNLBeaconScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNLBeaconScanner.m; sourceTree = ""; }; 82 | EEDCDBA91B9F935500AE693C /* RNLBeaconParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNLBeaconParser.h; sourceTree = ""; }; 83 | EEDCDBAA1B9F935500AE693C /* RNLBeaconParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNLBeaconParser.m; sourceTree = ""; }; 84 | EEDCDBAB1B9F935500AE693C /* RNLBeacon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNLBeacon.h; sourceTree = ""; }; 85 | EEDCDBAC1B9F935500AE693C /* RNLBeacon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNLBeacon.m; sourceTree = ""; }; 86 | EEDCDBAD1B9F935500AE693C /* RNLBeaconTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNLBeaconTableViewController.h; sourceTree = ""; }; 87 | EEDCDBAE1B9F935500AE693C /* RNLBeaconTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNLBeaconTableViewController.m; sourceTree = ""; }; 88 | EEDCDBC51B9F93BD00AE693C /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 89 | /* End PBXFileReference section */ 90 | 91 | /* Begin PBXFrameworksBuildPhase section */ 92 | EEDCDB6E1B9F92FA00AE693C /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | EEDCDB871B9F92FA00AE693C /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | /* End PBXFrameworksBuildPhase section */ 107 | 108 | /* Begin PBXGroup section */ 109 | EEDCDB681B9F92FA00AE693C = { 110 | isa = PBXGroup; 111 | children = ( 112 | EEAF8D91210153D3003E6402 /* sample.swift */, 113 | EEDCDBC51B9F93BD00AE693C /* LICENSE */, 114 | EEDCDB731B9F92FA00AE693C /* ios-beacon-tools */, 115 | EEDCDB8D1B9F92FA00AE693C /* ios-beacon-toolsTests */, 116 | EEDCDB721B9F92FA00AE693C /* Products */, 117 | EEAF8D90210153D3003E6402 /* ios-beacon-tools-Bridging-Header.h */, 118 | ); 119 | sourceTree = ""; 120 | }; 121 | EEDCDB721B9F92FA00AE693C /* Products */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | EEDCDB711B9F92FA00AE693C /* ios-beacon-tools.app */, 125 | EEDCDB8A1B9F92FA00AE693C /* ios-beacon-toolsTests.xctest */, 126 | ); 127 | name = Products; 128 | sourceTree = ""; 129 | }; 130 | EEDCDB731B9F92FA00AE693C /* ios-beacon-tools */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | EEAF8D93210158AD003E6402 /* RNLURLBeaconCompressor.h */, 134 | EEAF8D94210158AD003E6402 /* RNLURLBeaconCompressor.m */, 135 | EEDCDB9A1B9F935500AE693C /* RNLAppDelegate.h */, 136 | EEDCDB9B1B9F935500AE693C /* RNLAppDelegate.m */, 137 | EEDCDB9E1B9F935500AE693C /* RNLBeaconTracker.h */, 138 | EEDCDB9F1B9F935500AE693C /* RNLBeaconTracker.m */, 139 | EEDCDBA01B9F935500AE693C /* RNLLocationTracker.h */, 140 | EEDCDBA11B9F935500AE693C /* RNLLocationTracker.m */, 141 | EEDCDBA21B9F935500AE693C /* RNLSignalMeasurement.h */, 142 | EEDCDBA31B9F935500AE693C /* RNLSignalMeasurement.m */, 143 | EEDCDBA41B9F935500AE693C /* Main.storyboard */, 144 | EEDCDBA51B9F935500AE693C /* RNLBeacon+Distance.h */, 145 | EEDCDBA61B9F935500AE693C /* RNLBeacon+Distance.m */, 146 | EEDCDBA71B9F935500AE693C /* RNLBeaconScanner.h */, 147 | EEDCDBA81B9F935500AE693C /* RNLBeaconScanner.m */, 148 | EEDCDBA91B9F935500AE693C /* RNLBeaconParser.h */, 149 | EEDCDBAA1B9F935500AE693C /* RNLBeaconParser.m */, 150 | EEDCDBAB1B9F935500AE693C /* RNLBeacon.h */, 151 | EEDCDBAC1B9F935500AE693C /* RNLBeacon.m */, 152 | EEDCDBAD1B9F935500AE693C /* RNLBeaconTableViewController.h */, 153 | EEDCDBAE1B9F935500AE693C /* RNLBeaconTableViewController.m */, 154 | EEDCDB781B9F92FA00AE693C /* AppDelegate.h */, 155 | EEDCDB791B9F92FA00AE693C /* AppDelegate.m */, 156 | EEDCDB7E1B9F92FA00AE693C /* Main.storyboard */, 157 | EEDCDB811B9F92FA00AE693C /* Images.xcassets */, 158 | EEDCDB831B9F92FA00AE693C /* LaunchScreen.xib */, 159 | EEDCDB741B9F92FA00AE693C /* Supporting Files */, 160 | ); 161 | path = "ios-beacon-tools"; 162 | sourceTree = ""; 163 | }; 164 | EEDCDB741B9F92FA00AE693C /* Supporting Files */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | EEDCDB751B9F92FA00AE693C /* Info.plist */, 168 | EEDCDB761B9F92FA00AE693C /* main.m */, 169 | ); 170 | name = "Supporting Files"; 171 | sourceTree = ""; 172 | }; 173 | EEDCDB8D1B9F92FA00AE693C /* ios-beacon-toolsTests */ = { 174 | isa = PBXGroup; 175 | children = ( 176 | EEDCDB901B9F92FA00AE693C /* ios_beacon_toolsTests.m */, 177 | EEDCDB8E1B9F92FA00AE693C /* Supporting Files */, 178 | ); 179 | path = "ios-beacon-toolsTests"; 180 | sourceTree = ""; 181 | }; 182 | EEDCDB8E1B9F92FA00AE693C /* Supporting Files */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | EEDCDB8F1B9F92FA00AE693C /* Info.plist */, 186 | ); 187 | name = "Supporting Files"; 188 | sourceTree = ""; 189 | }; 190 | /* End PBXGroup section */ 191 | 192 | /* Begin PBXNativeTarget section */ 193 | EEDCDB701B9F92FA00AE693C /* ios-beacon-tools */ = { 194 | isa = PBXNativeTarget; 195 | buildConfigurationList = EEDCDB941B9F92FA00AE693C /* Build configuration list for PBXNativeTarget "ios-beacon-tools" */; 196 | buildPhases = ( 197 | EEDCDB6D1B9F92FA00AE693C /* Sources */, 198 | EEDCDB6E1B9F92FA00AE693C /* Frameworks */, 199 | EEDCDB6F1B9F92FA00AE693C /* Resources */, 200 | ); 201 | buildRules = ( 202 | ); 203 | dependencies = ( 204 | ); 205 | name = "ios-beacon-tools"; 206 | productName = "ios-beacon-tools"; 207 | productReference = EEDCDB711B9F92FA00AE693C /* ios-beacon-tools.app */; 208 | productType = "com.apple.product-type.application"; 209 | }; 210 | EEDCDB891B9F92FA00AE693C /* ios-beacon-toolsTests */ = { 211 | isa = PBXNativeTarget; 212 | buildConfigurationList = EEDCDB971B9F92FA00AE693C /* Build configuration list for PBXNativeTarget "ios-beacon-toolsTests" */; 213 | buildPhases = ( 214 | EEDCDB861B9F92FA00AE693C /* Sources */, 215 | EEDCDB871B9F92FA00AE693C /* Frameworks */, 216 | EEDCDB881B9F92FA00AE693C /* Resources */, 217 | ); 218 | buildRules = ( 219 | ); 220 | dependencies = ( 221 | EEDCDB8C1B9F92FA00AE693C /* PBXTargetDependency */, 222 | ); 223 | name = "ios-beacon-toolsTests"; 224 | productName = "ios-beacon-toolsTests"; 225 | productReference = EEDCDB8A1B9F92FA00AE693C /* ios-beacon-toolsTests.xctest */; 226 | productType = "com.apple.product-type.bundle.unit-test"; 227 | }; 228 | /* End PBXNativeTarget section */ 229 | 230 | /* Begin PBXProject section */ 231 | EEDCDB691B9F92FA00AE693C /* Project object */ = { 232 | isa = PBXProject; 233 | attributes = { 234 | LastUpgradeCheck = 0640; 235 | ORGANIZATIONNAME = Altbeacon.org; 236 | TargetAttributes = { 237 | EEDCDB701B9F92FA00AE693C = { 238 | CreatedOnToolsVersion = 6.4; 239 | LastSwiftMigration = 0940; 240 | }; 241 | EEDCDB891B9F92FA00AE693C = { 242 | CreatedOnToolsVersion = 6.4; 243 | TestTargetID = EEDCDB701B9F92FA00AE693C; 244 | }; 245 | }; 246 | }; 247 | buildConfigurationList = EEDCDB6C1B9F92FA00AE693C /* Build configuration list for PBXProject "ios-beacon-tools" */; 248 | compatibilityVersion = "Xcode 3.2"; 249 | developmentRegion = English; 250 | hasScannedForEncodings = 0; 251 | knownRegions = ( 252 | English, 253 | en, 254 | Base, 255 | ); 256 | mainGroup = EEDCDB681B9F92FA00AE693C; 257 | productRefGroup = EEDCDB721B9F92FA00AE693C /* Products */; 258 | projectDirPath = ""; 259 | projectRoot = ""; 260 | targets = ( 261 | EEDCDB701B9F92FA00AE693C /* ios-beacon-tools */, 262 | EEDCDB891B9F92FA00AE693C /* ios-beacon-toolsTests */, 263 | ); 264 | }; 265 | /* End PBXProject section */ 266 | 267 | /* Begin PBXResourcesBuildPhase section */ 268 | EEDCDB6F1B9F92FA00AE693C /* Resources */ = { 269 | isa = PBXResourcesBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | EEDCDB801B9F92FA00AE693C /* Main.storyboard in Resources */, 273 | EEDCDB851B9F92FA00AE693C /* LaunchScreen.xib in Resources */, 274 | EEDCDB821B9F92FA00AE693C /* Images.xcassets in Resources */, 275 | EEDCDBB91B9F935500AE693C /* Main.storyboard in Resources */, 276 | EEDCDBC61B9F93BD00AE693C /* LICENSE in Resources */, 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | EEDCDB881B9F92FA00AE693C /* Resources */ = { 281 | isa = PBXResourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | EEDCDBC71B9F93BD00AE693C /* LICENSE in Resources */, 285 | EEDCDBBA1B9F935500AE693C /* Main.storyboard in Resources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXResourcesBuildPhase section */ 290 | 291 | /* Begin PBXSourcesBuildPhase section */ 292 | EEDCDB6D1B9F92FA00AE693C /* Sources */ = { 293 | isa = PBXSourcesBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | EEDCDBC31B9F935500AE693C /* RNLBeaconTableViewController.m in Sources */, 297 | EEAF8D92210153D3003E6402 /* sample.swift in Sources */, 298 | EEDCDBB71B9F935500AE693C /* RNLSignalMeasurement.m in Sources */, 299 | EEDCDBBF1B9F935500AE693C /* RNLBeaconParser.m in Sources */, 300 | EEDCDBBD1B9F935500AE693C /* RNLBeaconScanner.m in Sources */, 301 | EEDCDBB51B9F935500AE693C /* RNLLocationTracker.m in Sources */, 302 | EEDCDBC11B9F935500AE693C /* RNLBeacon.m in Sources */, 303 | EEDCDB7A1B9F92FA00AE693C /* AppDelegate.m in Sources */, 304 | EEDCDB771B9F92FA00AE693C /* main.m in Sources */, 305 | EEDCDBAF1B9F935500AE693C /* RNLAppDelegate.m in Sources */, 306 | EEDCDBBB1B9F935500AE693C /* RNLBeacon+Distance.m in Sources */, 307 | EEAF8D95210158AD003E6402 /* RNLURLBeaconCompressor.m in Sources */, 308 | EEDCDBB31B9F935500AE693C /* RNLBeaconTracker.m in Sources */, 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | EEDCDB861B9F92FA00AE693C /* Sources */ = { 313 | isa = PBXSourcesBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | EEDCDBC21B9F935500AE693C /* RNLBeacon.m in Sources */, 317 | EEDCDBB61B9F935500AE693C /* RNLLocationTracker.m in Sources */, 318 | EEDCDB911B9F92FA00AE693C /* ios_beacon_toolsTests.m in Sources */, 319 | EEDCDBB41B9F935500AE693C /* RNLBeaconTracker.m in Sources */, 320 | EEDCDBC41B9F935500AE693C /* RNLBeaconTableViewController.m in Sources */, 321 | EEAF8D96210158AD003E6402 /* RNLURLBeaconCompressor.m in Sources */, 322 | EEDCDBBE1B9F935500AE693C /* RNLBeaconScanner.m in Sources */, 323 | EEDCDBB81B9F935500AE693C /* RNLSignalMeasurement.m in Sources */, 324 | EEDCDBB01B9F935500AE693C /* RNLAppDelegate.m in Sources */, 325 | EEDCDBBC1B9F935500AE693C /* RNLBeacon+Distance.m in Sources */, 326 | EEDCDBC01B9F935500AE693C /* RNLBeaconParser.m in Sources */, 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | }; 330 | /* End PBXSourcesBuildPhase section */ 331 | 332 | /* Begin PBXTargetDependency section */ 333 | EEDCDB8C1B9F92FA00AE693C /* PBXTargetDependency */ = { 334 | isa = PBXTargetDependency; 335 | target = EEDCDB701B9F92FA00AE693C /* ios-beacon-tools */; 336 | targetProxy = EEDCDB8B1B9F92FA00AE693C /* PBXContainerItemProxy */; 337 | }; 338 | /* End PBXTargetDependency section */ 339 | 340 | /* Begin PBXVariantGroup section */ 341 | EEDCDB7E1B9F92FA00AE693C /* Main.storyboard */ = { 342 | isa = PBXVariantGroup; 343 | children = ( 344 | EEDCDB7F1B9F92FA00AE693C /* Base */, 345 | ); 346 | name = Main.storyboard; 347 | sourceTree = ""; 348 | }; 349 | EEDCDB831B9F92FA00AE693C /* LaunchScreen.xib */ = { 350 | isa = PBXVariantGroup; 351 | children = ( 352 | EEDCDB841B9F92FA00AE693C /* Base */, 353 | ); 354 | name = LaunchScreen.xib; 355 | sourceTree = ""; 356 | }; 357 | /* End PBXVariantGroup section */ 358 | 359 | /* Begin XCBuildConfiguration section */ 360 | EEDCDB921B9F92FA00AE693C /* Debug */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ALWAYS_SEARCH_USER_PATHS = NO; 364 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 365 | CLANG_CXX_LIBRARY = "libc++"; 366 | CLANG_ENABLE_MODULES = YES; 367 | CLANG_ENABLE_OBJC_ARC = YES; 368 | CLANG_WARN_BOOL_CONVERSION = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 371 | CLANG_WARN_EMPTY_BODY = YES; 372 | CLANG_WARN_ENUM_CONVERSION = YES; 373 | CLANG_WARN_INT_CONVERSION = YES; 374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 375 | CLANG_WARN_UNREACHABLE_CODE = YES; 376 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 377 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 378 | COPY_PHASE_STRIP = NO; 379 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 380 | ENABLE_STRICT_OBJC_MSGSEND = YES; 381 | GCC_C_LANGUAGE_STANDARD = gnu99; 382 | GCC_DYNAMIC_NO_PIC = NO; 383 | GCC_NO_COMMON_BLOCKS = YES; 384 | GCC_OPTIMIZATION_LEVEL = 0; 385 | GCC_PREPROCESSOR_DEFINITIONS = ( 386 | "DEBUG=1", 387 | "$(inherited)", 388 | ); 389 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 390 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 391 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 392 | GCC_WARN_UNDECLARED_SELECTOR = YES; 393 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 394 | GCC_WARN_UNUSED_FUNCTION = YES; 395 | GCC_WARN_UNUSED_VARIABLE = YES; 396 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 397 | MTL_ENABLE_DEBUG_INFO = YES; 398 | ONLY_ACTIVE_ARCH = YES; 399 | SDKROOT = iphoneos; 400 | TARGETED_DEVICE_FAMILY = "1,2"; 401 | }; 402 | name = Debug; 403 | }; 404 | EEDCDB931B9F92FA00AE693C /* Release */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | ALWAYS_SEARCH_USER_PATHS = NO; 408 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 409 | CLANG_CXX_LIBRARY = "libc++"; 410 | CLANG_ENABLE_MODULES = YES; 411 | CLANG_ENABLE_OBJC_ARC = YES; 412 | CLANG_WARN_BOOL_CONVERSION = YES; 413 | CLANG_WARN_CONSTANT_CONVERSION = YES; 414 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 415 | CLANG_WARN_EMPTY_BODY = YES; 416 | CLANG_WARN_ENUM_CONVERSION = YES; 417 | CLANG_WARN_INT_CONVERSION = YES; 418 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 419 | CLANG_WARN_UNREACHABLE_CODE = YES; 420 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 421 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 422 | COPY_PHASE_STRIP = NO; 423 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 424 | ENABLE_NS_ASSERTIONS = NO; 425 | ENABLE_STRICT_OBJC_MSGSEND = YES; 426 | GCC_C_LANGUAGE_STANDARD = gnu99; 427 | GCC_NO_COMMON_BLOCKS = YES; 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 435 | MTL_ENABLE_DEBUG_INFO = NO; 436 | SDKROOT = iphoneos; 437 | TARGETED_DEVICE_FAMILY = "1,2"; 438 | VALIDATE_PRODUCT = YES; 439 | }; 440 | name = Release; 441 | }; 442 | EEDCDB951B9F92FA00AE693C /* Debug */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 446 | CLANG_ENABLE_MODULES = YES; 447 | INFOPLIST_FILE = "ios-beacon-tools/Info.plist"; 448 | IPHONEOS_DEPLOYMENT_TARGET = 17.6; 449 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 450 | PRODUCT_NAME = "$(TARGET_NAME)"; 451 | SWIFT_OBJC_BRIDGING_HEADER = "ios-beacon-tools-Bridging-Header.h"; 452 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 453 | SWIFT_VERSION = 5.0; 454 | }; 455 | name = Debug; 456 | }; 457 | EEDCDB961B9F92FA00AE693C /* Release */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 461 | CLANG_ENABLE_MODULES = YES; 462 | INFOPLIST_FILE = "ios-beacon-tools/Info.plist"; 463 | IPHONEOS_DEPLOYMENT_TARGET = 17.6; 464 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 465 | PRODUCT_NAME = "$(TARGET_NAME)"; 466 | SWIFT_OBJC_BRIDGING_HEADER = "ios-beacon-tools-Bridging-Header.h"; 467 | SWIFT_VERSION = 5.0; 468 | }; 469 | name = Release; 470 | }; 471 | EEDCDB981B9F92FA00AE693C /* Debug */ = { 472 | isa = XCBuildConfiguration; 473 | buildSettings = { 474 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 475 | BUNDLE_LOADER = "$(TEST_HOST)"; 476 | FRAMEWORK_SEARCH_PATHS = ( 477 | "$(SDKROOT)/Developer/Library/Frameworks", 478 | "$(inherited)", 479 | ); 480 | GCC_PREPROCESSOR_DEFINITIONS = ( 481 | "DEBUG=1", 482 | "$(inherited)", 483 | ); 484 | INFOPLIST_FILE = "ios-beacon-toolsTests/Info.plist"; 485 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 486 | PRODUCT_NAME = "$(TARGET_NAME)"; 487 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ios-beacon-tools.app/ios-beacon-tools"; 488 | }; 489 | name = Debug; 490 | }; 491 | EEDCDB991B9F92FA00AE693C /* Release */ = { 492 | isa = XCBuildConfiguration; 493 | buildSettings = { 494 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 495 | BUNDLE_LOADER = "$(TEST_HOST)"; 496 | FRAMEWORK_SEARCH_PATHS = ( 497 | "$(SDKROOT)/Developer/Library/Frameworks", 498 | "$(inherited)", 499 | ); 500 | INFOPLIST_FILE = "ios-beacon-toolsTests/Info.plist"; 501 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 502 | PRODUCT_NAME = "$(TARGET_NAME)"; 503 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ios-beacon-tools.app/ios-beacon-tools"; 504 | }; 505 | name = Release; 506 | }; 507 | /* End XCBuildConfiguration section */ 508 | 509 | /* Begin XCConfigurationList section */ 510 | EEDCDB6C1B9F92FA00AE693C /* Build configuration list for PBXProject "ios-beacon-tools" */ = { 511 | isa = XCConfigurationList; 512 | buildConfigurations = ( 513 | EEDCDB921B9F92FA00AE693C /* Debug */, 514 | EEDCDB931B9F92FA00AE693C /* Release */, 515 | ); 516 | defaultConfigurationIsVisible = 0; 517 | defaultConfigurationName = Release; 518 | }; 519 | EEDCDB941B9F92FA00AE693C /* Build configuration list for PBXNativeTarget "ios-beacon-tools" */ = { 520 | isa = XCConfigurationList; 521 | buildConfigurations = ( 522 | EEDCDB951B9F92FA00AE693C /* Debug */, 523 | EEDCDB961B9F92FA00AE693C /* Release */, 524 | ); 525 | defaultConfigurationIsVisible = 0; 526 | defaultConfigurationName = Release; 527 | }; 528 | EEDCDB971B9F92FA00AE693C /* Build configuration list for PBXNativeTarget "ios-beacon-toolsTests" */ = { 529 | isa = XCConfigurationList; 530 | buildConfigurations = ( 531 | EEDCDB981B9F92FA00AE693C /* Debug */, 532 | EEDCDB991B9F92FA00AE693C /* Release */, 533 | ); 534 | defaultConfigurationIsVisible = 0; 535 | defaultConfigurationName = Release; 536 | }; 537 | /* End XCConfigurationList section */ 538 | }; 539 | rootObject = EEDCDB691B9F92FA00AE693C /* Project object */; 540 | } 541 | --------------------------------------------------------------------------------