├── .gitignore ├── BeaconCtrl.podspec ├── BeaconCtrl.xcodeproj ├── BeaconOS.xcodeproj │ └── project.pbxproj └── project.pbxproj ├── BeaconCtrl ├── BCLAction.h ├── BCLAction.m ├── BCLBeacon.h ├── BCLBeacon.m ├── BCLBeaconControl-Bridging-Header.h ├── BCLBeaconCtrl.h ├── BCLBeaconCtrl.m ├── BCLBeaconCtrlAdmin.h ├── BCLBeaconCtrlAdmin.m ├── BCLBeaconCtrlDelegate.h ├── BCLBeaconRangingBatch.h ├── BCLBeaconRangingBatch.m ├── BCLCondition.h ├── BCLConfiguration.h ├── BCLConfiguration.m ├── BCLEncodableObject.h ├── BCLEncodableObject.m ├── BCLEventScheduler.h ├── BCLEventScheduler.m ├── BCLExtension.h ├── BCLKontaktIOBeaconConfigManager.h ├── BCLKontaktIOBeaconConfigManager.m ├── BCLLocation.h ├── BCLLocation.m ├── BCLTrigger.h ├── BCLTrigger.m ├── BCLTypes.h ├── BCLZone.h ├── BCLZone.m ├── BeaconCtrl-Prefix.pch ├── CLBeacon+BeaconCtrl.h ├── CLBeacon+BeaconCtrl.m ├── Conditions │ ├── BCLConditionEvent.h │ └── BCLConditionEvent.m ├── Private │ ├── BCLAbstractBackend.h │ ├── BCLAbstractBackend.m │ ├── BCLActionEvent.h │ ├── BCLActionEvent.m │ ├── BCLActionEventScheduler.h │ ├── BCLActionEventScheduler.m │ ├── BCLActionHandler.h │ ├── BCLActionHandlerFactory.h │ ├── BCLActionHandlerFactory.m │ ├── BCLAdminBackend.h │ ├── BCLAdminBackend.m │ ├── BCLBackend.h │ ├── BCLBackend.m │ ├── BCLCouponActionHandler.h │ ├── BCLCouponActionHandler.m │ ├── BCLObservedBeaconsPicker.h │ ├── BCLObservedBeaconsPicker.m │ ├── BCLURLActionHandler.h │ ├── BCLURLActionHandler.m │ ├── BCLUtils.h │ ├── BCLUtils.m │ ├── NSHTTPURLResponse+BCLHTTPCodes.h │ ├── NSHTTPURLResponse+BCLHTTPCodes.m │ ├── NSObject+BCLAdditions.h │ ├── NSObject+BCLAdditions.m │ ├── NSUserDefaults+BCLiCloud.h │ ├── NSUserDefaults+BCLiCloud.m │ ├── UIWindow+BCLVisibleViewController.h │ └── UIWindow+BCLVisibleViewController.m ├── SAMCache+BeaconCtrl.h ├── SAMCache+BeaconCtrl.m ├── UIColor+Hex.h └── UIColor+Hex.m ├── LICENSE.txt ├── Podfile ├── README.md └── help └── html ├── Classes ├── BCLAction.html ├── BCLBeacon.html ├── BCLBeaconCtrl.html ├── BCLBeaconCtrlAdmin.html ├── BCLConfiguration.html ├── BCLEventScheduler.html └── BCLZone.html ├── Constants ├── BCLBeaconCtrlPushEnvironment.html └── BCLEventType.html ├── Protocols └── BCLBeaconCtrlDelegate.html ├── css ├── scss │ ├── _index.scss │ ├── _layout.scss │ ├── _normalize.scss │ ├── _object.scss │ ├── _print.scss │ ├── _variables.scss │ ├── _xcode.scss │ └── style.scss └── style.css ├── hierarchy.html ├── img ├── button_bar_background.png ├── disclosure.png ├── disclosure_open.png ├── library_background.png └── title_background.png ├── index.html └── js └── script.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | .idea 3 | 4 | *.xcuserstate 5 | xcuserdata/ 6 | *.xccheckout 7 | BeaconCtrl.xcodeproj/project.xcworkspace/ 8 | 9 | *.swp 10 | *~.nib 11 | -------------------------------------------------------------------------------- /BeaconCtrl.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "BeaconCtrl" 3 | s.version = "0.0.1" 4 | s.summary = "Low Energy Bluetooth Framework" 5 | s.authors = { "Upnext Ltd." => "http://www.up-next.com"} 6 | s.homepage = "http://www.up-next.com/beacon" 7 | s.source = { :git => "ssh://git@stash.up-next.com:7999/bp/bp-ios-sdk.git", :tag => "v#{s.version}" } 8 | s.license = 'LICENSE*.*' 9 | 10 | s.platform = :ios, '7.0' 11 | s.ios.deployment_target = '7.0' 12 | 13 | s.source_files = "BeaconCtrl", "BeaconCtrl/**/*.{h,m}" 14 | s.private_header_files = "BeaconCtrl/Private/*.h" 15 | 16 | s.frameworks = 'Foundation', 'CoreFoundation', 'CoreLocation', 'SystemConfiguration', 'MobileCoreServices', 'UIKit' 17 | s.weak_frameworks = 'Twitter', 'Social', 'Accounts' 18 | 19 | s.dependency "SAMCache" 20 | s.dependency "UNNetworking" 21 | s.dependency "KontaktSDK-OLD" 22 | 23 | s.requires_arc = true 24 | end 25 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLAction.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLAction.h 3 | // 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "BCLEncodableObject.h" 14 | 15 | @class BCLTrigger; 16 | 17 | /*! 18 | * A class representing BeaconCtrl actions in the SDK 19 | */ 20 | @interface BCLAction : BCLEncodableObject 21 | 22 | /** @name Properties */ 23 | 24 | /// An identifier assigned by the backend 25 | @property (strong) NSNumber *identifier; 26 | 27 | /// Action's name 28 | @property (strong) NSString *name; 29 | 30 | /// Action's type 31 | @property (strong) NSString *type; 32 | 33 | /// Is this a test action (a special subtype of a custom action that can be created while adding a beacon in the admin interface) 34 | @property (nonatomic) BOOL isTestAction; 35 | 36 | /// An array with custom values assigned to an action in the admin interface 37 | @property (strong) NSArray *customValues; 38 | 39 | /// A dictionary with a custom payload of an action (e.g. an URL in case of URL actions) 40 | @property (strong) NSDictionary *payload; 41 | 42 | /// A trigger that calls an action 43 | @property (weak) BCLTrigger *trigger; 44 | 45 | /// A callback that is called when an action is performed 46 | @property (copy) void(^onActionCallback)(BCLAction *action); 47 | 48 | /** @name Methods */ 49 | 50 | /*! 51 | * @brief A shortcut method that returns an URL assigned to some types of actions 52 | * 53 | * @return An URL object assigned to an action 54 | */ 55 | - (NSURL *)URL; 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLAction.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLAction.m 3 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 4 | // All rights reserved. 5 | // 6 | // This source code is licensed under the BSD 3-Clause License found in the 7 | // LICENSE.txt file in the root directory of this source tree. 8 | // 9 | 10 | static NSString * const BCLActionCouponTypeName = @"coupon"; 11 | static NSString * const BCLActionURLTypeName = @"url"; 12 | static NSString * const BCLActionCouponURLAttrName = @"url"; 13 | 14 | #import "BCLAction.h" 15 | #import "UNCodingUtil.h" 16 | 17 | @implementation BCLAction 18 | 19 | - (NSString *)description 20 | { 21 | return [NSString stringWithFormat:@"%@ (%@)", self.name, self.type]; 22 | } 23 | 24 | - (BOOL)isCouponAction 25 | { 26 | return [self.type.lowercaseString isEqualToString:BCLActionCouponTypeName]; 27 | } 28 | 29 | - (BOOL)isUrlAction 30 | { 31 | return [self.type.lowercaseString isEqualToString:BCLActionURLTypeName]; 32 | } 33 | 34 | - (NSURL *)URL 35 | { 36 | if (!self.isCouponAction && !self.isUrlAction) { 37 | return nil; 38 | } 39 | 40 | return [NSURL URLWithString:self.payload[BCLActionCouponURLAttrName]]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLBeacon.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD 3-Clause License found in the 6 | // LICENSE.txt file in the root directory of this source tree. 7 | // 8 | 9 | // An app can register up to 20 regions at a time. 10 | // In order to report region changes in a timely manner, the region monitoring service requires network connectivity. 11 | 12 | #import 13 | #import 14 | 15 | #import 16 | 17 | #import 18 | 19 | @class BCLLocation; 20 | @class BCLZone; 21 | 22 | extern NSString * const BCLInvalidBeaconIdentifierException; 23 | extern NSString * const BCLBeaconTimerFireNotification; 24 | 25 | #define BLEBeaconStaysCacheName(beacon) \ 26 | [NSString stringWithFormat:@"com.up-next.BeaconCtrl.stays.%@",beacon.identifier] 27 | 28 | /*! 29 | * A class representing beacons in BeaconCtrl 30 | */ 31 | @interface BCLBeacon : CLBeacon 32 | 33 | /** @name Properties */ 34 | 35 | /// Protocol of a beacon (iBeacon or Eddystone) 36 | @property (readwrite, nonatomic, strong) NSString *protocol; 37 | 38 | /// UUID value of a beacon 39 | @property (readwrite, nonatomic, strong) NSUUID *proximityUUID; 40 | 41 | /// Major value of an iBeacon 42 | @property (readwrite, nonatomic, strong) NSNumber *major; 43 | 44 | /// Minor value of an iBeacon 45 | @property (readwrite, nonatomic, strong) NSNumber *minor; 46 | 47 | /// Namespace value of an Eddystone beacon 48 | @property (readwrite, nonatomic, strong) NSString *namespaceId; 49 | 50 | /// Intance id of an Eddystone beacon 51 | @property (readwrite, nonatomic, strong) NSString *instanceId; 52 | 53 | /// Proximity of a beacon to a device running the SDK described as a CLProximity constant 54 | @property (readwrite, nonatomic, assign) CLProximity proximity; 55 | 56 | /// Rough distance in meters from a beacon to a device running the SDK, 0 if the beacon is out of range 57 | @property (readwrite, nonatomic, assign) CLLocationAccuracy accuracy; 58 | 59 | /// Estimated distance in meters from a beacon to a device running the SDK 60 | @property (readwrite, nonatomic, assign) double estimatedDistance; 61 | 62 | /// Bluetooth signal strength of a beacon 63 | @property (readwrite, nonatomic, assign) NSInteger rssi; 64 | 65 | /// The date when a beacon's range was last entered 66 | @property (readwrite, nonatomic, strong) NSDate *lastEnteredDate; 67 | 68 | /// Name of a beacon. 69 | @property (strong) NSString *name; 70 | 71 | /// A zone to which a beacon is assigned, nil if there's none 72 | @property (nonatomic, weak) BCLZone *zone; 73 | 74 | /// How long a beacon has been in range of a device running the SDK (time since last entry, if there was no leave afterwards) 75 | @property (assign, readonly) NSTimeInterval staysTimeInterval; 76 | 77 | /// Triggers defined for beacon 78 | @property (strong, nonatomic) NSArray *triggers; 79 | 80 | /// A unique identifier - string of proximityUUID+major+minor 81 | @property (readonly) NSString *identifier; 82 | 83 | /// Beacon identifier assigned by the backend 84 | @property (strong) NSString *beaconIdentifier; 85 | 86 | /// Beacon's vendor (i.e. Kontakt.io, Estimote or other) 87 | @property (strong) NSString *vendor; 88 | 89 | /// Beacon's vendor-specific identifier 90 | @property (strong) NSString *vendorIdentifier; 91 | 92 | /// Beacon's vendor-specific firmware version 93 | @property (strong) NSString *vendorFirmwareVersion; 94 | 95 | /// Transmission power for a beacon. May be NSNotFound, if vendor is unknown 96 | @property (nonatomic) NSUInteger transmissionPower; 97 | 98 | /// Transmission interval for a beacon. May be NSNotFound, if vendor is unknown 99 | @property (nonatomic) NSUInteger transmissionInterval; 100 | 101 | /// Battery level for a beacon. May be NSNotFound, if vendor is unknown 102 | @property (nonatomic) NSUInteger batteryLevel; 103 | 104 | /// A flag stating whether a given beacon has outdated properties 105 | @property (nonatomic) BOOL needsCharacteristicsUpdate; 106 | 107 | /// A dictionary of fields to update with property names as keys and new values as values. May be nil for beacons with an unknown vendor 108 | @property (nonatomic, strong) NSDictionary *fieldsToUpdate; 109 | 110 | /// A flag stating whether a given beacon is currently being updated 111 | @property (nonatomic) BOOL characteristicsAreBeingUpdated; 112 | 113 | /// A flag stating whether a given beacon has outdated firmware 114 | @property (nonatomic) BOOL needsFirmwareUpdate; 115 | 116 | /// Firmware update progress 117 | @property (nonatomic) NSUInteger firmwareUpdateProgress; 118 | 119 | /// Callback called when a beacon's range is entered 120 | @property (copy) void(^onEnterCallback)(BCLBeacon *beacon); 121 | 122 | /// Callback called when a beacon's range is left 123 | @property (copy) void(^onExitCallback)(BCLBeacon *beacon); 124 | 125 | /// Callback called when beacon's proximity changes 126 | @property (copy) void(^onChangeProximityCallback)(BCLBeacon *beacon); 127 | 128 | /** @name MKAnnotation-related properties */ 129 | 130 | @property (nonatomic, copy, readonly) NSString *title; 131 | @property (nonatomic, strong) BCLLocation *location; 132 | @property (nonatomic, readonly) CLLocationCoordinate2D coordinate; 133 | 134 | /** @name Methods */ 135 | 136 | /*! 137 | * @brief Initialize beacon with parameters 138 | * 139 | * @param beaconIdentifier immutable value representing single beacon. 140 | * @param proximityUUID proximity UUID 141 | * @param major major 142 | * @param minor minor 143 | * 144 | * @return Initialized object 145 | */ 146 | - (instancetype) initWithIdentifier:(NSString *)beaconIdentifier proximityUUID:(NSUUID *)proximityUUID major:(NSNumber *)major minor:(NSNumber *)minor; 147 | 148 | /*! 149 | * @brief Update a beacon's properties with values taken from dictionary 150 | * @param dictionary A dictionary with beacon's properties 151 | */ 152 | - (void)updatePropertiesFromDictionary:(NSDictionary *)dictionary; 153 | 154 | /*! 155 | * @brief Check if a new proximity can be set for a beacon 156 | * @param newProximity a proximity that will be checked 157 | * @return YES, if a new proximity can be set for a beacon 158 | */ 159 | - (BOOL)canSetProximity:(CLProximity)newProximity; 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLBeaconControl-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLBeaconControl-Bridging-Header.h 3 | // Pods 4 | // 5 | // Created by Adrian Chojnacki on 14/12/16. 6 | // 7 | // 8 | 9 | #ifndef BCLBeaconControl_Bridging_Header_h 10 | #define BCLBeaconControl_Bridging_Header_h 11 | 12 | #import "BCLBeaconCtrl.h" 13 | 14 | #endif /* BCLBeaconControl_Bridging_Header_h */ 15 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLBeaconCtrl.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLBeaconCtrl.h 3 | // 4 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 5 | // All rights reserved. 6 | // 7 | // This source code is licensed under the BSD 3-Clause License found in the 8 | // LICENSE.txt file in the root directory of this source tree. 9 | // 10 | 11 | #import 12 | #import "BCLAction.h" 13 | #import "BCLConfiguration.h" 14 | #import "BCLBeaconCtrlDelegate.h" 15 | #import "BCLEncodableObject.h" 16 | #import "BCLBeacon.h" 17 | 18 | extern NSInteger const BCLInvalidParametersErrorCode; 19 | extern NSInteger const BCLInvalidDataErrorCode; 20 | extern NSInteger const BCLErrorHTTPError; 21 | extern NSInteger const BCLInvalidDeviceConfigurationError; 22 | extern NSString * const BCLBluetoothNotTurnedOnErrorKey; 23 | extern NSString * const BCLDeniedMonitoringErrorKey; 24 | extern NSString * const BCLDeniedLocationServicesErrorKey; 25 | extern NSString * const BCLDeniedBackgroundAppRefreshErrorKey; 26 | extern NSString * const BCLDeniedNotificationsErrorKey; 27 | extern NSString * const BCLErrorDomain; 28 | 29 | @protocol BCLExtension; 30 | 31 | /*! 32 | * @typedef BCLBeaconCtrlPushEnvironment 33 | * @brief A list of possible push environments 34 | * @constant BCLBeaconCtrlPushEnvironmentNone is for application builds that don't have any push environment set up 35 | * @constant BCLBeaconCtrlPushEnvironmentSandbox is for debug builds of applications with set up sandbox push environments 36 | * @constant BCLBeaconCtrlPushEnvironmentProduction is for production builds of applications with set up production push environments 37 | */ 38 | typedef NS_ENUM(NSUInteger, BCLBeaconCtrlPushEnvironment) { 39 | BCLBeaconCtrlPushEnvironmentNone, 40 | BCLBeaconCtrlPushEnvironmentSandbox, 41 | BCLBeaconCtrlPushEnvironmentProduction 42 | }; 43 | 44 | /*! 45 | * A BCLBeaconCtrl singleton is the main point of interaction with BeaconCtrl Client API 46 | */ 47 | @interface BCLBeaconCtrl : BCLEncodableObject 48 | 49 | /** @name Properties */ 50 | 51 | /// A reference to beacons', zones' and actions' configuration fetched from the backend 52 | @property (nonatomic, strong) BCLConfiguration *configuration; 53 | 54 | /// Processing status. YES if processing is paused. 55 | @property (assign) BOOL paused; 56 | 57 | /// client id obtained from the admin panel for authentication 58 | @property (copy, nonatomic, readonly) NSString *clientId; 59 | 60 | /// client secret obtained from the admin panel for authentication 61 | @property (copy, nonatomic, readonly) NSString *clientSecret; 62 | 63 | /// an arbitrary string identifier for an app user. given as a parameter during the setup 64 | @property (copy, nonatomic, readonly) NSString *userId; 65 | 66 | /// A set of beacons that are currently monitored by the SDK 67 | @property (nonatomic, copy, readonly) NSSet *observedBeacons; 68 | 69 | /// a weak reference to the delegate 70 | @property (weak) id delegate; 71 | 72 | /** @name Methods */ 73 | 74 | /*! 75 | * @brief Check, if bluetooth is turned on 76 | * @return YES, if bluetooth is turned on 77 | */ 78 | - (BOOL)isBluetoothTurnedOn; 79 | 80 | /*! 81 | * @brief Check, if the current device supports monitoring beacons 82 | * @return YES, if the current device supports monitoring beacons 83 | */ 84 | - (BOOL)isBeaconMonitoringAvailable; 85 | 86 | /*! 87 | * @brief Check, if the location services are turned on for the current app 88 | * @return Yes, if the location services are turned on for the current app 89 | */ 90 | - (BOOL)isLocationServicesAvailable; 91 | 92 | /*! 93 | * @brief Check, if background app refresh is turned on for the current app 94 | * @return Yes, if background app refresh is turned on for the current app 95 | */ 96 | - (BOOL)isBackgroundAppRefreshAvailable; 97 | 98 | /*! 99 | * @brief Check, if notifications are turned on for the current app 100 | * @return Yes, if notifications are turned on for the current app 101 | */ 102 | - (BOOL)isNotificationsAvailable; 103 | 104 | /*! 105 | * @brief Check, if the current device and the running app are capable of processing beacon actions 106 | * @param error A pointer to an NSError object that will be populated with error info, if processing beacon actions is not possible 107 | * @return YES, if the current device and the running app are capable of processing beacon actions 108 | */ 109 | - (BOOL)isBeaconCtrlReadyToProcessBeaconActions:(NSError **)error; 110 | 111 | /*! 112 | * @brief Start responding to beacon events 113 | * @return YES, if BecaonOS has successfully started monitoring, NO otherwise 114 | */ 115 | - (BOOL) startMonitoringBeacons; 116 | 117 | /*! 118 | * @brief Stop responding to beacon events 119 | */ 120 | - (void) stopMonitoringBeacons; 121 | 122 | /*! 123 | * @brief The main method that determines which beacons should currently be monitored, basing on the estimated location of the device 124 | * @return YES, if BecaonCtrl has successfully updated monitored beacons, NO otherwise 125 | */ 126 | - (BOOL)updateMonitoredBeacons; 127 | 128 | /*! 129 | * @return The zone that the user's device is currently in 130 | */ 131 | - (BCLZone *)currentZone; 132 | 133 | /*! 134 | * @brief Recalculates the current zone basing on the signal strengths of visible beacons 135 | */ 136 | - (void)recheckCurrentZone; 137 | 138 | /*! 139 | * @return The beacon that is closest to the user's device 140 | */ 141 | - (BCLBeacon *)closestBeacon; 142 | 143 | /*! 144 | @return The beacon whose range was last entered by the user's device 145 | */ 146 | - (BCLBeacon *)lastEnteredBeacon; 147 | 148 | /*! 149 | * @return An array of beacons sorted ascendingly by distance from the user's device 150 | */ 151 | - (NSArray *)beaconsSortedByDistance; 152 | 153 | /*! 154 | * @brief the main setup method for the SDK 155 | * @param clientId Client id obtained from the admin panel for authentication 156 | * @param clientSecret Client secret obtained from the admin panel for authentication 157 | * @param userId An arbitrary string identifier for an app user. given as a parameter during the setup 158 | * @param pushEnvironment Push environment that should be used in the current build as defined in "BCLBeaconCtrlPushEnvironment" NS_ENUM 159 | * @param pushToken Devices push token retrieved from the APNS or nil, if the push environment is BCLBeaconCtrlPushEnvironmentNone 160 | * @param completion The completion handler called after the setup is finished 161 | */ 162 | + (void)setupBeaconCtrlWithClientId:(NSString *)clientId clientSecret:(NSString *)clientSecret userId:(NSString *)userId pushEnvironment:(BCLBeaconCtrlPushEnvironment)pushEnvironment pushToken:(NSString *)pushToken completion:(void (^)(BCLBeaconCtrl *beaconCtrl, BOOL isRestoredFromCache, NSError *error))completion; 163 | 164 | /*! 165 | * @return A BCLBeaconCtrl instance restored from the device's cache 166 | */ 167 | + (BCLBeaconCtrl *) beaconCtrlRestoredFromCache; 168 | 169 | /*! 170 | @brief Stored the receiver in the device's cache 171 | @discussion Should be called when the application is terminated, so that all the beacon and/or zone actions are correctly handled when the application is launched after some beacon event has occured 172 | */ 173 | - (BOOL) storeInCache; 174 | 175 | /*! 176 | @brief Deletes the currently stored BCLBeaconCtrl instance from the device's cache 177 | */ 178 | + (void) deleteBeaconCtrlFromCache; 179 | 180 | /*! 181 | * @brief A method for handling push and local notifications fired by BeaconCtrl 182 | * @discussion BeaconCtrl backend sends some push notifications to the SDK in order to fire some admin-defined beacon and zone actions; BeaconCtrl SDK fires some local notifications for the same purpose. 183 | */ 184 | - (BOOL)handleNotification:(NSDictionary *)userInfo error:(NSError **)error; 185 | 186 | /*! 187 | * @brief Fetches the latest configuration from the backend 188 | * @param completion The completion handler that is fired after the fetch is finished 189 | */ 190 | - (void)fetchConfiguration:(void(^)(NSError *error))completion; 191 | 192 | /*! 193 | * @brief Fetches users who are in range of given beacons or zones 194 | * @discussion Available when the Presence Add-on is activated for the application in the Admin Panel 195 | * @param beacons A set of beacons that an SDK user wants to query for users being in their range 196 | * @param zones A set of zone that an SDK user wants to query for users being in their range 197 | * @param completion The completion handler that is fired after the fetch is finished 198 | */ 199 | - (void)fetchUsersInRangesOfBeacons:(NSSet *)beacons zones:(NSSet *)zones completion:(void (^)(NSDictionary *result, NSError *error))completion; 200 | 201 | /*! 202 | * @brief Logs out the current SDK client 203 | */ 204 | - (void)logout; 205 | 206 | @end 207 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLBeaconCtrlAdmin.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLBeaconCtrlAdmin.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "BCLTypes.h" 14 | 15 | @class BCLBeacon; 16 | @class BCLZone; 17 | 18 | /*! 19 | * A BCLBeaconCtrlAdmin singleton is the main point of interaction with BeaconCtrl S2S API 20 | */ 21 | @interface BCLBeaconCtrlAdmin : NSObject 22 | 23 | /** @name Properties */ 24 | 25 | /// Beacons fetched from the backend 26 | @property(nonatomic, strong) NSSet *beacons; 27 | 28 | // Zones fetched from the backend 29 | @property(nonatomic, strong) NSSet *zones; 30 | 31 | // Available zone colors fetched from the backend 32 | @property(nonatomic, strong) NSArray *zoneColors; 33 | 34 | 35 | /** @name Methods */ 36 | 37 | /*! 38 | * @brief The main setup method for BCLBeaconCtrlAdmin 39 | * @param clientId A client id obtained from the s2s API provider, used for authentication 40 | * @param clientSecret A client secret obtained from the s2s API provider, used for authentication 41 | * @return An initialized instance of BCLBeaconCtrlAdmin 42 | */ 43 | + (instancetype)beaconCtrlAdminWithCliendId:(NSString *)clientId clientSecret:(NSString *)clientSecret; 44 | 45 | /*! 46 | * @brief Fethes beacons and zones from the backend 47 | * @param completion A completion handler that is called after the fetch is finished 48 | */ 49 | - (void)fetchZonesAndBeacons:(void (^)(NSError *error))completion; 50 | 51 | /*! 52 | * @brief Syncs a given beacon with its state on the backend 53 | * @param beacon A beacon to sync 54 | * @param completion The completion handler that is fires after the sync is finished 55 | */ 56 | - (void)syncBeacon:(BCLBeacon *)beacon completion:(void (^)(NSError *error))completion; 57 | 58 | /*! 59 | * @brief Fetch available zone colors from the backend 60 | * @discussion Each zone can have a color that is one the colors stored in the zoneColors property that is populated once this method is successfully called 61 | * @param completion A completion handler that is called after the fetch is finished 62 | */ 63 | - (void)fetchZoneColors:(void (^)(NSError *error))completion; 64 | 65 | /*! 66 | * @brief Registers a new admin user on the backend 67 | * @param email The new user's email 68 | * @param password The new user's password 69 | * @param passwordConfirmation The new user's password confirmation for verification 70 | * @param completion A completion block that is called after the registration is finished 71 | */ 72 | - (void)registerAdminUserWithEmail:(NSString *)email password:(NSString *)password passwordConfirmation:(NSString *)passwordConfirmation completion:(void (^)(BOOL success, NSError *error))completion; 73 | 74 | /*! 75 | * @brief Authenticates an existing admin user against the backend 76 | * @param email Admin user's email 77 | * @param password Admin user's password 78 | * @param completion A completion block that is called after the authentication is finished 79 | */ 80 | - (void)loginAdminUserWithEmail:(NSString *)email password:(NSString *)password completion:(void (^)(BOOL success, NSError *error))completion; 81 | 82 | /*! 83 | * @brief Fethes a cliend id and client secret for the currently logged in admin user's test application 84 | * @discussion Every admin user has exactly one test application that monitors all the beacons and zones added to the admin user's account 85 | * @param completion A completion block that is called after the fetch is finished 86 | */ 87 | - (void)fetchTestApplicationCredentials:(void (^)(NSString *applicationClientId, NSString *applicationClientSecret, NSError *error))completion; 88 | 89 | /*! 90 | * @brief Creates a beacon on the backend 91 | * @discussion When creating a beacon, an admin SDK user can create the first test action. It will be added as a custom action with given attributes on the backend. 92 | * @param beacon A beacon that will be created 93 | * @param testActionName Name of the test action 94 | * @param testActionTrigger Trigger of the test action 95 | * @param testActionAttributes Attributes of the test action 96 | * @param completion A completion handler that will be called after the creation is finished 97 | */ 98 | - (void)createBeacon:(BCLBeacon *)beacon testActionName:(NSString *)testActionName testActionTrigger:(BCLEventType)trigger testActionAttributes:(NSArray *)testActionAttributes completion:(void (^)(BCLBeacon *, NSError *))completion; 99 | 100 | /*! 101 | * @brief Updates a beacon on the backend 102 | * @discussion When updating a beacon, an admin SDK user can create or update its test action. Any test action is added as a custom action with given attributes on the backend. 103 | * @param beacon A beacon that will be updated 104 | * @param testActionName Name of the test action 105 | * @param testActionTrigger Trigger of the test action 106 | * @param testActionAttributes Attributes of the test action 107 | * @param completion A completion handler that will be called after the update is finished 108 | */ 109 | - (void)updateBeacon:(BCLBeacon *)beacon testActionName:(NSString *)testActionName testActionTrigger:(BCLEventType)trigger testActionAttributes:(NSArray *)testActionAttributes completion:(void (^)(BOOL success, NSError *error))completion; 110 | 111 | /*! 112 | * @brief Deletes a beacon on the backend 113 | * @param beacon A beacon that will be deleted 114 | * @param completion A completion handler that will be called after the deletion is finished 115 | */ 116 | - (void)deleteBeacon:(BCLBeacon *)beacon completion:(void (^)(BOOL success, NSError *error))completion; 117 | 118 | /*! 119 | * @brief Creates a zone on the backend 120 | * @param zone A zone that will be created 121 | * @param completion A completion handler that will be called after the creation is finished 122 | */ 123 | - (void)createZone:(BCLZone *)zone completion:(void (^)(BCLZone *newZone, NSError *error))completion; 124 | 125 | /*! 126 | * @brief Updates a zone on the backend 127 | * @param zone A zone that will be updated 128 | * @param completion A completion handler that will be called after the update is finished 129 | */ 130 | - (void)updateZone:(BCLZone *)zone completion:(void (^)(BOOL success, NSError *error))completion; 131 | 132 | /*! 133 | * @brief Deletes a zone on the backend 134 | * @param zone A zone that will be deleted 135 | * @param completion A completion handler that will be called after the deletion is finished 136 | */ 137 | - (void)deleteZone:(BCLZone *)zone completion:(void (^)(BOOL success, NSError *error))completion; 138 | 139 | /*! 140 | * @brief Fetches an array of available vendors 141 | * @param completion A completion handler that will be called after the fetch is complete 142 | */ 143 | - (void)fetchVendors:(void (^)(NSArray *vendors, NSError *error))completion; 144 | 145 | - (void)logout; 146 | 147 | @end 148 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLBeaconCtrlAdmin.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLBeaconCtrlAdmin.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLBeaconCtrlAdmin.h" 13 | #import "BCLAdminBackend.h" 14 | 15 | @interface BCLBeaconCtrlAdmin () 16 | 17 | @property (nonatomic, copy) NSString *clientId; 18 | @property (nonatomic, copy) NSString *clientSecret; 19 | 20 | @property (nonatomic, strong) BCLAdminBackend *backend; 21 | 22 | @end 23 | 24 | @implementation BCLBeaconCtrlAdmin 25 | 26 | + (instancetype)beaconCtrlAdminWithCliendId:(NSString *)clientId clientSecret:(NSString *)clientSecret 27 | { 28 | BCLBeaconCtrlAdmin *beaconCtrlAdmin = [self new]; 29 | 30 | beaconCtrlAdmin.clientId = clientId; 31 | beaconCtrlAdmin.clientSecret = clientSecret; 32 | 33 | beaconCtrlAdmin.backend = [[BCLAdminBackend alloc] initWithClientId:clientId clientSecret:clientSecret]; 34 | 35 | return beaconCtrlAdmin; 36 | } 37 | 38 | - (void)fetchZonesAndBeacons:(void(^)(NSError *error))completion 39 | { 40 | __weak BCLBeaconCtrlAdmin *weakSelf = self; 41 | [self.backend fetchBeacons:^(NSSet *beacons, NSError *error) { 42 | if (!error) { 43 | weakSelf.beacons = beacons; 44 | [weakSelf.backend fetchZones:beacons completion:^(NSSet *zones, NSError *error) { 45 | if (!error) { 46 | weakSelf.zones = zones; 47 | } 48 | if (completion) completion(error); 49 | }]; 50 | } else { 51 | if (completion) completion(error); 52 | } 53 | }]; 54 | } 55 | 56 | - (void)syncBeacon:(BCLBeacon *)beacon completion:(void (^)(NSError *))completion 57 | { 58 | [self.backend syncBeacon:beacon completion:completion]; 59 | } 60 | 61 | - (void)fetchZoneColors:(void (^)(NSError *))completion 62 | { 63 | __weak BCLBeaconCtrlAdmin *weakSelf = self; 64 | 65 | [self.backend fetchZoneColors:^(NSArray *zoneColors, NSError *error) { 66 | if (!error) { 67 | weakSelf.zoneColors = zoneColors; 68 | if (completion) completion(error); 69 | } else { 70 | if (completion) completion(error); 71 | } 72 | }]; 73 | } 74 | 75 | - (void)fetchVendors:(void (^)(NSArray *vendors, NSError *error))completion 76 | { 77 | [self.backend fetchVendors:completion]; 78 | } 79 | 80 | - (void)loginAdminUserWithEmail:(NSString *)email password:(NSString *)password completion:(void (^)(BOOL, NSError *))completion 81 | { 82 | [self.backend authenticateUserWithEmail:email password:password completion:completion]; 83 | } 84 | 85 | - (void)registerAdminUserWithEmail:(NSString *)email password:(NSString *)password passwordConfirmation:(NSString *)passwordConfirmation completion:(void (^)(BOOL, NSError *))completion 86 | { 87 | [self.backend registerNewUserWithEmail:email password:password passwordConfirmation:passwordConfirmation completion:completion]; 88 | } 89 | 90 | - (void)fetchTestApplicationCredentials:(void (^)(NSString *, NSString *, NSError *))completion 91 | { 92 | [self.backend fetchTestApplicationCredentials:completion]; 93 | } 94 | 95 | - (void)createBeacon:(BCLBeacon *)beacon testActionName:(NSString *)testActionName testActionTrigger:(BCLEventType)trigger testActionAttributes:(NSArray *)testActionAttributes completion:(void (^)(BCLBeacon *, NSError *))completion 96 | { 97 | [self.backend createBeacon:beacon testActionName:testActionName testActionTrigger:trigger testActionAttributes:testActionAttributes completion:completion]; 98 | } 99 | 100 | - (void)updateBeacon:(BCLBeacon *)beacon testActionName:(NSString *)testActionName testActionTrigger:(BCLEventType)trigger testActionAttributes:(NSArray *)testActionAttributes completion:(void (^)(BOOL, NSError *))completion 101 | { 102 | [self.backend updateBeacon:beacon testActionName:testActionName testActionTrigger:trigger testActionAttributes:testActionAttributes completion:completion]; 103 | } 104 | 105 | - (void)deleteBeacon:(BCLBeacon *)beacon completion:(void (^)(BOOL, NSError *))completion 106 | { 107 | [self.backend deleteBeacon:beacon completion:completion]; 108 | } 109 | 110 | - (void)createZone:(BCLZone *)zone completion:(void (^)(BCLZone *, NSError *))completion 111 | { 112 | [self.backend createZone:zone completion:completion]; 113 | } 114 | 115 | - (void)updateZone:(BCLZone *)zone completion:(void (^)(BOOL, NSError *))completion 116 | { 117 | [self.backend updateZone:zone completion:completion]; 118 | } 119 | 120 | - (void)deleteZone:(BCLZone *)zone completion:(void (^)(BOOL, NSError *))completion 121 | { 122 | [self.backend deleteZone:zone completion:completion]; 123 | } 124 | 125 | - (void)logout 126 | { 127 | [self.backend reset]; 128 | } 129 | 130 | @end 131 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLBeaconCtrlDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLBeaconCtrlDelegate.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | @class BCLZone; 15 | @class BCLBeacon; 16 | 17 | /*! 18 | * BCLBeaconCtrl delegate. Gets notified about BeaconCtrl SDK actions 19 | */ 20 | @protocol BCLBeaconCtrlDelegate 21 | 22 | @optional 23 | 24 | /** @name Methods */ 25 | 26 | /*! 27 | * @brief Called when a beacon or zone action is performed in background-mode 28 | * 29 | * @param action An action that will be called 30 | */ 31 | - (void) notifyAction:(BCLAction *)action; 32 | 33 | /*! 34 | * @brief Called to ask a delegate if a local notificiation for the BeaconCtrl action should be presented automatically by the SDK 35 | * 36 | * @discussion When a BeaconCtrl action is performed in background-mode, a local notification is created automatically using the action's name. You can use this method to prevent this default behavior. 37 | * 38 | * @param action An action that would normally be notified in case of background-mode by default by the SDK 39 | * 40 | * @return YES, if the action should be notified automatically 41 | */ 42 | - (BOOL) shouldAutomaticallyNotifyAction:(BCLAction *)action; 43 | 44 | /*! 45 | * @brief Called just before a beacon or zone action is performed 46 | * 47 | * @param action An action that will be called 48 | */ 49 | - (void) willPerformAction:(BCLAction *)action; 50 | 51 | /*! 52 | * @brief Called just after a beacon or zone action was performed 53 | * 54 | * @param action An action that was called 55 | */ 56 | - (void) didPerformAction:(BCLAction *)action; 57 | 58 | /*! 59 | * @brief Called to ask a delegate if a BeaconCtrl action should be handled automatically by the SDK 60 | * 61 | * @discussion Some BeaconCtrl actions, e.g. URL actions, are handled automatically by the SDK by default (in case of URL actions a modal web view is shown). You can use this method to prevent this default behavior. 62 | * 63 | * @param action An action that would normally be handled by default by the SDK 64 | * 65 | * @return YES, if the action should be handled automatically 66 | */ 67 | - (BOOL) shouldAutomaticallyPerformAction:(BCLAction *)action; 68 | 69 | 70 | /*! 71 | * @brief Called each time a set of beacons currently monitored by the SDK has changed 72 | * 73 | * @discussion The SDK monitors a limited set of beacons at any given time and changes this set basing on the device's position and beacons currently in range. This way, the SDK gets through the iOS limitation for 20 monitored beacons at a time. 74 | * 75 | * @param newObservedBeacons A set of new monitored beacons. 76 | */ 77 | - (void) didChangeObservedBeacons:(NSSet *)newObservedBeacons; 78 | 79 | /*! 80 | * @brief Called each time the closest beacon has changed. 81 | * 82 | * @param closestBeacon A beacons that is currently the closest to the device 83 | */ 84 | - (void) closestObservedBeaconDidChange:(BCLBeacon *)closestBeacon; 85 | 86 | /*! 87 | * @brief Called each time the current zone has changed 88 | * 89 | * @param currentZone The beacon zone in which the device is at a given time. It's calculated by looking at beacons in range and their estimated distances from the device 90 | */ 91 | - (void) currentZoneDidChange:(BCLZone *)currentZone; 92 | 93 | - (void) beaconsPropertiesUpdateDidStart:(BCLBeacon *)beacon; 94 | 95 | - (void) beaconsPropertiesUpdateDidFinish:(BCLBeacon *)beacon success:(BOOL)success; 96 | 97 | - (void) beaconsFirmwareUpdateDidStart:(BCLBeacon *)beacon; 98 | 99 | - (void) beaconsFirmwareUpdateDidProgress:(BCLBeacon *)beacon progress:(NSUInteger)progress; 100 | 101 | - (void) beaconsFirmwareUpdateDidFinish:(BCLBeacon *)beacon success:(BOOL)success; 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLBeaconRangingBatch.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD 3-Clause License found in the 6 | // LICENSE.txt file in the root directory of this source tree. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class BCLBeaconRangingBatch; 13 | 14 | @protocol BCLBeaconRangingBatchDelegate 15 | 16 | - (void) processBeaconBatch:(BCLBeaconRangingBatch *)batch beacons:(NSArray *)rangedBeacons; 17 | @end 18 | 19 | @interface BCLBeaconRangingBatch : NSObject 20 | 21 | @property (strong) NSMutableDictionary *batch; 22 | 23 | @property (strong, readonly) NSArray *regions; 24 | 25 | @property (weak) id delegate; 26 | 27 | - (instancetype) initWithDelegate:(id )delegate; 28 | 29 | - (void) add:(NSArray *)rangedBeacons forRegion:(CLBeaconRegion *)region; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLBeaconRangingBatch.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD 3-Clause License found in the 6 | // LICENSE.txt file in the root directory of this source tree. 7 | // 8 | 9 | #import "BCLBeaconRangingBatch.h" 10 | 11 | #define BCLRangingSecondsTimeFrame 1 12 | 13 | // timeout value since last read. After that amount of time batch is cheared out 14 | #define BCLRangingSecondsTimeout 120 15 | 16 | static NSDate *lastRanging; 17 | 18 | @implementation BCLBeaconRangingBatch 19 | 20 | - (instancetype) initWithDelegate:(id )delegate 21 | { 22 | if (self = [self init]) { 23 | self.delegate = delegate; 24 | } 25 | return self; 26 | } 27 | 28 | 29 | - (void) add:(NSArray *)rangedBeacons forRegion:(CLBeaconRegion *)region 30 | { 31 | @synchronized(self) { 32 | if (!self.batch) { 33 | self.batch = [NSMutableDictionary dictionary]; 34 | [self resetForRegion:region]; 35 | } 36 | 37 | NSTimeInterval timeIntervalSinceLastRanging = [[NSDate date] timeIntervalSinceDate:lastRanging ?: [NSDate dateWithTimeIntervalSinceReferenceDate:0]]; 38 | if (timeIntervalSinceLastRanging >= BCLRangingSecondsTimeout) { 39 | [self resetForRegion:region]; 40 | } 41 | 42 | lastRanging = [NSDate date]; 43 | 44 | NSArray *beaconsInBatch = self.batch[region.identifier][@"beacons"]; 45 | 46 | if (beaconsInBatch.count > 0) { 47 | // if time elapsed from the last read is significant I assume that there was 48 | // break and batch is processed as new 49 | NSDate *refDate = self.batch[region.identifier][@"refdate"]; 50 | NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:refDate]; 51 | 52 | // reset batch after BCLRangingSecondsTimeout 53 | if (timeInterval >= BCLRangingSecondsTimeout) { 54 | [self resetForRegion:region]; 55 | beaconsInBatch = self.batch[region.identifier][@"beacons"]; 56 | } 57 | 58 | // expand 59 | beaconsInBatch = [beaconsInBatch arrayByAddingObjectsFromArray:rangedBeacons]; 60 | self.batch[region.identifier] = @{@"refdate": refDate, @"beacons": beaconsInBatch}; 61 | 62 | if (timeInterval >= BCLRangingSecondsTimeFrame) { 63 | id delegateStrong = self.delegate; 64 | if ([delegateStrong conformsToProtocol:@protocol(BCLBeaconRangingBatchDelegate)]) { 65 | [delegateStrong processBeaconBatch:self beacons:beaconsInBatch]; 66 | } 67 | [self resetForRegion:region]; 68 | } 69 | } else { 70 | // init with ranged beacons 71 | self.batch[region.identifier] = @{@"refdate": [NSDate date], @"beacons": rangedBeacons}; 72 | id delegateStrong = self.delegate; 73 | if ([delegateStrong conformsToProtocol:@protocol(BCLBeaconRangingBatchDelegate)]) { 74 | [delegateStrong processBeaconBatch:self beacons:rangedBeacons]; 75 | } 76 | [self resetForRegion:region]; 77 | } 78 | } 79 | } 80 | 81 | - (NSArray *) regions 82 | { 83 | @synchronized(self) { 84 | return [self.batch allKeys]; 85 | } 86 | } 87 | 88 | - (void) resetForRegion:(CLBeaconRegion *)region 89 | { 90 | self.batch[region.identifier] = @{@"refdate": [NSDate date], @"beacons": [NSArray array]}; 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLCondition.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLCondition.h 3 | // SBB 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "BCLTypes.h" 14 | 15 | @class BCLBeacon; 16 | @class BCLZone; 17 | 18 | @protocol BCLCondition 19 | 20 | @required 21 | 22 | + (NSString *) bcl_conditionType; 23 | - (instancetype) initWithParameters:(NSDictionary *)parameters; 24 | 25 | - (BOOL) evaluateCondition:(BCLEventType)eventType forBeacon:(BCLBeacon *)beacon; 26 | - (BOOL)evaluateCondition:(BCLEventType)eventType forZone:(BCLZone *)zone; 27 | 28 | @end -------------------------------------------------------------------------------- /BeaconCtrl/BCLConfiguration.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLConfiguration.h 3 | // 4 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 5 | // All rights reserved. 6 | // 7 | // This source code is licensed under the BSD 3-Clause License found in the 8 | // LICENSE.txt file in the root directory of this source tree. 9 | // 10 | 11 | #import 12 | #import "BCLExtension.h" 13 | #import "BCLEncodableObject.h" 14 | 15 | /*! 16 | * A BCLConfiguration object represents a beacon, zone, actions and extensions configuration relevant for a given BeaconCtrl Application, fetched from the BeaconCtrl Client API and is kept as a reference in 17 | * the BCLBeaconCtrl singleton 18 | */ 19 | @interface BCLConfiguration : BCLEncodableObject 20 | 21 | /** @name Properties */ 22 | 23 | /// Fetched extensions. Set of initialized instances of objects. 24 | @property (strong, nonatomic, readonly) NSSet *extensions; 25 | 26 | /// Fetched beacons. A set of CTLBeacon objects 27 | @property (strong, nonatomic, readonly) NSSet *beacons; 28 | 29 | /// Fetched zones. A set of CTLZone objects 30 | @property (strong, nonatomic, readonly) NSSet *zones; 31 | 32 | /// Kontakt.io API key or nil if the kontakt.io add-on is not switched on 33 | @property (nonatomic, copy, readonly) NSString *kontaktIOAPIKey; 34 | 35 | /** @name Methods */ 36 | 37 | /*! 38 | * @brief inits a BCLConfiguration object with jsonData fetched from the backend 39 | * @param jsonData An NSData object that contains json with a configuration representation fetched from the backend 40 | */ 41 | - (instancetype) initWithJSON:(NSData *)jsonData; 42 | 43 | /*! 44 | * @brief Finds a class with a given name (found by calling a given selector on a class) and protocol 45 | * @param name A name of the class to find 46 | * @param protocol A protocol that the class needs to conform to 47 | * @param nameSelector A selector that will be called on a class to get its name 48 | */ 49 | + (Class) classForName:(NSString *)name protocol:(Protocol *)protocol selector:(SEL)nameSelector; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLConfiguration.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLConfiguration.m 3 | // 4 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 5 | // All rights reserved. 6 | // 7 | // This source code is licensed under the BSD 3-Clause License found in the 8 | // LICENSE.txt file in the root directory of this source tree. 9 | // 10 | 11 | #import "BCLConfiguration.h" 12 | #import "BCLBeacon.h" 13 | #import "BCLZone.h" 14 | #import "BCLTrigger.h" 15 | 16 | #import 17 | 18 | @interface BCLConfiguration () 19 | @property (strong, nonatomic, readwrite) NSSet *extensions; 20 | @property (strong, nonatomic, readwrite) NSSet *beacons; 21 | @property (strong, nonatomic, readwrite) NSSet *zones; 22 | @property (copy, nonatomic, readwrite) NSString *kontaktIOAPIKey; 23 | @end 24 | 25 | @implementation BCLConfiguration 26 | 27 | - (instancetype) init 28 | { 29 | if (self = [super init]) { 30 | self.beacons = [NSSet set]; 31 | } 32 | return self; 33 | } 34 | 35 | - (instancetype) initWithJSON:(NSData *)jsonData 36 | { 37 | if (self = [self init]) { 38 | [self loadFromJSON:jsonData]; 39 | } 40 | return self; 41 | } 42 | 43 | - (NSSet *)extensions 44 | { 45 | if (!_extensions) { 46 | _extensions = (NSSet *)[NSSet set]; 47 | } 48 | return _extensions; 49 | } 50 | 51 | - (BOOL) loadFromJSON:(NSData *)jsonData 52 | { 53 | // load json and translato to BeaconCtrl format 54 | NSError *error = nil; 55 | NSDictionary *configurationDictionary = nil; 56 | if (jsonData) { 57 | configurationDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; 58 | } 59 | if (error || !configurationDictionary) 60 | return NO; 61 | 62 | // Load and initialize extension classess 63 | NSDictionary *extensionsDictionary = configurationDictionary[@"extensions"]; 64 | if (extensionsDictionary) { 65 | for (NSString *extensionKey in extensionsDictionary.allKeys) { 66 | Class extensionClass = [BCLConfiguration classForName:extensionKey protocol:@protocol(BCLExtension) selector:@selector(bcl_extensionName)]; 67 | if (extensionClass) { 68 | id extensionImpl = [[extensionClass alloc] initWithParameters:extensionsDictionary[extensionKey]]; 69 | self.extensions = (NSSet *)[self.extensions setByAddingObject:extensionImpl]; 70 | } 71 | } 72 | } 73 | 74 | // Load beacons 75 | NSMutableSet *beaconsSet = [NSMutableSet set]; 76 | 77 | for (NSDictionary *beaconDictionary in configurationDictionary[@"ranges"]) { 78 | BCLBeacon *beacon = [[BCLBeacon alloc] init]; 79 | [beacon updatePropertiesFromDictionary:beaconDictionary]; 80 | [beaconsSet addObject:beacon]; 81 | } 82 | 83 | // load zones 84 | NSMutableSet *zonesSet = [NSMutableSet set]; 85 | 86 | for (NSDictionary *zoneDictionary in configurationDictionary[@"zones"]) { 87 | BCLZone *zone = [[BCLZone alloc] init]; 88 | [zone updatePropertiesFromDictionary:zoneDictionary beacons:beaconsSet]; 89 | [zonesSet addObject:zone]; 90 | } 91 | 92 | // load triggers 93 | for (NSDictionary *triggerDictionary in configurationDictionary[@"triggers"]) { 94 | [triggerDictionary[@"range_ids"] enumerateObjectsUsingBlock:^(NSNumber *beaconId, NSUInteger idx, BOOL *stop) { 95 | BCLTrigger *trigger = [[BCLTrigger alloc] init]; 96 | 97 | if ([beaconId isEqual:@236]) { 98 | NSLog(@""); 99 | } 100 | 101 | NSSet *beaconSet = [beaconsSet filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"beaconIdentifier == %@", beaconId.description]]; 102 | for (BCLBeacon *beacon in beaconSet) { 103 | trigger.beacon = beacon; //FIXME: fix strong cross reference 104 | [trigger updatePropertiesFromDictionary:triggerDictionary]; 105 | beacon.triggers = [beacon.triggers arrayByAddingObject:trigger]; 106 | } 107 | }]; 108 | 109 | [triggerDictionary[@"zone_ids"] enumerateObjectsUsingBlock:^(NSNumber *zoneId, NSUInteger idx, BOOL *stop) { 110 | BCLTrigger *trigger = [[BCLTrigger alloc] init]; 111 | 112 | NSSet *zoneSet = [zonesSet filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"zoneIdentifier == %@", zoneId.description]]; 113 | for (BCLZone *zone in zoneSet) { 114 | trigger.zone = zone; //FIXME: fix strong cross reference 115 | [trigger updatePropertiesFromDictionary:triggerDictionary]; 116 | zone.triggers = [zone.triggers arrayByAddingObject:trigger]; 117 | } 118 | }]; 119 | } 120 | 121 | 122 | 123 | self.beacons = [beaconsSet copy]; 124 | self.zones = [zonesSet copy]; 125 | 126 | if (configurationDictionary[@"kontakt_api_key"] != [NSNull null] && ![configurationDictionary[@"kontakt_api_key"] isEqualToString:@""]) { 127 | self.kontaktIOAPIKey = configurationDictionary[@"kontakt_api_key"]; 128 | } 129 | 130 | return YES; 131 | } 132 | 133 | + (Class) classForName:(NSString *)name protocol:(Protocol *)protocol selector:(SEL)nameSelector 134 | { 135 | int numberOfClasses = objc_getClassList(NULL, 0); 136 | Class classList[numberOfClasses]; 137 | numberOfClasses = objc_getClassList(classList, numberOfClasses); 138 | 139 | for (int idx = 0; idx < numberOfClasses; idx++) 140 | { 141 | Class class = classList[idx]; 142 | if (class_getClassMethod(class, @selector(conformsToProtocol:)) && [class conformsToProtocol:protocol]) 143 | { 144 | #ifdef DEBUG 145 | NSLog(@"Found class %@ (%@)", NSStringFromClass(class), NSStringFromProtocol(protocol)); 146 | #endif 147 | Class extensionClass = class; 148 | if ([class respondsToSelector:nameSelector]) { 149 | NSString *identifier = [class performSelector:nameSelector]; 150 | if ([identifier isEqualToString:name]) { 151 | return extensionClass; 152 | } 153 | } 154 | } 155 | } 156 | return nil; 157 | } 158 | 159 | @end 160 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLEncodableObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLEncodableObject.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "UNCoding.h" 14 | 15 | @interface BCLEncodableObject : NSObject 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLEncodableObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLEncodableObject.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLEncodableObject.h" 13 | #import "UNCodingUtil.h" 14 | 15 | @implementation BCLEncodableObject 16 | 17 | #pragma mark - NSSecureCoding 18 | 19 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 20 | { 21 | if (!self) { 22 | return nil; 23 | } 24 | 25 | [UNCodingUtil decodeObject:self withCoder:aDecoder]; 26 | 27 | return self; 28 | } 29 | 30 | - (void)encodeWithCoder:(NSCoder *)aCoder 31 | { 32 | [UNCodingUtil encodeObject:self withCoder:aCoder]; 33 | } 34 | 35 | + (BOOL)supportsSecureCoding 36 | { 37 | return YES; 38 | } 39 | 40 | #pragma mark UNCoding 41 | 42 | - (instancetype) initWithDictionary:(NSDictionary *)dictionary 43 | { 44 | if (self = [super init]) { 45 | [[[UNCodingUtil alloc] initWithObject:self] loadDictionaryRepresentation:dictionary]; 46 | } 47 | return self; 48 | } 49 | 50 | - (NSDictionary *) dictionaryRepresentation 51 | { 52 | return [[[UNCodingUtil alloc] initWithObject:self] dictionaryRepresentation]; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLEventScheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD 3-Clause License found in the 6 | // LICENSE.txt file in the root directory of this source tree. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class BCLBeacon; 13 | @class BCLZone; 14 | 15 | /** 16 | * Schedules events for later execution. Used, e.g., to delay beacon 'leave' and zone 'enter' actions. 17 | */ 18 | @interface BCLEventScheduler : NSObject 19 | 20 | /** @name Properties */ 21 | 22 | /// A background task identifier used to schedule events for execution in background mode. 23 | @property (assign) UIBackgroundTaskIdentifier backgroundTaskIdentifier; 24 | 25 | /** @name Methods */ 26 | 27 | /** 28 | * @brief Schedule a beacon event for later. 29 | * @param beacon A beacon for which the event occured 30 | * @param delay Delay value in seconds 31 | * @param callback On time callback block 32 | */ 33 | - (void) scheduleEventForBeacon:(BCLBeacon *)beacon afterDelay:(NSTimeInterval)delay onTime:(void(^)(BCLBeacon *beacon))callback; 34 | 35 | /** 36 | * @biref Schedule a zone 'enter' event for later. 37 | * @param previousZone A zone whose range has been left 38 | * @param newZone A zone whose range has been entered 39 | * @param delay Delay value in seconds 40 | * @param callback On time callback block 41 | */ 42 | - (void) scheduleChangeZoneEventWithPreviousZone:(BCLZone *)previousZone newZone:(BCLZone *)newZone afterDelay:(NSTimeInterval)delay onTime:(void(^)(BCLZone *previousZone, BCLZone *newZone))callback; 43 | 44 | /** 45 | * @brief Cancel all events scheduled for a beacon 46 | * @param beacon A beacon beacon whose events will be cancelled 47 | * 48 | * @return YES if canelled. 49 | */ 50 | - (BOOL) cancelForBeacon:(BCLBeacon *)beacon; 51 | 52 | /*! 53 | * @brief Cancel any scheduled 'enter' zone events 54 | * @return YES is an event has been cancelled 55 | */ 56 | - (BOOL) cancelChangeZoneEvent; 57 | 58 | /** 59 | * @brief Check if a beacon has any scheduled events 60 | * @param beacon A beacon that will be checked for scheduled events 61 | * 62 | * @return YES if any event is scheduled for a beacon 63 | */ 64 | - (BOOL) isScheduledForBeacon:(BCLBeacon *)beacon; 65 | 66 | /*! 67 | * @brief Check if there's a scheduled 'enter' zone event 68 | * @return YES if there's any scheduled 'enter' zone event 69 | */ 70 | - (BOOL) isChangeZoneEventScheduled; 71 | 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLEventScheduler.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD 3-Clause License found in the 6 | // LICENSE.txt file in the root directory of this source tree. 7 | // 8 | 9 | #import "BCLEventScheduler.h" 10 | #import "CLBeacon+BeaconCtrl.h" 11 | #import "BCLBeacon.h" 12 | #import "BCLZone.h" 13 | #import "BCLActionEventScheduler.h" 14 | 15 | @interface BCLEventScheduler () 16 | 17 | @property (strong) NSMutableDictionary *beaconTimers; 18 | @property (strong) NSMutableDictionary *zoneTimers; 19 | 20 | @end 21 | 22 | @implementation BCLEventScheduler 23 | 24 | - (instancetype)init 25 | { 26 | if (self = [super init]) { 27 | self.beaconTimers = [NSMutableDictionary dictionary]; 28 | } 29 | return self; 30 | } 31 | 32 | - (void) scheduleEventForBeacon:(BCLBeacon *)beacon afterDelay:(NSTimeInterval)delay onTime:(void(^)(BCLBeacon *beacon))callback 33 | { 34 | @synchronized(self) { 35 | if (!self.beaconTimers) { 36 | self.beaconTimers = [NSMutableDictionary dictionary]; 37 | } 38 | 39 | UIBackgroundTaskIdentifier backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"beacon-os-event-scheduler" expirationHandler:^{ 40 | NSLog(@"Application about to terminate with range timers %@", self.beaconTimers); 41 | }]; 42 | 43 | NSDictionary *userInfo = @{@"beacon": beacon, @"delay": @(delay), @"callback": [callback copy], BCLActionEventSchedulerBackgroundTaskIdentifier: @(backgroundTaskIdentifier)}; 44 | 45 | if ([self isScheduledForBeacon:beacon]) { 46 | [self cancelForBeacon:beacon]; 47 | } 48 | 49 | // Schedule event for delay 50 | __weak typeof(self)selfWeak = self; 51 | NSTimer *timer = [NSTimer timerWithTimeInterval:delay target:selfWeak selector:@selector(handleBeaconTimer:) userInfo:userInfo repeats:NO]; 52 | [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 53 | 54 | [self.beaconTimers setObject:@{@"userInfo":userInfo, @"timer": timer} forKey:beacon.identifier]; 55 | } 56 | } 57 | 58 | - (void)scheduleChangeZoneEventWithPreviousZone:(BCLZone *)previousZone newZone:(BCLZone *)newZone afterDelay:(NSTimeInterval)delay onTime:(void (^)(BCLZone *, BCLZone *))callback 59 | { 60 | @synchronized(self) { 61 | if (!self.zoneTimers) { 62 | self.zoneTimers = [NSMutableDictionary dictionary]; 63 | } 64 | 65 | UIBackgroundTaskIdentifier backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"beacon-os-event-scheduler" expirationHandler:^{ 66 | NSLog(@"Application about to terminate with zone timers %@", self.zoneTimers); 67 | }]; 68 | 69 | NSDictionary *userInfo = @{@"previousZone": previousZone ? : [NSNull null], @"newZone": newZone ? : [NSNull null], @"delay": @(delay), @"callback": [callback copy], BCLActionEventSchedulerBackgroundTaskIdentifier: @(backgroundTaskIdentifier)}; 70 | 71 | // Schedule event for delay 72 | __weak typeof(self)selfWeak = self; 73 | NSTimer *timer = [NSTimer timerWithTimeInterval:delay target:selfWeak selector:@selector(handleChangeZoneTimer:) userInfo:userInfo repeats:NO]; 74 | [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 75 | 76 | self.zoneTimers[@"changeZoneEventTimer"] = @{@"userInfo" : userInfo, @"timer" : timer}; 77 | } 78 | } 79 | 80 | - (void) handleBeaconTimer:(NSTimer *)timer 81 | { 82 | @synchronized(self) { 83 | void (^callback)(BCLBeacon *beacon) = [timer.userInfo objectForKey:@"callback"]; 84 | 85 | UIBackgroundTaskIdentifier timerBackgroundTaskIdentifier = [timer.userInfo[BCLActionEventSchedulerBackgroundTaskIdentifier] unsignedIntegerValue]; 86 | UIBackgroundTaskIdentifier newBackgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]; 87 | 88 | BCLBeacon *beacon = timer.userInfo[@"beacon"]; 89 | if (callback) { 90 | callback(beacon); 91 | } 92 | 93 | [self.beaconTimers removeObjectForKey:beacon.identifier]; 94 | 95 | if (newBackgroundTaskIdentifier != UIBackgroundTaskInvalid) { 96 | [[UIApplication sharedApplication] endBackgroundTask:newBackgroundTaskIdentifier]; 97 | newBackgroundTaskIdentifier = UIBackgroundTaskInvalid; 98 | #ifdef DEBUG 99 | NSLog(@"handleBeaconTimer endBackgroundTask 1"); 100 | #endif 101 | } 102 | 103 | if (timerBackgroundTaskIdentifier != UIBackgroundTaskInvalid) { 104 | [[UIApplication sharedApplication] endBackgroundTask:timerBackgroundTaskIdentifier]; 105 | #ifdef DEBUG 106 | NSLog(@"handleBeaconTimer endBackgroundTask 2"); 107 | #endif 108 | } 109 | } 110 | } 111 | 112 | - (void) handleChangeZoneTimer:(NSTimer *)timer 113 | { 114 | @synchronized(self) { 115 | void (^callback)(BCLZone *previousZone, BCLZone *newZone) = [timer.userInfo objectForKey:@"callback"]; 116 | 117 | UIBackgroundTaskIdentifier timerBackgroundTaskIdentifier = [timer.userInfo[BCLActionEventSchedulerBackgroundTaskIdentifier] unsignedIntegerValue]; 118 | UIBackgroundTaskIdentifier newBackgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]; 119 | 120 | id previousZone = timer.userInfo[@"previousZone"]; 121 | if (previousZone == [NSNull null]) { 122 | previousZone = nil; 123 | } 124 | 125 | id newZone = timer.userInfo[@"newZone"]; 126 | if (newZone == [NSNull null]) { 127 | newZone = nil; 128 | } 129 | 130 | if (callback) { 131 | callback(previousZone, newZone); 132 | } 133 | 134 | [self.zoneTimers removeObjectForKey:@"changeZoneEventTimer"]; 135 | 136 | if (newBackgroundTaskIdentifier != UIBackgroundTaskInvalid) { 137 | [[UIApplication sharedApplication] endBackgroundTask:newBackgroundTaskIdentifier]; 138 | NSLog(@"handleChangeZoneTimer endBackgroundTask 1"); 139 | } 140 | 141 | if (timerBackgroundTaskIdentifier != UIBackgroundTaskInvalid) { 142 | [[UIApplication sharedApplication] endBackgroundTask:timerBackgroundTaskIdentifier]; 143 | NSLog(@"handleChangeZoneTimer endBackgroundTask 2"); 144 | } 145 | } 146 | } 147 | 148 | - (BOOL) cancelForBeacon:(BCLBeacon *)beacon 149 | { 150 | @synchronized(self) { 151 | 152 | if (!self.beaconTimers) 153 | return NO; 154 | 155 | NSDictionary *timerDict = self.beaconTimers[beacon.identifier]; 156 | if (timerDict) { 157 | NSTimer *timer = timerDict[@"timer"]; 158 | NSDictionary *userInfo = timerDict[@"userInfo"]; //workaround for crashin timer.userInfo 159 | 160 | // stop background if any 161 | UIBackgroundTaskIdentifier backgroundTaskIdentifier = [userInfo[BCLActionEventSchedulerBackgroundTaskIdentifier] unsignedIntegerValue]; 162 | 163 | if (backgroundTaskIdentifier != UIBackgroundTaskInvalid) { 164 | [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier]; 165 | #ifdef DEBUG 166 | NSLog(@"cancelForBeacon endBackgroundTask"); 167 | #endif 168 | } 169 | 170 | if (timer.isValid) { 171 | [timer invalidate]; 172 | } 173 | 174 | [self.beaconTimers removeObjectForKey:beacon.identifier]; 175 | } 176 | return NO; 177 | } 178 | } 179 | 180 | - (BOOL)cancelChangeZoneEvent 181 | { 182 | @synchronized(self) { 183 | 184 | if (!self.zoneTimers) 185 | return NO; 186 | 187 | NSDictionary *timerDict = self.zoneTimers[@"changeZoneEventTimer"]; 188 | 189 | if (timerDict) { 190 | NSTimer *timer = timerDict[@"timer"]; 191 | NSDictionary *userInfo = timerDict[@"userInfo"]; //workaround for crashin timer.userInfo 192 | 193 | // stop background if any 194 | UIBackgroundTaskIdentifier backgroundTaskIdentifier = [userInfo[BCLActionEventSchedulerBackgroundTaskIdentifier] unsignedIntegerValue]; 195 | 196 | if (backgroundTaskIdentifier != UIBackgroundTaskInvalid) { 197 | [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier]; 198 | #ifdef DEBUG 199 | NSLog(@"cancelChangeZoneEvent endBackgroundTask"); 200 | #endif 201 | } 202 | 203 | if (timer.isValid) { 204 | [timer invalidate]; 205 | } 206 | 207 | [self.beaconTimers removeObjectForKey:@"changeZoneEventTimer"]; 208 | } 209 | return NO; 210 | } 211 | } 212 | 213 | - (BOOL) isScheduledForBeacon:(BCLBeacon *)beacon 214 | { 215 | @synchronized(self) { 216 | id obj = self.beaconTimers[beacon.identifier]; 217 | return obj ? YES : NO; 218 | } 219 | } 220 | 221 | - (BOOL) isChangeZoneEventScheduled 222 | { 223 | @synchronized(self) { 224 | return self.zoneTimers[@"changeZoneEventTimer"] ? YES : NO; 225 | } 226 | } 227 | 228 | 229 | 230 | @end 231 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLExtension.h 3 | // 4 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 5 | // All rights reserved. 6 | // 7 | // This source code is licensed under the BSD 3-Clause License found in the 8 | // LICENSE.txt file in the root directory of this source tree. 9 | // 10 | 11 | #import 12 | #import "BCLTypes.h" 13 | 14 | @class BCLBeacon; 15 | 16 | @protocol BCLExtension 17 | @required 18 | 19 | + (NSString *) bcl_extensionName; 20 | - (instancetype) initWithParameters:(NSDictionary *)parameters; 21 | 22 | - (void) event:(BCLEventType)eventType forBeacon:(BCLBeacon *)beacon; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLKontaktIOBeaconConfigManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLKontaktIOBeaconConfigManager.h 3 | // Pods 4 | // 5 | // Created by Artur Wdowiarski on 18.09.2015. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @class BCLKontaktIOBeaconConfigManager; 12 | @class KTKBeacon; 13 | 14 | @protocol BCLKontaktIOBeaconConfigManagerDelegate 15 | 16 | - (void)kontaktIOBeaconManagerDidFetchKontaktIOBeacons:(BCLKontaktIOBeaconConfigManager *)manager; 17 | - (void)kontaktIOBeaconManager:(BCLKontaktIOBeaconConfigManager *)manager didMonitorBeaconDevices:(NSArray *)devices; 18 | - (void)kontaktIOBeaconManager:(BCLKontaktIOBeaconConfigManager *)manager didStartUpdatingBeaconWithUniqueId:(NSString *)uniqueId; 19 | - (void)kontaktIOBeaconManager:(BCLKontaktIOBeaconConfigManager *)manager didFinishUpdatingBeaconWithUniqueId:(NSString *)uniqueId newConfig:(KTKBeacon *)config success:(BOOL)success; 20 | - (void)kontaktIOBeaconManager:(BCLKontaktIOBeaconConfigManager *)manager didStartUpdatingFirmwareForBeaconWithUniqueId:(NSString *)uniqueId; 21 | - (void)kontaktIOBeaconManager:(BCLKontaktIOBeaconConfigManager *)manager isUpdatingFirmwareForBeaconWithUniqueId:(NSString *)uniqueId progress:(NSUInteger)progress; 22 | - (void)kontaktIOBeaconManager:(BCLKontaktIOBeaconConfigManager *)manager didFinishUpdatingFirmwareForBeaconWithUniqueId:(NSString *)uniqueId newFirwmareVersion:(NSString *)firmwareVersion success:(BOOL)success; 23 | 24 | @end 25 | 26 | @interface BCLKontaktIOBeaconConfigManager : NSObject 27 | 28 | @property (nonatomic, weak) id delegate; 29 | 30 | @property (nonatomic, copy) NSMutableDictionary *configsToUpdate; 31 | 32 | @property (nonatomic, copy) NSMutableDictionary *firmwaresToUpdate; 33 | 34 | @property (nonatomic, strong) NSMutableDictionary *kontaktBeaconsDictionary; 35 | 36 | - (instancetype)initWithApiKey:(NSString *)apiKey; 37 | 38 | - (void)fetchConfiguration:(void(^)(NSError *error))completion; 39 | 40 | - (void)startManagement; 41 | 42 | - (NSDictionary *)fieldsToUpdateForKontaktBeacon:(KTKBeacon *)beacon; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLKontaktIOBeaconConfigManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLKontaktIOBeaconConfigManager.m 3 | // Pods 4 | // 5 | // Created by Artur Wdowiarski on 18.09.2015. 6 | // 7 | // 8 | 9 | #import "BCLKontaktIOBeaconConfigManager.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | 19 | @interface BCLKontaktIOBeaconConfigManager () 20 | 21 | @property (nonatomic, strong) KTKClient *kontaktClient; 22 | @property (nonatomic, strong) KTKBluetoothManager *kontaktBluetoothManager; 23 | @property (nonatomic) BOOL isUpdatingBeacons; 24 | 25 | @end 26 | 27 | @implementation BCLKontaktIOBeaconConfigManager 28 | 29 | - (instancetype)initWithApiKey:(NSString *)apiKey 30 | { 31 | if (self = [super init]) { 32 | _kontaktClient = [KTKClient new]; 33 | _kontaktClient.apiKey = apiKey; 34 | 35 | _kontaktBluetoothManager = [KTKBluetoothManager new]; 36 | _kontaktBluetoothManager.delegate = self; 37 | 38 | _configsToUpdate = @{}.mutableCopy; 39 | } 40 | 41 | return self; 42 | } 43 | 44 | - (void)fetchConfiguration:(void (^)(NSError *))completion 45 | { 46 | NSError *error; 47 | 48 | NSArray *configsToChangeArray = [self.kontaktClient configsPaged:[[KTKPagingConfigs alloc] initWithIndexStart:0 andMaxResults:1000] forDevices:KTKDeviceTypeBeacon withError:&error]; 49 | 50 | if (error) { 51 | if (completion) { 52 | completion(error); 53 | } 54 | return; 55 | } 56 | 57 | [configsToChangeArray enumerateObjectsUsingBlock:^(KTKBeacon *beacon, NSUInteger idx, BOOL *stop) { 58 | self.configsToUpdate[beacon.uniqueID] = beacon; 59 | }]; 60 | 61 | NSArray *kontaktBeacons = [self.kontaktClient beaconsPaged:[[KTKPagingBeacons alloc] initWithIndexStart:0 andMaxResults:1000] withError:&error]; 62 | 63 | if (error) { 64 | if (completion) { 65 | completion(error); 66 | } 67 | return; 68 | } 69 | 70 | NSMutableArray *kontaktBeaconsUniqueIds = @[].mutableCopy; 71 | 72 | NSMutableDictionary *kontaktBeaconsDictionary = @{}.mutableCopy; 73 | [kontaktBeacons enumerateObjectsUsingBlock:^(KTKBeacon *beacon, NSUInteger idx, BOOL *stop) { 74 | [kontaktBeaconsUniqueIds addObject:beacon.uniqueID]; 75 | kontaktBeaconsDictionary[beacon.uniqueID] = beacon; 76 | }]; 77 | 78 | self.kontaktBeaconsDictionary = kontaktBeaconsDictionary; 79 | 80 | NSError *firmareUpdatesError; 81 | self.firmwaresToUpdate = [self.kontaktClient firmwaresLatestForBeaconsUniqueIds:kontaktBeaconsUniqueIds.copy withError:&firmareUpdatesError].mutableCopy; 82 | 83 | if (firmareUpdatesError) { 84 | if (completion) { 85 | completion(firmareUpdatesError); 86 | } 87 | return; 88 | } 89 | 90 | [self.delegate kontaktIOBeaconManagerDidFetchKontaktIOBeacons:self]; 91 | 92 | if (completion) { 93 | completion(nil); 94 | } 95 | } 96 | 97 | - (void)startManagement 98 | { 99 | [self.kontaktBluetoothManager startFindingDevices]; 100 | } 101 | 102 | - (NSDictionary *)fieldsToUpdateForKontaktBeacon:(KTKBeacon *)beacon 103 | { 104 | NSMutableDictionary *fieldsToUpdate = @{}.mutableCopy; 105 | 106 | KTKBeacon *config = self.configsToUpdate[beacon.uniqueID]; 107 | 108 | if (config) { 109 | if (config.interval) { 110 | fieldsToUpdate[@"interval"] = config.interval; 111 | } 112 | 113 | if (config.power) { 114 | fieldsToUpdate[@"power"] = config.power; 115 | } 116 | 117 | if (config.proximity) { 118 | fieldsToUpdate[@"proximity"] = config.proximity; 119 | } 120 | 121 | if (config.major) { 122 | fieldsToUpdate[@"major"] = config.major; 123 | } 124 | 125 | if (config.minor) { 126 | fieldsToUpdate[@"minor"] = config.minor; 127 | } 128 | } 129 | 130 | return fieldsToUpdate.copy; 131 | } 132 | 133 | #pragma mark - KTKBluetoothManagerDelegate 134 | 135 | - (void)bluetoothManager:(KTKBluetoothManager *)bluetoothManager didChangeDevices:(NSSet *)devices 136 | { 137 | NSLog(@"Kontakt.io bluetooth manager did change devices: %@", devices); 138 | if (self.isUpdatingBeacons) { 139 | return; 140 | } 141 | 142 | [self.delegate kontaktIOBeaconManager:self didMonitorBeaconDevices:[devices allObjects]]; 143 | [self updateKontaktBeaconDevices:devices]; 144 | } 145 | 146 | #pragma mark - Private 147 | 148 | - (void)updateKontaktBeaconDevices:(NSSet *)devices 149 | { 150 | self.isUpdatingBeacons = YES; 151 | dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^() { 152 | [devices enumerateObjectsUsingBlock:^(KTKBeaconDevice *beacon, BOOL *stop) { 153 | if ([self.configsToUpdate.allKeys containsObject:beacon.uniqueID] || [self.firmwaresToUpdate.allKeys containsObject:beacon.uniqueID]) { 154 | NSLog(@"Trying update kontakt.io beacon with uniqueId %@", beacon.uniqueID); 155 | NSString *password; 156 | NSString *masterPassword; 157 | KTKError *error; 158 | [self.kontaktClient beaconPassword:&password andMasterPassword:&masterPassword byUniqueId:beacon.uniqueID withError:&error]; 159 | if (error) { 160 | return; 161 | } 162 | 163 | if ([self.configsToUpdate.allKeys containsObject:beacon.uniqueID]) { 164 | if ([beacon connectWithPassword:password andError:&error]) { 165 | KTKBeacon *newConfig = self.configsToUpdate[beacon.uniqueID]; 166 | NSError *updateError; 167 | [self updateKontaktBeaconDevice:beacon withNewConfig:newConfig error:&updateError]; 168 | [beacon disconnect]; 169 | } 170 | } 171 | 172 | if ([self.firmwaresToUpdate.allKeys containsObject:beacon.uniqueID]) { 173 | KTKFirmware *newFirmware = self.firmwaresToUpdate[beacon.uniqueID]; 174 | if ([beacon connectWithPassword:password andError:&error]) { 175 | NSError *firmwareUpdateError; 176 | [self updateFirmwareForKontaktBeaconDevice:beacon masterPassword:masterPassword newFirmware:newFirmware error:&firmwareUpdateError]; 177 | [beacon disconnect]; 178 | } 179 | } 180 | } 181 | }]; 182 | 183 | self.isUpdatingBeacons = NO; 184 | }); 185 | } 186 | 187 | - (BOOL)updateFirmwareForKontaktBeaconDevice:(KTKBeaconDevice *)beaconDevice masterPassword:(NSString *)masterPassword newFirmware:(KTKFirmware *)newFirmware error:(NSError **)error 188 | { 189 | NSError *firmwareUpdateError = [beaconDevice updateFirmware:newFirmware usingMasterPassword:masterPassword progressHandler:^(KTKBeaconDeviceFirmwareUpdateState state, int progress) { 190 | switch (state) { 191 | case KTKBeaconDeviceFirmwareUpdateStatePreparing: 192 | { 193 | dispatch_async(dispatch_get_main_queue(), ^() { 194 | [self.delegate kontaktIOBeaconManager:self didStartUpdatingFirmwareForBeaconWithUniqueId:beaconDevice.uniqueID]; 195 | }); 196 | break; 197 | } 198 | case KTKBeaconDeviceFirmwareUpdateStateUploading: 199 | { 200 | dispatch_async(dispatch_get_main_queue(), ^() { 201 | [self.delegate kontaktIOBeaconManager:self isUpdatingFirmwareForBeaconWithUniqueId:beaconDevice.uniqueID progress:progress]; 202 | }); 203 | break; 204 | } 205 | } 206 | }]; 207 | 208 | if (firmwareUpdateError) { 209 | dispatch_async(dispatch_get_main_queue(), ^() { 210 | [self.delegate kontaktIOBeaconManager:self didFinishUpdatingFirmwareForBeaconWithUniqueId:beaconDevice.uniqueID newFirwmareVersion:nil success:NO]; 211 | }); 212 | return NO; 213 | } KTKBeacon *beaconToUpdate = self.kontaktBeaconsDictionary[beaconDevice.uniqueID]; 214 | beaconToUpdate.firmware = newFirmware.version; 215 | NSError *updateError; 216 | 217 | [self.kontaktClient beaconUpdate:beaconToUpdate withError:&updateError]; 218 | 219 | if (updateError){ 220 | dispatch_async(dispatch_get_main_queue(), ^() { 221 | [self.delegate kontaktIOBeaconManager:self didFinishUpdatingFirmwareForBeaconWithUniqueId:beaconDevice.uniqueID newFirwmareVersion:nil success:NO]; 222 | }); 223 | return NO; 224 | } 225 | 226 | if (self.firmwaresToUpdate[beaconDevice.uniqueID]) { 227 | [self.firmwaresToUpdate removeObjectForKey:beaconDevice.uniqueID]; 228 | } 229 | 230 | dispatch_async(dispatch_get_main_queue(), ^() { 231 | [self.delegate kontaktIOBeaconManager:self didFinishUpdatingFirmwareForBeaconWithUniqueId:beaconDevice.uniqueID newFirwmareVersion:newFirmware.version success:YES]; 232 | }); 233 | return YES; 234 | } 235 | 236 | - (BOOL)updateKontaktBeaconDevice:(KTKBeaconDevice *)beaconDevice withNewConfig:(KTKBeacon *)config error:(NSError **)error 237 | { 238 | dispatch_async(dispatch_get_main_queue(), ^() { 239 | [self.delegate kontaktIOBeaconManager:self didStartUpdatingBeaconWithUniqueId:config.uniqueID]; 240 | }); 241 | 242 | NSError *writeError; 243 | KTKCharacteristicDescriptor *descriptor; 244 | BOOL success = YES; 245 | 246 | KTKBeacon *kontaktBeacon = self.kontaktBeaconsDictionary[config.uniqueID]; 247 | 248 | if (success && config.power) { 249 | descriptor = [beaconDevice characteristicDescriptorWithType:kKTKCharacteristicDescriptorTypeTxPowerLevel]; 250 | writeError = [beaconDevice writeString:config.power.stringValue forCharacteristicWithDescriptor:descriptor]; 251 | if (writeError) { 252 | *error = writeError; 253 | success = NO; 254 | } else { 255 | kontaktBeacon.power = config.power; 256 | } 257 | } 258 | 259 | if (success && config.proximity) { 260 | descriptor = [beaconDevice characteristicDescriptorWithType:kKTKCharacteristicDescriptorTypeProximityUUID]; 261 | writeError = [beaconDevice writeString:config.proximity forCharacteristicWithDescriptor:descriptor]; 262 | if (writeError) { 263 | *error = writeError; 264 | success = NO; 265 | } else { 266 | kontaktBeacon.proximity = config.proximity; 267 | } 268 | } 269 | 270 | if (success && config.major) { 271 | descriptor = [beaconDevice characteristicDescriptorWithType:kKTKCharacteristicDescriptorTypeMajor]; 272 | writeError = [beaconDevice writeString:config.major.stringValue forCharacteristicWithDescriptor:descriptor]; 273 | if (writeError) { 274 | *error = writeError; 275 | return NO; 276 | } else { 277 | kontaktBeacon.major = config.major; 278 | } 279 | } 280 | 281 | if (success && config.minor) { 282 | descriptor = [beaconDevice characteristicDescriptorWithType:kKTKCharacteristicDescriptorTypeMinor]; 283 | writeError = [beaconDevice writeString:config.minor.stringValue forCharacteristicWithDescriptor:descriptor]; 284 | if (writeError) { 285 | *error = writeError; 286 | success = NO; 287 | } else { 288 | kontaktBeacon.minor = config.minor; 289 | } 290 | } 291 | 292 | if (success && config.interval) { 293 | descriptor = [beaconDevice characteristicDescriptorWithType:kKTKCharacteristicDescriptorTypeAdvertisingInterval]; 294 | writeError = [beaconDevice writeString:config.interval.stringValue forCharacteristicWithDescriptor:descriptor]; 295 | if (writeError) { 296 | *error = writeError; 297 | success = NO; 298 | } else { 299 | kontaktBeacon.interval = config.interval; 300 | } 301 | } 302 | 303 | if (success) { 304 | NSError *updateError; 305 | success = [self.kontaktClient beaconUpdate:config withError:&updateError]; 306 | if (!success) { 307 | *error = updateError; 308 | NSLog(@"There was an error while trying to update a kontakt.io beacon in kontakt.io panel: %@", updateError); 309 | } else if (self.configsToUpdate[beaconDevice.uniqueID]) { 310 | [self.configsToUpdate removeObjectForKey:beaconDevice.uniqueID]; 311 | } 312 | } 313 | 314 | dispatch_async(dispatch_get_main_queue(), ^() { 315 | [self.delegate kontaktIOBeaconManager:self didFinishUpdatingBeaconWithUniqueId:config.uniqueID newConfig:config success:success]; 316 | }); 317 | 318 | return success; 319 | } 320 | 321 | @end 322 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLLocation.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLLocation.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | #import 16 | 17 | @interface BCLLocation : NSObject 18 | 19 | @property (nonatomic, strong, readonly) CLLocation *location; 20 | @property (nonatomic, strong, readonly) NSNumber *floor; 21 | 22 | - (instancetype)initWithLocation:(CLLocation *)location floor:(NSNumber *)floor; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLLocation.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLLocation.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLLocation.h" 13 | #import 14 | 15 | @interface BCLLocation () 16 | 17 | @property (nonatomic, strong, readwrite) CLLocation *location; 18 | @property (nonatomic, strong, readwrite) NSNumber *floor; 19 | 20 | @end 21 | 22 | @implementation BCLLocation 23 | 24 | - (instancetype)initWithLocation:(CLLocation *)location floor:(NSNumber *)floor 25 | { 26 | if (self = [super init]) { 27 | _location = location; 28 | _floor = floor; 29 | } 30 | 31 | return self; 32 | } 33 | 34 | #pragma mark - NSSecureCoding 35 | 36 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 37 | { 38 | self = [super init]; 39 | if (!self) { 40 | return nil; 41 | } 42 | [UNCodingUtil decodeObject:self withCoder:aDecoder]; 43 | 44 | return self; 45 | } 46 | 47 | - (void)encodeWithCoder:(NSCoder *)aCoder 48 | { 49 | [UNCodingUtil encodeObject:self withCoder:aCoder]; 50 | } 51 | 52 | + (BOOL)supportsSecureCoding 53 | { 54 | return YES; 55 | } 56 | 57 | #pragma mark UNCoding 58 | 59 | - (instancetype) initWithDictionary:(NSDictionary *)dictionary 60 | { 61 | if (self = [super init]) { 62 | [[[UNCodingUtil alloc] initWithObject:self] loadDictionaryRepresentation:dictionary]; 63 | } 64 | return self; 65 | } 66 | 67 | - (NSDictionary *) dictionaryRepresentation 68 | { 69 | return [[[UNCodingUtil alloc] initWithObject:self] dictionaryRepresentation]; 70 | } 71 | 72 | - (NSArray *)propertiesToExcludeFromEncoding 73 | { 74 | return @[]; 75 | } 76 | 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLTrigger.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLTrigger.h 3 | // 4 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 5 | // All rights reserved. 6 | // 7 | // This source code is licensed under the BSD 3-Clause License found in the 8 | // LICENSE.txt file in the root directory of this source tree. 9 | // 10 | 11 | #import 12 | #import "BCLBeacon.h" 13 | #import "BCLZone.h" 14 | #import "BCLCondition.h" 15 | #import "BCLAction.h" 16 | #import "BCLEncodableObject.h" 17 | 18 | @protocol BCLBeaconCtrlDelegate; 19 | 20 | @interface BCLTrigger : BCLEncodableObject 21 | 22 | @property (strong, nonatomic) BCLBeacon *beacon; //FIXME: why strong ? 23 | @property (strong, nonatomic) BCLZone *zone; //FIXME: why strong ? 24 | @property (strong, nonatomic) NSArray *conditions; 25 | @property (strong, nonatomic) NSArray *actions; 26 | 27 | - (void)updatePropertiesFromDictionary:(NSDictionary *)dictionary; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLTrigger.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLTrigger.m 3 | // 4 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 5 | // All rights reserved. 6 | // 7 | // This source code is licensed under the BSD 3-Clause License found in the 8 | // LICENSE.txt file in the root directory of this source tree. 9 | // 10 | 11 | #import "BCLTrigger.h" 12 | #import "BCLCondition.h" 13 | #import "BCLConfiguration.h" 14 | #import "BCLActionEvent.h" 15 | #import "BCLBeaconCtrlDelegate.h" 16 | #import "UNCodingUtil.h" 17 | 18 | @implementation BCLTrigger 19 | 20 | - (instancetype) init 21 | { 22 | if (self = [super init]) { 23 | self.conditions = (NSArray *)[NSArray array]; 24 | self.actions = [NSArray array]; 25 | } 26 | return self; 27 | } 28 | 29 | - (void)updatePropertiesFromDictionary:(NSDictionary *)dictionary 30 | { 31 | // load conditions 32 | for (NSDictionary *conditionDictionary in dictionary[@"conditions"]) { 33 | // parameters without key "type" 34 | NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; 35 | NSArray *parameterKeys = [conditionDictionary.allKeys filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF != %@",@"type"]]; 36 | for (NSString *key in parameterKeys) { 37 | parameters[key] = conditionDictionary[key]; 38 | } 39 | 40 | Class conditionClass = [BCLConfiguration classForName:conditionDictionary[@"type"] protocol:@protocol(BCLCondition) selector:@selector(bcl_conditionType)]; 41 | if (conditionClass) { 42 | id conditionImpl = [[conditionClass alloc] initWithParameters:parameters]; 43 | self.conditions = (NSArray *)[self.conditions arrayByAddingObject:conditionImpl]; 44 | } 45 | } 46 | 47 | BCLAction *action = [[BCLAction alloc] init]; 48 | action.identifier = dictionary[@"action"][@"id"]; 49 | action.name = dictionary[@"action"][@"name"]; 50 | action.type = dictionary[@"action"][@"type"]; 51 | action.isTestAction = [dictionary[@"test"] boolValue]; 52 | 53 | action.customValues = dictionary[@"action"][@"custom_attributes"]; 54 | action.payload = dictionary[@"action"][@"payload"]; 55 | action.trigger = self; 56 | self.actions = [self.actions arrayByAddingObject:action]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLTypes.h 3 | // 4 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 5 | // All rights reserved. 6 | // 7 | // This source code is licensed under the BSD 3-Clause License found in the 8 | // LICENSE.txt file in the root directory of this source tree. 9 | // 10 | 11 | #import 12 | 13 | typedef NS_ENUM(NSInteger, BCLEventType) { 14 | BCLEventTypeUnknown = 0, 15 | BCLEventTypeEnter = 1, 16 | BCLEventTypeLeave = 2, 17 | BCLEventTypeRangeImmediate = 3, 18 | BCLEventTypeRangeNear = 4, 19 | BCLEventTypeRangeFar = 5, 20 | BCLEventTypeDwellTime = 6, 21 | BCLEventTypeTimer = 7 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLZone.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLZone.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | #import 15 | 16 | /*! 17 | * A class representing zones of beacons in BeaconCtrl 18 | */ 19 | 20 | @interface BCLZone : NSObject 21 | 22 | /** @name Properties */ 23 | 24 | /// Zone identifier assigned by the backend 25 | @property (nonatomic, copy) NSString *zoneIdentifier; 26 | 27 | /// Name of a zone 28 | @property (nonatomic, copy) NSString *name; 29 | 30 | /// A custom color assigned to a zone on the backend 31 | @property (nonatomic, strong) UIColor *color; 32 | 33 | /// Beacons assigned to a zone. A hash table with weak references to BCLBeacon objects 34 | @property (nonatomic, strong) NSHashTable *beacons; // conveniency 35 | 36 | /// Triggers assigned to a zone 37 | @property (strong, nonatomic) NSArray *triggers; 38 | 39 | /// Callback called on enter zone event 40 | @property (copy) void(^onEnterCallback)(BCLZone *zone); 41 | 42 | /// Callback called on leave zone event 43 | @property (copy) void(^onExitCallback)(BCLZone *zone); 44 | 45 | /** @name Methods */ 46 | 47 | /*! 48 | * @brief Initialize a zone object with an identifier and a name 49 | * 50 | * @param zoneIdentifier A zone identifier 51 | * @param name A zone name 52 | * 53 | * @return An initialized zone object 54 | */ 55 | - (instancetype)initWithIdentifier:(NSString *)zoneIdentifier name:(NSString *)name; 56 | 57 | /*! 58 | * @brief Update a zone's properties with values from a dictionary and a set of beacons 59 | * 60 | * @param dictionary A dictionary with zone's properties 61 | * @param beaconsSet A set of BLCBeacon objects that will be assigned to a zone 62 | * 63 | * @return An updated zone object 64 | */ 65 | - (void)updatePropertiesFromDictionary:(NSDictionary *)dictionary beacons:(NSSet *)beaconsSet; 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /BeaconCtrl/BCLZone.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLZone.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLZone.h" 13 | #import "BCLBeacon.h" 14 | #import "BCLUtils.h" 15 | #import "UIColor+Hex.h" 16 | #import 17 | 18 | @implementation BCLZone 19 | 20 | - (instancetype)initWithIdentifier:(NSString *)zoneIdentifier name:(NSString *)name 21 | { 22 | if (self = [super init]) { 23 | _zoneIdentifier = zoneIdentifier; 24 | _name = name; 25 | } 26 | 27 | return self; 28 | } 29 | 30 | - (NSString *)description 31 | { 32 | return [NSString stringWithFormat:@"%@ id: %@", self.name, self.zoneIdentifier]; 33 | } 34 | 35 | - (void)updatePropertiesFromDictionary:(NSDictionary *)dictionary beacons:(NSSet *)beaconsSet 36 | { 37 | self->_name = dictionary[@"name"]; 38 | self->_zoneIdentifier = [dictionary[@"id"] description]; 39 | 40 | // convert number value to string 41 | NSMutableSet *beaconIds = [NSMutableSet set]; 42 | for (NSNumber *number in dictionary[@"beacon_ids"]) { 43 | NSAssert([number isKindOfClass:[NSNumber class]], @"Invalid data - beacon_ids"); 44 | [beaconIds addObject:number.description]; 45 | } 46 | 47 | if (dictionary[@"color"]) { 48 | self.color = [UIColor colorFromHexString:dictionary[@"color"]]; 49 | } 50 | 51 | // store found beacons 52 | NSHashTable *beacons = [NSHashTable weakObjectsHashTable]; 53 | for (BCLBeacon *beacon in [beaconsSet filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"beaconIdentifier IN %@",beaconIds]]) { 54 | [beacons addObject:beacon]; 55 | beacon.zone = self; 56 | } 57 | self->_beacons = beacons; 58 | } 59 | 60 | - (NSArray *)triggers 61 | { 62 | @synchronized(self) { 63 | if (!_triggers) { 64 | _triggers = [NSArray array]; 65 | } 66 | return _triggers; 67 | } 68 | } 69 | 70 | #pragma mark - NSCopying 71 | 72 | - (id)copyWithZone:(NSZone *)zone 73 | { 74 | BCLZone *copyZone = [[BCLZone alloc] init]; 75 | UNCodingUtil *codingHelper = [[UNCodingUtil alloc] initWithObject:self]; 76 | for (NSString *propertyKey in codingHelper.allProperties) { 77 | [copyZone setValue:[self valueForKey:propertyKey] forKey:propertyKey]; 78 | } 79 | 80 | return copyZone; 81 | } 82 | 83 | #pragma mark - NSSecureCoding 84 | 85 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 86 | { 87 | self = [super init]; 88 | if (!self) { 89 | return nil; 90 | } 91 | [UNCodingUtil decodeObject:self withCoder:aDecoder]; 92 | 93 | return self; 94 | } 95 | 96 | - (void)encodeWithCoder:(NSCoder *)aCoder 97 | { 98 | [UNCodingUtil encodeObject:self withCoder:aCoder]; 99 | } 100 | 101 | + (BOOL)supportsSecureCoding 102 | { 103 | return YES; 104 | } 105 | 106 | #pragma mark UNCoding 107 | 108 | - (instancetype) initWithDictionary:(NSDictionary *)dictionary 109 | { 110 | if (self = [super init]) { 111 | [[[UNCodingUtil alloc] initWithObject:self] loadDictionaryRepresentation:dictionary]; 112 | } 113 | return self; 114 | } 115 | 116 | - (NSDictionary *) dictionaryRepresentation 117 | { 118 | return [[[UNCodingUtil alloc] initWithObject:self] dictionaryRepresentation]; 119 | } 120 | 121 | - (NSArray *)propertiesToExcludeFromEncoding 122 | { 123 | return @[]; 124 | } 125 | 126 | @end 127 | -------------------------------------------------------------------------------- /BeaconCtrl/BeaconCtrl-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 7 | // All rights reserved. 8 | // 9 | // This source code is licensed under the BSD 3-Clause License found in the 10 | // LICENSE.txt file in the root directory of this source tree. 11 | // 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #endif 16 | -------------------------------------------------------------------------------- /BeaconCtrl/CLBeacon+BeaconCtrl.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD 3-Clause License found in the 6 | // LICENSE.txt file in the root directory of this source tree. 7 | // 8 | 9 | #import 10 | 11 | @interface CLBeacon (BeaconCtrl) 12 | 13 | - (NSString *) bcl_identifier; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /BeaconCtrl/CLBeacon+BeaconCtrl.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD 3-Clause License found in the 6 | // LICENSE.txt file in the root directory of this source tree. 7 | // 8 | 9 | #import "CLBeacon+BeaconCtrl.h" 10 | 11 | @implementation CLBeacon (BeaconCtrl) 12 | 13 | - (NSString *) bcl_identifier 14 | { 15 | @synchronized(self) { 16 | // Build identifier 17 | 18 | NSMutableArray *arr = [NSMutableArray arrayWithCapacity:3]; 19 | if (self.proximityUUID) { 20 | [arr addObject:self.proximityUUID.UUIDString]; 21 | } 22 | 23 | if (self.major) { 24 | [arr addObject:self.major]; 25 | } 26 | 27 | if (self.minor) { 28 | [arr addObject:self.minor]; 29 | } 30 | 31 | return [arr componentsJoinedByString:@"+"]; 32 | } 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /BeaconCtrl/Conditions/BCLConditionEvent.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLConditionEvent.h 3 | // 4 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 5 | // All rights reserved. 6 | // 7 | // This source code is licensed under the BSD 3-Clause License found in the 8 | // LICENSE.txt file in the root directory of this source tree. 9 | // 10 | 11 | #import 12 | #import "BCLCondition.h" 13 | #import "UNCoding.h" 14 | 15 | @interface BCLConditionEvent : NSObject 16 | 17 | @property (nonatomic, strong) NSString *eventType; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /BeaconCtrl/Conditions/BCLConditionEvent.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLConditionEvent.m 3 | // 4 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 5 | // All rights reserved. 6 | // 7 | // This source code is licensed under the BSD 3-Clause License found in the 8 | // LICENSE.txt file in the root directory of this source tree. 9 | // 10 | 11 | #import "BCLConditionEvent.h" 12 | #import "UNCodingUtil.h" 13 | #import "BCLBeacon.h" 14 | 15 | @implementation BCLConditionEvent 16 | 17 | + (NSString *)bcl_conditionType 18 | { 19 | return @"event_type"; 20 | } 21 | 22 | - (instancetype)initWithParameters:(NSDictionary *)parameters 23 | { 24 | if (self = [self init]) { 25 | _eventType = parameters[@"event_type"]; 26 | } 27 | return self; 28 | } 29 | 30 | - (BOOL)evaluateCondition:(BCLEventType)eventType forBeacon:(BCLBeacon *)beacon 31 | { 32 | if ((eventType == BCLEventTypeEnter) && [self.eventType isEqualToString:@"enter"]) { 33 | return YES; 34 | } else if ((eventType == BCLEventTypeLeave) && [self.eventType isEqualToString:@"leave"]) { 35 | return YES; 36 | } else if ((eventType == BCLEventTypeRangeFar) && [self.eventType isEqualToString:@"far"]) { 37 | return YES; 38 | } else if ((eventType == BCLEventTypeRangeNear) && [self.eventType isEqualToString:@"near"]) { 39 | return YES; 40 | } else if ((eventType == BCLEventTypeRangeImmediate) && [self.eventType isEqualToString:@"immediate"]) { 41 | return YES; 42 | } 43 | return NO; 44 | } 45 | 46 | - (BOOL)evaluateCondition:(BCLEventType)eventType forZone:(BCLZone *)zone 47 | { 48 | if ((eventType == BCLEventTypeEnter) && [self.eventType isEqualToString:@"enter"]) { 49 | return YES; 50 | } else if ((eventType == BCLEventTypeLeave) && [self.eventType isEqualToString:@"leave"]) { 51 | return YES; 52 | } 53 | 54 | return NO; 55 | } 56 | 57 | #pragma mark - NSSecureCoding 58 | 59 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 60 | { 61 | if (!self) { 62 | return nil; 63 | } 64 | [UNCodingUtil decodeObject:self withCoder:aDecoder]; 65 | return self; 66 | } 67 | 68 | - (void)encodeWithCoder:(NSCoder *)aCoder 69 | { 70 | [UNCodingUtil encodeObject:self withCoder:aCoder]; 71 | } 72 | 73 | + (BOOL)supportsSecureCoding 74 | { 75 | return YES; 76 | } 77 | 78 | #pragma mark UNCoding 79 | 80 | - (instancetype) initWithDictionary:(NSDictionary *)dictionary 81 | { 82 | if (self = [super init]) { 83 | [[[UNCodingUtil alloc] initWithObject:self] loadDictionaryRepresentation:dictionary]; 84 | } 85 | return self; 86 | } 87 | 88 | - (NSDictionary *) dictionaryRepresentation 89 | { 90 | return [[[UNCodingUtil alloc] initWithObject:self] dictionaryRepresentation]; 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLAbstractBackend.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLAbstractBackend.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "UNCoding.h" 14 | 15 | @interface BCLAbstractBackend : NSObject 16 | 17 | @property (nonatomic, copy) NSString *clientId; 18 | @property (nonatomic, copy) NSString *clientSecret; 19 | 20 | @property (copy, readonly) NSString *accessToken; 21 | 22 | - (instancetype) initWithClientId:(NSString *)clientId clientSecret:(NSString *)clientSecret; 23 | 24 | + (NSString *) baseURLString; 25 | + (NSString *) authenticationURLString; 26 | 27 | - (void) setupURLRequest:(NSMutableURLRequest *)mutableRequest; 28 | - (BOOL)shouldFurtherProcessResponse:(NSURLResponse *)response completion:(void(^)(NSError *error))completion; 29 | - (BOOL) retrySelector:(SEL)selector sender:(id)sender parameters:(NSArray *)parameters; 30 | - (void) refetchToken:(void(^)(NSString *token, NSError *error))completion; 31 | - (NSDictionary *)authenticationParameters; 32 | - (void) reset; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLAbstractBackend.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLAbstractBackend.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLAbstractBackend.h" 13 | #import "NSObject+BCLAdditions.h" 14 | #import "NSHTTPURLResponse+BCLHTTPCodes.h" 15 | #import "BCLBeaconCtrl.h" 16 | #import "UNCodingUtil.h" 17 | 18 | @interface BCLAbstractBackend () 19 | 20 | @property (strong, nonatomic) NSMutableDictionary *retries; 21 | 22 | @property (copy, readwrite) NSString *accessToken; 23 | 24 | @end 25 | 26 | @implementation BCLAbstractBackend 27 | 28 | - (instancetype)initWithClientId:(NSString *)clientId clientSecret:(NSString *)clientSecret 29 | { 30 | if (self = [super init]) { 31 | _clientId = clientId; 32 | _clientSecret = clientSecret; 33 | } 34 | 35 | return self; 36 | } 37 | 38 | - (NSMutableDictionary *)retries 39 | { 40 | @synchronized(self) { 41 | if (!_retries) { 42 | _retries = [NSMutableDictionary dictionary]; 43 | } 44 | return _retries; 45 | } 46 | } 47 | 48 | /** 49 | * Retry selector 50 | * 51 | * @param selector selector 52 | * @param sender sender 53 | * @param parameters parameters 54 | * 55 | * @return YES if retry is possible (due tu maximum retry count) 56 | */ 57 | - (BOOL) retrySelector:(SEL)selector sender:(id)sender parameters:(NSArray *)parameters 58 | { 59 | @synchronized(self) { 60 | NSString *selectorString = NSStringFromSelector(selector); 61 | NSNumber *retryCount = self.retries[selectorString]; 62 | 63 | if (!retryCount) { 64 | retryCount = @(0); 65 | } else { 66 | retryCount = @(retryCount.integerValue + 1); 67 | } 68 | 69 | self.retries[selectorString] = retryCount; 70 | 71 | if (retryCount.integerValue > 3) 72 | return NO; 73 | 74 | [sender bcl_performSelector:selector withParameters:parameters]; 75 | [self.retries removeObjectForKey:selectorString]; 76 | return YES; 77 | } 78 | } 79 | 80 | - (void) setupURLRequest:(NSMutableURLRequest *)mutableRequest 81 | { 82 | if (self.accessToken) { 83 | [mutableRequest addValue:[NSString stringWithFormat:@"Bearer %@", self.accessToken] forHTTPHeaderField:@"Authorization"]; 84 | } 85 | } 86 | 87 | - (NSDictionary *)authenticationParameters 88 | { 89 | NSAssert(NO, @"This method needs to be implemented in a subclass"); 90 | return nil; 91 | } 92 | 93 | + (NSString *)baseURLString 94 | { 95 | NSAssert(NO, @"This method needs to be implemented in a subclass"); 96 | return nil; 97 | } 98 | 99 | + (NSString *)authenticationURLString 100 | { 101 | return [NSString stringWithFormat:@"%@/oauth/token", [self baseURLString]]; 102 | } 103 | 104 | - (void) refetchToken:(void(^)(NSString *token, NSError *error))completion 105 | { 106 | NSString *urlString = [[self class] authenticationURLString]; 107 | 108 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]]; 109 | [self setupURLRequest:request]; 110 | request.HTTPMethod = @"POST"; 111 | [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; 112 | [request addValue:@"application/json" forHTTPHeaderField:@"Accept"]; 113 | 114 | NSDictionary *params = [self authenticationParameters]; 115 | 116 | [request setHTTPBody:[NSJSONSerialization dataWithJSONObject:[params copy] options:0 error:nil]]; 117 | 118 | NSURLSession *session = [NSURLSession sharedSession]; 119 | NSURLSessionDataTask *task = [session dataTaskWithRequest:request 120 | completionHandler: 121 | ^(NSData *data, NSURLResponse *response, NSError *error) { 122 | 123 | NSError *jsonError = nil; 124 | NSDictionary *responseDictionary = nil; 125 | if (data) { 126 | responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; 127 | } 128 | 129 | NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; 130 | if (error || ![httpResponse isSuccess]) { 131 | if (completion) { 132 | completion(nil, error ?: [NSError errorWithDomain:BCLErrorDomain code:BCLErrorHTTPError userInfo:@{NSLocalizedDescriptionKey: [httpResponse description], @"BCLResponseDictionaryKey": responseDictionary ? : [NSNull null]}]); 133 | } 134 | return; 135 | } 136 | 137 | 138 | if (!responseDictionary && jsonError) { 139 | if (completion) { 140 | completion(nil, jsonError); 141 | } 142 | return; 143 | } 144 | 145 | NSString *accessToken = responseDictionary[@"access_token"]; 146 | 147 | if (!accessToken && completion) { 148 | completion(nil, [NSError errorWithDomain:BCLErrorDomain code:BCLInvalidDataErrorCode userInfo:@{NSLocalizedDescriptionKey: @"Unable to fetch token"}]); 149 | } 150 | 151 | self.accessToken = accessToken; 152 | 153 | if (completion) { 154 | completion(self.accessToken, nil); 155 | } 156 | }]; 157 | 158 | [task resume]; 159 | } 160 | 161 | - (BOOL)shouldFurtherProcessResponse:(NSURLResponse *)response completion:(void(^)(NSError *error))completion 162 | { 163 | NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; 164 | 165 | if (httpResponse.statusCode == 401) { 166 | // need authorization 167 | [self refetchToken:^(NSString *token, NSError *error) { 168 | if (completion) { 169 | completion(error); 170 | } 171 | }]; 172 | return YES; 173 | } 174 | return NO; 175 | } 176 | 177 | - (void)reset 178 | { 179 | self.accessToken = nil; 180 | } 181 | 182 | #pragma mark - NSSecureCoding 183 | 184 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 185 | { 186 | if (!self) { 187 | return nil; 188 | } 189 | [UNCodingUtil decodeObject:self withCoder:aDecoder]; 190 | return self; 191 | } 192 | 193 | - (void)encodeWithCoder:(NSCoder *)aCoder 194 | { 195 | [UNCodingUtil encodeObject:self withCoder:aCoder]; 196 | } 197 | 198 | + (BOOL)supportsSecureCoding 199 | { 200 | return YES; 201 | } 202 | 203 | #pragma mark UNCoding 204 | 205 | - (instancetype) initWithDictionary:(NSDictionary *)dictionary 206 | { 207 | if (self = [super init]) { 208 | [[[UNCodingUtil alloc] initWithObject:self] loadDictionaryRepresentation:dictionary]; 209 | } 210 | return self; 211 | } 212 | 213 | - (NSDictionary *) dictionaryRepresentation 214 | { 215 | return [[[UNCodingUtil alloc] initWithObject:self] dictionaryRepresentation]; 216 | } 217 | 218 | 219 | 220 | @end 221 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLActionEvent.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLEvent.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | #import "BCLTypes.h" 15 | 16 | @interface BCLActionEvent : NSObject 17 | 18 | @property (readonly) NSString *identifier; 19 | @property (assign) NSTimeInterval timestamp; 20 | @property (strong) NSString *beaconIdentifier; 21 | @property (strong) NSString *zoneIdentifier; 22 | @property (strong) NSString *actionIdentifier; 23 | @property (strong) NSString *actionName; 24 | @property (nonatomic) BCLEventType eventType; 25 | 26 | - (NSString *) eventTypeName; 27 | 28 | @end 29 | 30 | 31 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLActionEvent.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLEvent.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLActionEvent.h" 13 | #import "BCLActionEventScheduler.h" 14 | #import 15 | 16 | @implementation BCLActionEvent 17 | 18 | - (instancetype)init 19 | { 20 | if (self = [super init]) { 21 | _identifier = [[NSUUID UUID] UUIDString]; 22 | _timestamp = [[NSDate date] timeIntervalSince1970]; 23 | } 24 | return self; 25 | } 26 | 27 | - (id)copyWithZone:(NSZone *)zone 28 | { 29 | BCLActionEvent *copyEvent = [[BCLActionEvent alloc] init]; 30 | UNCodingUtil *codingHelper = [[UNCodingUtil alloc] initWithObject:self]; 31 | for (NSString *propertyKey in codingHelper.allProperties) { 32 | [self setValue:[self valueForKey:propertyKey] forKey:propertyKey]; 33 | } 34 | return copyEvent; 35 | } 36 | 37 | - (NSString *)eventTypeName 38 | { 39 | switch (self.eventType) { 40 | case BCLEventTypeEnter: 41 | return @"enter"; 42 | case BCLEventTypeLeave: 43 | return @"leave"; 44 | case BCLEventTypeRangeFar: 45 | return @"far"; 46 | case BCLEventTypeRangeNear: 47 | return @"near"; 48 | case BCLEventTypeRangeImmediate: 49 | return @"immediate"; 50 | case BCLEventTypeDwellTime: 51 | return @"dwell_time"; 52 | default: 53 | return nil; 54 | } 55 | } 56 | 57 | - (NSString *)description 58 | { 59 | return [NSString stringWithFormat:@"Action Event: type: %@ - beacon_id: %@ - zone_id: %@ - timestamp: %f ", self.eventTypeName, self.beaconIdentifier, self.zoneIdentifier, self.timestamp]; 60 | } 61 | 62 | #pragma mark - NSCoding 63 | 64 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 65 | { 66 | [UNCodingUtil decodeObject:self withCoder:aDecoder]; 67 | return self; 68 | } 69 | 70 | - (void)encodeWithCoder:(NSCoder *)aCoder 71 | { 72 | [UNCodingUtil encodeObject:self withCoder:aCoder]; 73 | } 74 | 75 | + (BOOL)supportsSecureCoding 76 | { 77 | return YES; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLActionEventScheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLActionEventScheduler.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "BCLTypes.h" 14 | 15 | extern NSString * const BCLActionEventSchedulerBackgroundTaskIdentifier; 16 | 17 | @class BCLActionEvent, BCLBackend; 18 | 19 | @interface BCLActionEventScheduler : NSObject 20 | 21 | - (instancetype) initWithBackend:(BCLBackend *)backend; 22 | 23 | - (void)sendActionEvents:(void (^)(NSError *error))completion; 24 | 25 | - (void) storeEvent:(BCLActionEvent *)event; 26 | 27 | - (BCLActionEvent *) lastStoredEventWithType:(BCLEventType)type; 28 | 29 | + (void)clearCache; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLActionEventScheduler.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLActionEventScheduler.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLActionEventScheduler.h" 13 | #import "BCLActionEvent.h" 14 | #import "BCLBackend.h" 15 | #import "SAMCache+BeaconCtrl.h" 16 | #import 17 | 18 | static NSTimeInterval BCLActionEventSchedulerMinSendIdleInterval = 15; 19 | static NSString * const BCLActionEventSchedulerCachedEventsCacheKey = @"cachedEventsMutableArray"; 20 | NSString * const BCLActionEventSchedulerBackgroundTaskIdentifier = @"backgroundTaskIdentifier"; 21 | 22 | NSString * _cacheKeyForEventType(BCLEventType type) { 23 | return [NSString stringWithFormat:@"%li", type]; 24 | }; 25 | 26 | @interface BCLActionEventScheduler () 27 | 28 | @property (weak) BCLBackend *backend; 29 | 30 | @property (nonatomic, strong) NSTimer *sendEventsTimer; 31 | @property (nonatomic, strong) NSDate *lastSendDate; 32 | @property (nonatomic, strong) NSNumber *currentBackgroundTaskIdentifierNumber; 33 | 34 | @end 35 | 36 | 37 | /** 38 | * Action send events scheduler, connected to BCLBackend and initialized from there. 39 | */ 40 | @implementation BCLActionEventScheduler 41 | 42 | #pragma mark - Events recorder 43 | 44 | /** 45 | * Initialize with backend instance, called from BCLBackend 46 | * 47 | * @param backend weak reference to backend instance 48 | * 49 | * @return Initialized instance 50 | */ 51 | - (instancetype) initWithBackend:(BCLBackend *)backend 52 | { 53 | if (self = [self init]) { 54 | self.backend = backend; 55 | } 56 | return self; 57 | } 58 | 59 | /** 60 | * Schedule for sending actions periodicaly 61 | */ 62 | - (void) scheduleSendingActionEventsWithDelay:(NSTimeInterval)delay userInfo:(NSDictionary *)userInfo 63 | { 64 | if (self.sendEventsTimer) { 65 | return; 66 | } 67 | 68 | self.sendEventsTimer = [NSTimer timerWithTimeInterval:delay target:self selector:@selector(sendActionEventsTimerHandler:) userInfo:userInfo repeats:NO]; 69 | [[NSRunLoop mainRunLoop] addTimer:self.sendEventsTimer forMode:NSDefaultRunLoopMode]; 70 | } 71 | 72 | /** 73 | * Send action events 74 | */ 75 | - (void) sendActionEventsTimerHandler:(NSTimer *)timer 76 | { 77 | NSDictionary *userInfo = timer.userInfo; 78 | 79 | self.sendEventsTimer = nil; 80 | 81 | [self sendActionEvents:^(NSError *error) { 82 | if (userInfo[BCLActionEventSchedulerBackgroundTaskIdentifier]) { 83 | UIBackgroundTaskIdentifier backgroundTaskIdentifier = [userInfo[BCLActionEventSchedulerBackgroundTaskIdentifier] unsignedIntegerValue]; 84 | 85 | if (backgroundTaskIdentifier != UIBackgroundTaskInvalid) { 86 | [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier]; 87 | self.currentBackgroundTaskIdentifierNumber = nil; 88 | #ifdef DEBUG 89 | NSLog(@"sendActionEventsTimerHandler endBackgroundTask"); 90 | #endif 91 | } 92 | } 93 | }]; 94 | } 95 | 96 | - (void)sendActionEvents:(void (^)(NSError *error))completion 97 | { 98 | NSMutableArray *cachedEvents = [[SAMCache bcl_actionEventsCache] objectForKey:BCLActionEventSchedulerCachedEventsCacheKey]; 99 | 100 | if (cachedEvents.count) { 101 | self.lastSendDate = [NSDate date]; 102 | } 103 | 104 | [self.backend sendEvents:cachedEvents completion:^(NSError *error) { 105 | if (error) { 106 | NSLog(@"Unable to send events %@",error); 107 | if (completion) { 108 | completion(error); 109 | } 110 | return; 111 | } 112 | 113 | // clear storage 114 | [self.class clearCache]; 115 | 116 | if (completion) { 117 | completion(nil); 118 | } 119 | }]; 120 | 121 | self.sendEventsTimer = nil; 122 | } 123 | 124 | - (dispatch_queue_t)eventsDispatchQueue 125 | { 126 | static dispatch_queue_t events_queue; 127 | 128 | if (!events_queue) { 129 | events_queue = dispatch_queue_create("events queue", DISPATCH_QUEUE_SERIAL); 130 | } 131 | return events_queue; 132 | } 133 | 134 | - (void) storeEvent:(BCLActionEvent *)event 135 | { 136 | if (!self.currentBackgroundTaskIdentifierNumber) { 137 | __weak typeof(self) weakSelf = self; 138 | self.currentBackgroundTaskIdentifierNumber = @([[UIApplication sharedApplication] beginBackgroundTaskWithName:@"beacon-os-action-event-scheduler" expirationHandler:^{ 139 | weakSelf.currentBackgroundTaskIdentifierNumber = nil; 140 | NSLog(@"Application about to terminate with unsent action events: %@", [[SAMCache bcl_actionEventsCache] objectForKey:BCLActionEventSchedulerCachedEventsCacheKey]); 141 | }]); 142 | } 143 | 144 | dispatch_queue_t queue = [self eventsDispatchQueue]; 145 | dispatch_async(queue, ^{ 146 | // store in cache 147 | NSMutableArray *cachedEvents = [[[SAMCache bcl_actionEventsCache] objectForKey:BCLActionEventSchedulerCachedEventsCacheKey] mutableCopy]; 148 | if (!cachedEvents) { 149 | cachedEvents = [NSMutableArray array]; 150 | } 151 | 152 | [cachedEvents addObject:event]; 153 | 154 | [[SAMCache bcl_actionEventsCache] setObject:cachedEvents.copy forKey:BCLActionEventSchedulerCachedEventsCacheKey]; 155 | 156 | [[SAMCache bcl_lastActionEventsCache] setObject:event forKey:_cacheKeyForEventType(event.eventType)]; 157 | 158 | NSDictionary *userInfo = @{BCLActionEventSchedulerBackgroundTaskIdentifier: self.currentBackgroundTaskIdentifierNumber}; 159 | 160 | NSTimeInterval intervalSinceLastSendDate = [[NSDate date] timeIntervalSinceDate:self.lastSendDate]; 161 | 162 | if (!self.lastSendDate || (intervalSinceLastSendDate > BCLActionEventSchedulerMinSendIdleInterval)) { 163 | NSLog(@"BEACON OS WILL SEND AN ACTION EVENT RIGHT AWAY"); 164 | [self scheduleSendingActionEventsWithDelay:1 userInfo:userInfo]; 165 | } else { 166 | NSLog(@"BEACON OS WILL SEND AN ACTION EVENT IN %f SECONDS", BCLActionEventSchedulerMinSendIdleInterval - intervalSinceLastSendDate); 167 | [self scheduleSendingActionEventsWithDelay:(BCLActionEventSchedulerMinSendIdleInterval - intervalSinceLastSendDate) userInfo:userInfo]; 168 | } 169 | }); 170 | } 171 | 172 | - (BCLActionEvent *)lastStoredEventWithType:(BCLEventType)type 173 | { 174 | return [[SAMCache bcl_lastActionEventsCache] objectForKey:_cacheKeyForEventType(type)]; 175 | } 176 | 177 | + (void)clearCache 178 | { 179 | [[SAMCache bcl_actionEventsCache] setObject:nil forKey:BCLActionEventSchedulerCachedEventsCacheKey]; 180 | } 181 | 182 | @end 183 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLActionHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLActionHandler.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "BCLAction.h" 14 | 15 | @protocol BCLActionHandler 16 | 17 | + (NSString *)handledActionTypeName; 18 | 19 | - (void)handleAction:(BCLAction *)action; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLActionHandlerFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLActionHandlerFactory.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "BCLActionHandler.h" 14 | 15 | @interface BCLActionHandlerFactory : NSObject 16 | 17 | - (id)actionHandlerForActionTypeName:(NSString *)actionTypeName; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLActionHandlerFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLActionHandlerFactory.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLActionHandlerFactory.h" 13 | #import "BCLActionHandler.h" 14 | #import 15 | 16 | static NSDictionary *_actionHandlerMapping; 17 | 18 | @implementation BCLActionHandlerFactory 19 | 20 | - (id)init 21 | { 22 | if (self = [super init]) { 23 | if (!_actionHandlerMapping) { 24 | _actionHandlerMapping = [BCLActionHandlerFactory mappingDictionary]; 25 | } 26 | } 27 | 28 | return self; 29 | } 30 | 31 | - (id)actionHandlerForActionTypeName:(NSString *)actionTypeName 32 | { 33 | return _actionHandlerMapping[actionTypeName.lowercaseString]; 34 | } 35 | 36 | #pragma mark - Private 37 | 38 | + (NSDictionary *)mappingDictionary 39 | { 40 | int numberOfClasses = objc_getClassList(NULL, 0); 41 | Class classList[numberOfClasses]; 42 | numberOfClasses = objc_getClassList(classList, numberOfClasses); 43 | 44 | NSMutableDictionary *mapping = [NSMutableDictionary dictionary]; 45 | 46 | for (int i = 0; i < numberOfClasses; i++) { 47 | Class aClass = classList[i]; 48 | 49 | if (class_getClassMethod(aClass, @selector(conformsToProtocol:)) && [aClass conformsToProtocol:@protocol(BCLActionHandler)]) { 50 | mapping[[aClass handledActionTypeName]] = [[aClass alloc] init]; 51 | } 52 | } 53 | 54 | return [mapping copy]; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLAdminBackend.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLAdminBackend.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLAbstractBackend.h" 13 | #import "BCLTypes.h" 14 | 15 | @class BCLBeacon; 16 | @class BCLZone; 17 | 18 | @interface BCLAdminBackend : BCLAbstractBackend 19 | 20 | - (void)authenticateUserWithEmail:(NSString *)email password:(NSString *)password completion:(void(^)(BOOL success, NSError *error))completion; 21 | - (void)registerNewUserWithEmail:(NSString *)email password:(NSString *)password passwordConfirmation:(NSString *)passwordConfirmation completion:(void(^)(BOOL success, NSError *error))completion; 22 | 23 | - (void)fetchTestApplicationCredentials:(void (^)(NSString *applicationClientId, NSString *applicationClientSecret, NSError *error))completion; 24 | 25 | - (void)createBeacon:(BCLBeacon *)beacon testActionName:(NSString *)testActionName testActionTrigger:(BCLEventType)trigger testActionAttributes:(NSArray *)testActionAttributes completion:(void (^)(BCLBeacon *, NSError *))completion; 26 | - (void)updateBeacon:(BCLBeacon *)beacon testActionName:(NSString *)testActionName testActionTrigger:(BCLEventType)trigger testActionAttributes:(NSArray *)testActionAttributes completion:(void (^)(BOOL success, NSError *error))completion; 27 | - (void)deleteBeacon:(BCLBeacon *)beacon completion:(void (^)(BOOL success, NSError *error))completion; 28 | 29 | - (void)fetchVendors:(void (^)(NSArray *vendors, NSError *error))completion; 30 | 31 | - (void)fetchBeacons:(void (^)(NSSet *beacons, NSError *error))completion; 32 | 33 | - (void)syncBeacon:(BCLBeacon *)beacon completion:(void (^)(NSError *error))completion; 34 | 35 | - (void)fetchZones:(NSSet *)beacons completion:(void (^)(NSSet *zones, NSError *error))completion; 36 | 37 | - (void)fetchZoneColors:(void (^)(NSArray *zoneColors, NSError *error))completion; 38 | 39 | - (void)createZone:(BCLZone *)zone completion:(void (^)(BCLZone *newZone, NSError *error))completion; 40 | - (void)updateZone:(BCLZone *)zone completion:(void (^)(BOOL success, NSError *error))completion; 41 | - (void)deleteZone:(BCLZone *)zone completion:(void (^)(BOOL success, NSError *error))completion; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLBackend.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLBackend.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "BCLAbstractBackend.h" 14 | 15 | @class BCLConfiguration, BCLActionEventScheduler, BCLBeacon; 16 | 17 | @interface BCLBackend : BCLAbstractBackend 18 | 19 | @property (copy, readonly) NSString *pushEnvironment; 20 | @property (copy, readonly) NSString *pushToken; 21 | @property (copy, readwrite, nonatomic) NSString *userId; 22 | 23 | - (instancetype) initWithClientId:(NSString *)clientId clientSecret:(NSString *)clientSecret pushEnvironment:(NSString *)pushEnvironment pushToken:(NSString *)pushToken; 24 | 25 | - (void) fetchConfiguration:(void(^)(BCLConfiguration *configuration, NSError *error))completion; 26 | - (void) sendEvents:(NSArray *)events completion:(void(^)(NSError *error))completion; 27 | - (void) fetchUsersInRangesOfBeacons:(NSSet *)beacons zones:(NSSet *)zones completion:(void (^)(NSDictionary *result, NSError *error))completion; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLCouponActionHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLCouponActionHandler.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLURLActionHandler.h" 13 | 14 | @interface BCLCouponActionHandler : BCLURLActionHandler 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLCouponActionHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLCouponActionHandler.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLCouponActionHandler.h" 13 | 14 | @interface BCLCouponActionHandler () 15 | 16 | @end 17 | 18 | @implementation BCLCouponActionHandler 19 | 20 | + (NSString *)handledActionTypeName 21 | { 22 | return @"coupon"; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLObservedBeaconsPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLObservedBeaconsPicker.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "BCLLocation.h" 14 | #import "BCLEncodableObject.h" 15 | 16 | @interface BCLObservedBeaconsPicker : BCLEncodableObject 17 | 18 | - (instancetype)initWithBeacons:(NSSet *)beacons andZones:(NSSet *)zones; 19 | 20 | - (NSSet *)observedBeaconsWithLocation:(BCLLocation *)location beaconsDidChange:(BOOL *)didChange; 21 | - (NSSet *)observedZones:(BOOL *)didChange; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLObservedBeaconsPicker.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLObservedBeaconsPicker.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLObservedBeaconsPicker.h" 13 | #import "BCLBeacon.h" 14 | #import "BCLZone.h" 15 | 16 | static NSUInteger const BCLObservedBeaconsPickerMaxObservedBeaconsCount = 16; 17 | static CGFloat const BCLMinimumDistanceChangeForRecalculation = 6.0; 18 | 19 | @interface BCLObservedBeaconsPicker () 20 | 21 | @property (nonatomic, copy) NSDictionary *allBeaconsDictionary; // dictionary of sets 22 | @property (nonatomic, copy) NSSet *allZones; 23 | 24 | @property (nonatomic, strong) BCLLocation *lastCheckedLocation; 25 | @property (nonatomic, strong) NSSet *lastComputedObservedBeacons; 26 | @property (nonatomic, strong) NSSet *lastComputedObservedZones; 27 | @property (nonatomic) BOOL shouldZonesBeRecalculated; 28 | 29 | @end 30 | 31 | @implementation BCLObservedBeaconsPicker 32 | 33 | - (instancetype)initWithBeacons:(NSSet *)beacons andZones:(NSSet *)zones 34 | { 35 | if (self = [super init]) { 36 | NSMutableDictionary *allBeaconsMutableDictionary = [NSMutableDictionary dictionaryWithCapacity:beacons.count]; 37 | 38 | // Store all beacons in a dictionary with floor numbers as keys and 39 | // sets of beacons as values 40 | 41 | __block NSNumber *normalizedLocationFloor; 42 | 43 | [beacons enumerateObjectsUsingBlock:^(BCLBeacon *beacon, BOOL *stop) { 44 | 45 | normalizedLocationFloor = beacon.location.floor ? : @-1; 46 | 47 | if (!allBeaconsMutableDictionary[normalizedLocationFloor]) { 48 | allBeaconsMutableDictionary[normalizedLocationFloor] = [NSMutableSet set]; 49 | } 50 | 51 | [allBeaconsMutableDictionary[normalizedLocationFloor] addObject:beacon]; 52 | }]; 53 | 54 | _allBeaconsDictionary = [allBeaconsMutableDictionary copy]; 55 | _allZones = zones; 56 | _shouldZonesBeRecalculated = YES; 57 | } 58 | 59 | return self; 60 | } 61 | 62 | - (NSSet *)observedBeaconsWithLocation:(BCLLocation *)location beaconsDidChange:(BOOL *)didChange 63 | { 64 | /* 65 | Returns a set that contains closest beacons from the given location's floor 66 | plus at least one closest beacon from each adjacent florr (well... there are at most two 67 | adjacent floors ;)) 68 | 69 | If the floor is not given, it returns the nearest beacons, without checking up their floors 70 | */ 71 | 72 | // Return last computed beacons is given location doesn't differ significantly 73 | // from the last calculated one 74 | if (self.lastCheckedLocation && [self.lastCheckedLocation.location distanceFromLocation:location.location] < BCLMinimumDistanceChangeForRecalculation && ([self.lastCheckedLocation.floor isEqual:location.floor] || self.lastCheckedLocation.floor == location.floor)) { 75 | *didChange = NO; 76 | return self.lastComputedObservedBeacons; 77 | } 78 | 79 | self.lastCheckedLocation = location; 80 | 81 | __block NSSet *adjacentFloorNumbersSet = [NSSet set]; 82 | NSArray *sortedAvailableFloors = [[self.allBeaconsDictionary.allKeys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { 83 | return [obj1 compare:obj2]; 84 | }] copy]; 85 | 86 | // We're only interested in adjacent floors, if the floor is given 87 | // Otherwise, we ignore beacons' floors 88 | 89 | NSMutableArray *otherAvailableFloors; 90 | 91 | if (location.floor) { 92 | // Get an array of available floors other than the requested location's floor 93 | otherAvailableFloors = sortedAvailableFloors.mutableCopy; 94 | [otherAvailableFloors removeObject:location.floor]; 95 | 96 | // Calculate which adjacent floors should we also monitor for (so that it's possible at all to 97 | // move from one floor to another) 98 | adjacentFloorNumbersSet = [NSSet set]; 99 | [otherAvailableFloors enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 100 | if ([location.floor compare:obj] == NSOrderedAscending) { 101 | if (idx == 0) { 102 | adjacentFloorNumbersSet = [NSSet setWithArray:@[obj]]; 103 | } else { 104 | adjacentFloorNumbersSet = [NSSet setWithArray:@[otherAvailableFloors[idx -1], obj]]; 105 | } 106 | *stop = YES; 107 | } else if (idx + 1 == otherAvailableFloors.count) { 108 | adjacentFloorNumbersSet = [NSSet setWithArray:@[obj]]; 109 | } 110 | }]; 111 | } 112 | 113 | // Get the observed beacons for the requested location's floor 114 | NSNumber *adjacentFloorsCapacity; 115 | if (adjacentFloorNumbersSet.count == 0) { 116 | adjacentFloorsCapacity = @0; 117 | } else if (adjacentFloorNumbersSet.count == 1) { 118 | adjacentFloorsCapacity = @(3); 119 | } else { 120 | adjacentFloorsCapacity = @(2); 121 | } 122 | 123 | NSUInteger givenFloorBeaconsCapacity = BCLObservedBeaconsPickerMaxObservedBeaconsCount - (adjacentFloorsCapacity.integerValue * adjacentFloorNumbersSet.count); 124 | 125 | NSMutableArray *observedBeacons = [[self sortedBeaconsWithLocation:location floor:location.floor capacityNumber:@(givenFloorBeaconsCapacity)] mutableCopy]; 126 | 127 | // Get at least one closest beacon from each of the adjacent floors 128 | __block NSArray *otherFloorFirstBeaconArray; 129 | 130 | [adjacentFloorNumbersSet enumerateObjectsUsingBlock:^(id floorNumber, BOOL *stop) { 131 | otherFloorFirstBeaconArray = [self sortedBeaconsWithLocation:location floor:floorNumber capacityNumber:adjacentFloorsCapacity]; 132 | [observedBeacons addObjectsFromArray:otherFloorFirstBeaconArray]; 133 | }]; 134 | 135 | NSSet *observedBeaconsSet = [NSSet setWithArray:observedBeacons]; 136 | 137 | *didChange = ![self.lastComputedObservedBeacons isEqualToSet:observedBeaconsSet]; 138 | 139 | self.lastComputedObservedBeacons = observedBeaconsSet; 140 | 141 | return self.lastComputedObservedBeacons; 142 | } 143 | 144 | - (NSSet *)observedZones:(BOOL *)didChange 145 | { 146 | if (!self.shouldZonesBeRecalculated) { 147 | return self.lastComputedObservedZones; 148 | } 149 | 150 | if (!self.lastComputedObservedBeacons) { 151 | return nil; 152 | } 153 | 154 | self.shouldZonesBeRecalculated = NO; 155 | 156 | NSMutableSet *observedZones = [NSMutableSet set]; 157 | 158 | __block BCLZone *currentZone; 159 | [self.lastComputedObservedBeacons enumerateObjectsUsingBlock:^(id beaconObj, BOOL *stop) { 160 | [self.allZones enumerateObjectsUsingBlock:^(id zoneObj, BOOL *innerStop) { 161 | currentZone = zoneObj; 162 | if ([currentZone.beacons containsObject:beaconObj]) { 163 | [observedZones addObject:currentZone]; 164 | } 165 | }]; 166 | }]; 167 | 168 | NSSet *result = [observedZones copy]; 169 | 170 | *didChange = ![result isEqual:self.lastComputedObservedZones]; 171 | 172 | self.lastComputedObservedZones = result; 173 | 174 | return result; 175 | } 176 | 177 | #pragma mark - Private 178 | 179 | - (NSArray *)sortedBeaconsWithLocation:(BCLLocation *)location floor:(NSNumber *)floor capacityNumber:(NSNumber *)capacity 180 | { 181 | NSArray *observedBeacons; 182 | 183 | // If no floor is specified, we're looking on all floors 184 | if (floor) { 185 | observedBeacons = [self.allBeaconsDictionary[floor] copy]; 186 | } else { 187 | NSMutableArray *mutableObservedBeacons = [NSMutableArray array]; 188 | 189 | [self.allBeaconsDictionary.allKeys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 190 | NSAssert([self.allBeaconsDictionary[obj] isKindOfClass:[NSSet class]], @"Invalid object class"); 191 | [mutableObservedBeacons addObjectsFromArray:[self.allBeaconsDictionary[obj] allObjects]]; 192 | }]; 193 | 194 | observedBeacons = [mutableObservedBeacons copy]; 195 | } 196 | 197 | __block BCLBeacon *beacon1; 198 | __block BCLBeacon *beacon2; 199 | __block CLLocationDistance beacon1Distance; 200 | __block CLLocationDistance beacon2Distance; 201 | 202 | observedBeacons = [observedBeacons sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { 203 | beacon1 = (BCLBeacon *)obj1; 204 | beacon2 = (BCLBeacon *)obj2; 205 | 206 | if (!beacon1.location || !beacon2.location) { 207 | NSLog(@""); 208 | } 209 | 210 | beacon1Distance = [beacon1.location.location distanceFromLocation:location.location]; 211 | beacon2Distance = [beacon2.location.location distanceFromLocation:location.location]; 212 | 213 | if (!beacon1.location && beacon2.location) { 214 | return NSOrderedDescending; 215 | } else if (!beacon2.location && beacon1.location) { 216 | return NSOrderedAscending; 217 | } if (beacon1Distance > beacon2Distance) { 218 | return NSOrderedDescending; 219 | } else if (beacon1Distance < beacon2Distance) { 220 | return NSOrderedAscending; 221 | } else { 222 | return NSOrderedSame; 223 | } 224 | }]; 225 | 226 | if (capacity && observedBeacons.count > capacity.integerValue) { 227 | observedBeacons = [observedBeacons subarrayWithRange:NSMakeRange(0, capacity.integerValue)]; 228 | } 229 | 230 | return observedBeacons; 231 | } 232 | 233 | @end 234 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLURLActionHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLURLActionHandler.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "BCLActionHandler.h" 14 | 15 | @interface BCLURLActionHandler : NSObject 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLURLActionHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLURLActionHandler.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLURLActionHandler.h" 13 | #import "UIWindow+BCLVisibleViewController.h" 14 | 15 | @interface BCLURLActionHandler () 16 | 17 | @property (nonatomic, weak, readonly) UIWebView *webView; 18 | @property (nonatomic) BOOL isPresenting; 19 | @property (nonatomic) BOOL isDismissing; 20 | @property (nonatomic, strong) NSMutableArray *navigationControllers; 21 | 22 | @end 23 | 24 | @implementation BCLURLActionHandler 25 | 26 | - (instancetype)init 27 | { 28 | if (self = [super init]) { 29 | _navigationControllers = [@[] mutableCopy]; 30 | } 31 | 32 | return self; 33 | } 34 | 35 | + (NSString *)handledActionTypeName 36 | { 37 | return @"url"; 38 | } 39 | 40 | - (void)handleAction:(BCLAction *)action 41 | { 42 | NSLog(@"Is going to explicitly perform coupon action: %@", action); 43 | 44 | if (self.isPresenting || self.isDismissing) { 45 | [self performSelector:@selector(handleAction:) withObject:action afterDelay:0.3]; 46 | return; 47 | } 48 | 49 | UIViewController *visibleViewController = [[UIApplication sharedApplication].keyWindow bcl_visibleViewController]; 50 | 51 | if (![self canPresentURLOnViewController:visibleViewController]) { 52 | [self performSelector:@selector(handleAction:) withObject:action afterDelay:5]; 53 | return; 54 | } 55 | 56 | UIViewController *webViewController = [[UIViewController alloc] init]; 57 | webViewController.view.frame = visibleViewController.view.frame; 58 | UIWebView *webView = [[UIWebView alloc] initWithFrame:webViewController.view.frame]; 59 | webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 60 | [webViewController.view addSubview:webView]; 61 | 62 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:webViewController]; 63 | [self.navigationControllers addObject:navigationController]; 64 | 65 | UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissCoupon:)]; 66 | webViewController.navigationItem.rightBarButtonItem = barButtonItem; 67 | 68 | self.isPresenting = YES; 69 | dispatch_async(dispatch_get_main_queue(), ^() { 70 | [visibleViewController presentViewController:navigationController animated:YES completion:^() { 71 | self.isPresenting = NO; 72 | [webView loadRequest:[NSURLRequest requestWithURL:action.URL]]; 73 | }]; 74 | }); 75 | } 76 | 77 | - (void)dismissCoupon:(id)sender 78 | { 79 | UINavigationController *navigationController = (UINavigationController *)[[[sender nextResponder] nextResponder] nextResponder]; 80 | 81 | if ([navigationController.visibleViewController.navigationItem.rightBarButtonItem isEqual:sender]) { 82 | self.isDismissing = YES; 83 | [navigationController dismissViewControllerAnimated:YES completion:^() { 84 | self.isDismissing = NO; 85 | }]; 86 | } 87 | 88 | if ([self.navigationControllers containsObject:navigationController]) { 89 | [self.navigationControllers removeObject:navigationController]; 90 | } 91 | } 92 | 93 | - (BOOL)canPresentURLOnViewController:(UIViewController *)viewController 94 | { 95 | return ![viewController isKindOfClass:[UIAlertController class]]; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // BCLUtils.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NSArray* AllocNotRetainedArray(); 15 | NSSet* AllocNotRetainedSet(); -------------------------------------------------------------------------------- /BeaconCtrl/Private/BCLUtils.m: -------------------------------------------------------------------------------- 1 | // 2 | // BCLUtils.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "BCLUtils.h" 13 | 14 | NSArray* AllocNotRetainedArray() { 15 | CFArrayRef arrayRef = NULL; 16 | CFArrayCallBacks notRetainedCallbacks = kCFTypeArrayCallBacks; 17 | notRetainedCallbacks.retain = NULL; 18 | notRetainedCallbacks.release = NULL; 19 | arrayRef = CFArrayCreate(kCFAllocatorDefault, 0, 0, ¬RetainedCallbacks); 20 | return (__bridge NSArray *)arrayRef; 21 | } 22 | 23 | NSSet* AllocNotRetainedSet() { 24 | CFSetRef setRef = NULL; 25 | CFSetCallBacks notRetainedCallbacks = kCFTypeSetCallBacks; 26 | notRetainedCallbacks.retain = NULL; 27 | notRetainedCallbacks.release = NULL; 28 | setRef = CFSetCreate(kCFAllocatorDefault, 0, 0, ¬RetainedCallbacks); 29 | return (__bridge NSSet *)setRef; 30 | } 31 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/NSHTTPURLResponse+BCLHTTPCodes.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSHTTPURLResponse+BCLHTTPCodes.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | @interface NSHTTPURLResponse (BCLHTTPCodes) 15 | 16 | @property (readonly) BOOL isInformational; 17 | @property (readonly) BOOL isSuccess; 18 | @property (readonly) BOOL isRedirect; 19 | @property (readonly) BOOL isClientError; 20 | @property (readonly) BOOL isServerError; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/NSHTTPURLResponse+BCLHTTPCodes.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSHTTPURLResponse+BCLHTTPCodes.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "NSHTTPURLResponse+BCLHTTPCodes.h" 13 | 14 | @implementation NSHTTPURLResponse (BCLHTTPCodes) 15 | 16 | - (BOOL) isInformational 17 | { 18 | return (self.statusCode >= 100 && self.statusCode < 200); 19 | } 20 | 21 | - (BOOL) isSuccess 22 | { 23 | return (self.statusCode >= 200 && self.statusCode < 300) || [self isRedirect] || [self isInformational]; 24 | } 25 | 26 | - (BOOL) isRedirect 27 | { 28 | return (self.statusCode >= 300 && self.statusCode < 400); 29 | } 30 | 31 | - (BOOL) isClientError 32 | { 33 | return (self.statusCode >= 400 && self.statusCode < 500); 34 | } 35 | 36 | - (BOOL) isServerError 37 | { 38 | return (self.statusCode >= 500 && self.statusCode < 600); 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/NSObject+BCLAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+BCLAdditions.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | @interface NSObject (BCLAdditions) 15 | 16 | - (id) bcl_performSelector:(SEL)selector withParameters:(NSArray *)parameters; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/NSObject+BCLAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+BCLAdditions.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "NSObject+BCLAdditions.h" 13 | 14 | @implementation NSObject (BCLAdditions) 15 | 16 | - (id) bcl_performSelector:(SEL)selector withParameters:(NSArray *)parameters 17 | { 18 | // perform selector 19 | NSMethodSignature *sig = [self methodSignatureForSelector:selector]; 20 | if (!sig) 21 | return nil; 22 | 23 | NSInvocation* invo = [NSInvocation invocationWithMethodSignature:sig]; 24 | [invo setSelector:selector]; 25 | [invo retainArguments]; 26 | 27 | for (NSInteger idx = 0; idx < parameters.count; idx++) { 28 | id parameter = parameters[idx]; 29 | if (parameter != [NSNull null]) { 30 | [invo setArgument:¶meter atIndex:idx + 2]; 31 | } 32 | } 33 | 34 | [invo invokeWithTarget:self]; 35 | if (sig.methodReturnLength) { 36 | id anObject; 37 | [invo getReturnValue:&anObject]; 38 | return anObject; 39 | } 40 | return nil; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/NSUserDefaults+BCLiCloud.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSUserDefaults+BCLiCloud.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | @interface NSUserDefaults (BCLiCloud) 15 | 16 | -(void)setValue:(id)value forKey:(NSString *)key iCloudSync:(BOOL)sync; 17 | -(void)setObject:(id)value forKey:(NSString *)key iCloudSync:(BOOL)sync; 18 | 19 | -(id)valueForKey:(NSString *)key iCloudSync:(BOOL)sync; 20 | -(id)objectForKey:(NSString *)key iCloudSync:(BOOL)sync; 21 | 22 | -(BOOL)synchronizeWithiCloud; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/NSUserDefaults+BCLiCloud.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSUserDefaults+BCLiCloud.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "NSUserDefaults+BCLiCloud.h" 13 | 14 | @implementation NSUserDefaults (BCLiCloud) 15 | 16 | -(void)setValue:(id)value forKey:(NSString *)key iCloudSync:(BOOL)sync 17 | { 18 | if (sync) 19 | [[NSUbiquitousKeyValueStore defaultStore] setValue:value forKey:key]; 20 | 21 | [self setValue:value forKey:key]; 22 | } 23 | 24 | -(id)valueForKey:(NSString *)key iCloudSync:(BOOL)sync 25 | { 26 | if (sync) 27 | { 28 | //Get value from iCloud 29 | id value = [[NSUbiquitousKeyValueStore defaultStore] valueForKey:key]; 30 | 31 | //Store locally and synchronize 32 | [self setValue:value forKey:key]; 33 | [self synchronize]; 34 | 35 | return value; 36 | } 37 | 38 | return [self valueForKey:key]; 39 | } 40 | 41 | - (void)removeValueForKey:(NSString *)key iCloudSync:(BOOL)sync 42 | { 43 | [self removeObjectForKey:key iCloudSync:sync]; 44 | } 45 | 46 | 47 | 48 | -(void)setObject:(id)value forKey:(NSString *)defaultName iCloudSync:(BOOL)sync 49 | { 50 | if (sync) 51 | [[NSUbiquitousKeyValueStore defaultStore] setObject:value forKey:defaultName]; 52 | 53 | [self setObject:value forKey:defaultName]; 54 | } 55 | 56 | -(id)objectForKey:(NSString *)key iCloudSync:(BOOL)sync 57 | { 58 | if (sync) 59 | { 60 | //Get value from iCloud 61 | id value = [[NSUbiquitousKeyValueStore defaultStore] objectForKey:key]; 62 | 63 | //Store to NSUserDefault and synchronize 64 | [self setObject:value forKey:key]; 65 | [self synchronize]; 66 | 67 | return value; 68 | } 69 | 70 | return [self objectForKey:key]; 71 | } 72 | 73 | - (void)removeObjectForKey:(NSString *)key iCloudSync:(BOOL)sync 74 | { 75 | if (sync) 76 | [[NSUbiquitousKeyValueStore defaultStore] removeObjectForKey:key]; 77 | 78 | //Remove from NSUserDefault 79 | return [self removeObjectForKey:key]; 80 | } 81 | 82 | 83 | 84 | -(BOOL)synchronizeWithiCloud 85 | { 86 | BOOL res = true; 87 | 88 | res &= [self synchronize]; 89 | res &= [[NSUbiquitousKeyValueStore defaultStore] synchronize]; 90 | 91 | return res; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/UIWindow+BCLVisibleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIWindow+BCLVisibleViewController.h 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | @interface UIWindow (BCLVisibleViewController) 15 | 16 | - (UIViewController *)bcl_visibleViewController; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /BeaconCtrl/Private/UIWindow+BCLVisibleViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIWindow+BCLVisibleViewController.m 3 | // BeaconCtrl 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "UIWindow+BCLVisibleViewController.h" 13 | 14 | @implementation UIWindow (BCLVisibleViewController) 15 | 16 | - (UIViewController *)bcl_visibleViewController 17 | { 18 | return [self bcl_getVisibleViewControllerFrom:self.rootViewController]; 19 | } 20 | 21 | - (UIViewController *) bcl_getVisibleViewControllerFrom:(UIViewController *) vc { 22 | if ([vc isKindOfClass:[UINavigationController class]]) { 23 | return [self bcl_getVisibleViewControllerFrom:[((UINavigationController *) vc) visibleViewController]]; 24 | } else if ([vc isKindOfClass:[UITabBarController class]]) { 25 | return [self bcl_getVisibleViewControllerFrom:[((UITabBarController *) vc) selectedViewController]]; 26 | } else { 27 | if (vc.presentedViewController) { 28 | return [self bcl_getVisibleViewControllerFrom:vc.presentedViewController]; 29 | } else { 30 | return vc; 31 | } 32 | } 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /BeaconCtrl/SAMCache+BeaconCtrl.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD 3-Clause License found in the 6 | // LICENSE.txt file in the root directory of this source tree. 7 | // 8 | 9 | #import "SAMCache.h" 10 | 11 | #define BLECacheActionIdentifierFormat(action,beacon) \ 12 | [NSString stringWithFormat:@"beacon.%@.action.%@.type.%@", beacon.identifier, action.uniqueIdentifier, action.type] 13 | 14 | @interface SAMCache (BeaconCtrl) 15 | 16 | + (SAMCache *) bcl_monitoredProximityCache; 17 | + (SAMCache *) bcl_lastActionEventsCache; 18 | + (SAMCache *)bcl_actionEventsCache; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /BeaconCtrl/SAMCache+BeaconCtrl.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 3 | // All rights reserved. 4 | // 5 | // This source code is licensed under the BSD 3-Clause License found in the 6 | // LICENSE.txt file in the root directory of this source tree. 7 | // 8 | 9 | #import "SAMCache+BeaconCtrl.h" 10 | 11 | static SAMCache *bcl_monitoredProximityCache; 12 | static SAMCache *bcl_lastActionEventsCache; 13 | static SAMCache *bcl_actionEventsCache; 14 | 15 | @implementation SAMCache (BeaconCtrl) 16 | 17 | + (SAMCache *) bcl_monitoredProximityCache 18 | { 19 | if (bcl_monitoredProximityCache != nil) { 20 | return bcl_monitoredProximityCache; 21 | } 22 | 23 | static dispatch_once_t onceToken; 24 | dispatch_once(&onceToken, ^{ 25 | bcl_monitoredProximityCache = [[SAMCache alloc] initWithName:[NSString stringWithFormat:@"com.up-next.BeaconCtrl.monitored"]]; 26 | }); 27 | 28 | return bcl_monitoredProximityCache; 29 | } 30 | 31 | + (SAMCache *)bcl_lastActionEventsCache 32 | { 33 | if (bcl_lastActionEventsCache != nil) { 34 | return bcl_lastActionEventsCache; 35 | } 36 | 37 | static dispatch_once_t onceToken; 38 | dispatch_once(&onceToken, ^{ 39 | bcl_lastActionEventsCache = [[SAMCache alloc] initWithName:[NSString stringWithFormat:@"com.up-next.BeaconCtrl.lastActionEventsCache"]]; 40 | }); 41 | 42 | return bcl_lastActionEventsCache; 43 | } 44 | 45 | + (SAMCache *)bcl_actionEventsCache 46 | { 47 | if (bcl_actionEventsCache != nil) { 48 | return bcl_actionEventsCache; 49 | } 50 | 51 | static dispatch_once_t onceToken; 52 | dispatch_once(&onceToken, ^{ 53 | bcl_actionEventsCache = [[SAMCache alloc] initWithName:[NSString stringWithFormat:@"com.up-next.BeaconCtrl.actionEventsCache"]]; 54 | }); 55 | 56 | return bcl_actionEventsCache; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /BeaconCtrl/UIColor+Hex.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Hex.h 3 | // Pods 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | @interface UIColor (Hex) 15 | 16 | + (UIColor *)colorFromHexString:(NSString *)hexString; 17 | - (NSString *)hexString; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /BeaconCtrl/UIColor+Hex.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Hex.m 3 | // Pods 4 | // 5 | // Copyright (c) 2015, Upnext Technologies Sp. z o.o. 6 | // All rights reserved. 7 | // 8 | // This source code is licensed under the BSD 3-Clause License found in the 9 | // LICENSE.txt file in the root directory of this source tree. 10 | // 11 | 12 | #import "UIColor+Hex.h" 13 | 14 | @implementation UIColor (Hex) 15 | 16 | + (UIColor *)colorFromHexString:(NSString *)hexString { 17 | unsigned rgbValue = 0; 18 | NSScanner *scanner = [NSScanner scannerWithString:hexString]; 19 | [scanner scanHexInt:&rgbValue]; 20 | return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0]; 21 | } 22 | 23 | - (NSString *)hexString 24 | { 25 | const CGFloat *components = CGColorGetComponents(self.CGColor); 26 | 27 | CGFloat r = components[0]; 28 | CGFloat g = components[1]; 29 | CGFloat b = components[2]; 30 | 31 | NSString *result = [[NSString stringWithFormat:@"%02lX%02lX%02lX", 32 | lroundf(r * 255), 33 | lroundf(g * 255), 34 | lroundf(b * 255)] lowercaseString]; 35 | 36 | return result; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Upnext Technologies Sp. z o.o. 2 | All rights reserved. 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 5 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 6 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 7 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | platform :ios, '7.0' 4 | 5 | xcodeproj 'BeaconCtrl.xcodeproj' 6 | 7 | target "BeaconCtrl", :exclusive => true do 8 | link_with "BeaconCtrl" 9 | pod "UNNetworking", :git => "https://github.com/upnext/UNNetworking.git", :branch => :master 10 | pod "KontaktSDK-OLD” 11 | pod "SAMCache" 12 | end 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | #BeaconControl 3 | ##Integration Guide ver. 1.0 4 | ###Revision History 5 | 6 | 7 | Date |Revision |Description 8 | -----------|-------------|----------- 9 | 10.07.2015 | 1.0 |Added BeaconCtrl usage guide. Added BeaconControl iOS SDK integration guide. 10 | 15.10.2015 | 1.1 |Switched to publicly available podspec version of UNNetworking. Bug fixes. 11 | 14.04.2016 | 1.2 |Added support to override hosting default base URL. Fixed issues with beacons ranging and events cache. 12 | 13 | ###Overview 14 | 15 | BeaconControl is your free entry to the beacon world. It's an open source platform that lets your applications sense the world around them. Beacons provide context-rich information to a user’s device upon entering the range of a specific beacon. BeaconControl allows you to set-up predefined triggers and actions for each beacon. For example, when a “listening” mobile device walks by a beacon, you can configure your app to send notifications, trigger URL openings, or deliver content. 16 | 17 | In order to use BeaconCtrl you will need to: 18 | 19 | 1. Setup your beacon infrastructure (with the help of BeaconControl iOS App - link needed!) and configure actions triggered in your mobile applications. 20 | 2. Integrate the BeaconControl SDK with an application. 21 | 22 | 23 | ### BeaconControl Architechture Description 24 | 25 | BeaconControl SDK provides intuitive interfaces for interaction with two APIs provided by BeaconControl: Client API and Server-to-Server API (S2S API). 26 | 27 | Main public interfaces (refer to the efficial documentation for more detailed information): 28 | 29 | BCLBeaconCtrl - the main interface for interaction with the Client API. You'll use it to authenticate your mobile application against the backend, fetch beacon and action configurations, respond to beacon events etc. 30 | 31 | BCLBeaconCtrlAdmin - the main interface for interaction with the S2S API. You'll use it to authorize as an admin user, create beacons, zones and actions, update them, etc. 32 | 33 | BCLRange - the BeaconCtrl SDK class that corresponds to your phisical beacons. You'll use it to get information about your beacons and update it using BCLBeaconCtrlAdmin 34 | 35 | BCLZone - zone corresponds to a group of beacons in BeaconCtrl. You can use them to describe larger phisical areas, covered with many beacons. Other than that, zones behave similarly to beacons, e.g. you can also define actions for them. BCLZone is the interface that describes zones in BeaconCtrl. You'll use it to retrieve information about zones and change it using BCLBeaconCtrlAdmin 36 | 37 | BCLAction - this is the BeaconCtrl class that corresponds to actions that you can assign to your beacons or zones. There are several types of actions, some of them are handled automatically by the SDK (but you can always override the default behavior), some are left for the developer to handle. You can use this class to get information about your actions, e.g. to show them to your mobile users 38 | 39 | BCLConfiguration - each mobile application has a configurations of beacons, zones and actions that it uses. This is the class that describes such an app configuration. You'll use it to get detailed information about your configuration, e.g. the number of beacons (and their details information) it interacts with, etc. 40 | 41 | BCLBeaconCtrlDelegate - this is a protocol that you'll implement in your interfaces to respond to BeaconControl SDK events. You'll get called each time an action is just about to be triggered, when the closest beacon or the current zone have changed, etc. You can also use this protocol to let the SDK know, which exact actions you want it to handle automatically and which you want to deal with on your own. 42 | 43 | 44 | ###Beacons Infrastructure Setup 45 | 46 | 1. Create your BeaconControl account at www.beaconctrl.com 47 | 2. Download the BeaconControl mobile application from the App Store 48 | 3. Log in to the application using your e-mail and password 49 | 4. Add your beacons using the application or BeaconControl Admin Panel (UUID, Minor i Major numbers are essential to identify your beacons and are provided by their producer) 50 | 5. Use BeaconControl test notifications to check your setup on the BeaconControl mobile application 51 | 6. Create a folder of your new application using BeaconControl Admin Panel (Applications) 52 | 7. Copy the automatically generatedClient ID and Client Secret from the application settings in the Admin Panel 53 | 8. Follow the below SDK integration instructions to start interacting with your beacons in your new mobile application 54 | 55 | 56 | ###Beacon OS iOS SDK Integration 57 | 58 | 1. It's easiest to integrate BeaconControl iOS SDK using CocoaPods. The name of the pod is just "BeaconControl" 59 | 2. Add ``NSLocationWhenInUseUsageDescription`` and ``NSLocationAlwaysUsageDescription`` keys to project’s Info.plist file. 60 | 3. In case of a self-hosted BeaconControl environment, you'll need to add the ``BCLBaseURLAPI`` key to project's Info.plist file in order to override the default base url. 61 | 4. Import BeaconControl iOS SDK headers into project’s source code and create a variable or property which will keep strong reference to ``BCLBeaconCtrl`` or ``BCLBeaconCtrlAdmin`` object. 62 | 5. Initialise BeaconControl object: 63 | ````objc 64 | [BCLBeaconCtrl setupBeaconCtrlWithClientId: 65 | clientSecret: 66 | userId:email 67 | pushEnvironment: 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 |
95 |
96 |
97 |
98 |

BCLConfiguration Class Reference

99 | 100 | 101 |
102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
Inherits fromBCLEncodableObject : NSObject
Declared inBCLConfiguration.h
BCLConfiguration.m
110 | 111 | 112 | 113 | 114 |
115 | 116 |

Overview

117 |

A BCLConfiguration object represents a beacon, zone, actions and extensions configuration relevant for a given BeaconCtrl Application, fetched from the BeaconCtrl Client API and is kept as a reference in 118 | the BCLBeaconCtrl singleton

119 |
120 | 121 | 122 | 123 | 124 | 125 |
126 | 127 | 128 | 129 | 130 |

Properties

131 | 132 |
133 |
134 | 135 |

  extensions 136 |

137 | 138 |
139 |
140 | 141 |
142 | 143 | 144 |
145 |

Fetched extensions. Set of initialized instances of objects.

146 |
147 | 148 | 149 | 150 |
@property (strong, nonatomic, readonly) NSSet<BCLExtension> *extensions
151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 |
167 |

Declared In

168 |

BCLConfiguration.h

169 |
170 | 171 | 172 |
173 |
174 |
175 | 176 |

  beacons 177 |

178 | 179 |
180 |
181 | 182 |
183 | 184 | 185 |
186 |

Fetched beacons. A set of CTLBeacon objects

187 |
188 | 189 | 190 | 191 |
@property (strong, nonatomic, readonly) NSSet *beacons
192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 |
208 |

Declared In

209 |

BCLConfiguration.h

210 |
211 | 212 | 213 |
214 |
215 |
216 | 217 |

  zones 218 |

219 | 220 |
221 |
222 | 223 |
224 | 225 | 226 |
227 |

Fetched zones. A set of CTLZone objects

228 |
229 | 230 | 231 | 232 |
@property (strong, nonatomic, readonly) NSSet *zones
233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 |
249 |

Declared In

250 |

BCLConfiguration.h

251 |
252 | 253 | 254 |
255 |
256 |
257 | 258 |

  kontaktIOAPIKey 259 |

260 | 261 |
262 |
263 | 264 |
265 | 266 | 267 |
268 |

Kontakt.io API key or nil if the kontakt.io add-on is not switched on

269 |
270 | 271 | 272 | 273 |
@property (nonatomic, copy, readonly) NSString *kontaktIOAPIKey
274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 |
290 |

Declared In

291 |

BCLConfiguration.h

292 |
293 | 294 | 295 |
296 |
297 |
298 |
299 | 300 | 301 | 302 |

Methods

303 | 304 |
305 |
306 | 307 |

– initWithJSON: 308 |

309 | 310 |
311 |
312 | 313 |
314 | 315 | 316 |
317 |

inits a BCLConfiguration object with jsonData fetched from the backend

318 |
319 | 320 | 321 | 322 |
- (instancetype)initWithJSON:(NSData *)jsonData
323 | 324 | 325 | 326 |
327 |

Parameters

328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 |
jsonData

An NSData object that contains json with a configuration representation fetched from the backend

336 |
337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 |
351 |

Declared In

352 |

BCLConfiguration.h

353 |
354 | 355 | 356 |
357 |
358 |
359 | 360 |

+ classForName:protocol:selector: 361 |

362 | 363 |
364 |
365 | 366 |
367 | 368 | 369 |
370 |

Finds a class with a given name (found by calling a given selector on a class) and protocol

371 |
372 | 373 | 374 | 375 |
+ (Class)classForName:(NSString *)name protocol:(Protocol *)protocol selector:(SEL)nameSelector
376 | 377 | 378 | 379 |
380 |

Parameters

381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 |
name

A name of the class to find

protocol

A protocol that the class needs to conform to

nameSelector

A selector that will be called on a class to get its name

399 |
400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 |
414 |

Declared In

415 |

BCLConfiguration.h

416 |
417 | 418 | 419 |
420 |
421 |
422 |
423 | 424 |
425 | 426 | 427 | 428 | 429 | 430 | 431 |
432 | 433 |
434 | 442 |
443 |
444 |
445 |
446 | 447 | 448 | 449 | -------------------------------------------------------------------------------- /help/html/Constants/BCLBeaconCtrlPushEnvironment.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | BCLBeaconCtrlPushEnvironment Constants Reference 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 |

17 | BeaconCtrl 18 |

19 | 20 |

21 | UP-NEXT 22 |

23 | 24 |
25 |
26 | 27 | 57 | 58 |
59 |
60 |
61 |
62 |

BCLBeaconCtrlPushEnvironment Constants Reference

63 | 64 | 65 |
66 | 67 | 68 | 69 | 70 |
Declared inBCLBeaconCtrl.h
71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |

BCLBeaconCtrlPushEnvironment

79 | 80 | 81 |
82 |

@typedef BCLBeaconCtrlPushEnvironment

83 |
84 | 85 | 86 |
87 | 88 | 89 |

Definition

90 | typedef NS_ENUM(NSUInteger, BCLBeaconCtrlPushEnvironment ) {
91 | 92 |    BCLBeaconCtrlPushEnvironmentNone,
93 | 94 |    BCLBeaconCtrlPushEnvironmentSandbox,
95 | 96 |    BCLBeaconCtrlPushEnvironmentProduction,
97 | 98 | };
99 | 100 |
101 | 102 |
103 |

Constants

104 |
105 | 106 |
BCLBeaconCtrlPushEnvironmentNone
107 |
108 | 109 | 110 |

A list of possible push environments 111 | @constant BCLBeaconCtrlPushEnvironmentNone is for application builds that don’t have any push environment set up 112 | @constant BCLBeaconCtrlPushEnvironmentSandbox is for debug builds of applications with set up sandbox push environments 113 | @constant BCLBeaconCtrlPushEnvironmentProduction is for production builds of applications with set up production push environments

114 | 115 | 116 | 117 | 118 | 119 | 120 |

121 | Declared In BCLBeaconCtrl.h. 122 |

123 | 124 |
125 | 126 |
BCLBeaconCtrlPushEnvironmentSandbox
127 |
128 | 129 | 130 |

A list of possible push environments 131 | @constant BCLBeaconCtrlPushEnvironmentNone is for application builds that don’t have any push environment set up 132 | @constant BCLBeaconCtrlPushEnvironmentSandbox is for debug builds of applications with set up sandbox push environments 133 | @constant BCLBeaconCtrlPushEnvironmentProduction is for production builds of applications with set up production push environments

134 | 135 | 136 | 137 | 138 | 139 | 140 |

141 | Declared In BCLBeaconCtrl.h. 142 |

143 | 144 |
145 | 146 |
BCLBeaconCtrlPushEnvironmentProduction
147 |
148 | 149 | 150 |

A list of possible push environments 151 | @constant BCLBeaconCtrlPushEnvironmentNone is for application builds that don’t have any push environment set up 152 | @constant BCLBeaconCtrlPushEnvironmentSandbox is for debug builds of applications with set up sandbox push environments 153 | @constant BCLBeaconCtrlPushEnvironmentProduction is for production builds of applications with set up production push environments

154 | 155 | 156 | 157 | 158 | 159 | 160 |

161 | Declared In BCLBeaconCtrl.h. 162 |

163 | 164 |
165 | 166 |
167 |
168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 |
177 |

Declared In

178 |

BCLBeaconCtrl.h

179 |
180 | 181 | 182 | 183 | 184 | 185 |
186 | 187 |
188 | 196 |
197 |
198 |
199 |
200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /help/html/Constants/BCLEventType.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | BCLEventType Constants Reference 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 |

17 | BeaconCtrl 18 |

19 | 20 |

21 | UP-NEXT 22 |

23 | 24 |
25 |
26 | 27 | 57 | 58 |
59 |
60 |
61 |
62 |

BCLEventType Constants Reference

63 | 64 | 65 |
66 | 67 | 68 | 69 | 70 |
Declared inBCLTypes.h
71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |

BCLEventType

79 | 80 |
81 | 82 | 83 |

Definition

84 | typedef NS_ENUM(NSInteger, BCLEventType ) {
85 | 86 |    BCLEventTypeUnknown = 0,
87 | 88 |    BCLEventTypeEnter = 1,
89 | 90 |    BCLEventTypeLeave = 2,
91 | 92 |    BCLEventTypeRangeImmediate = 3,
93 | 94 |    BCLEventTypeRangeNear = 4,
95 | 96 |    BCLEventTypeRangeFar = 5,
97 | 98 |    BCLEventTypeDwellTime = 6,
99 | 100 |    BCLEventTypeTimer = 7,
101 | 102 | };
103 | 104 |
105 | 106 |
107 |

Constants

108 |
109 | 110 |
BCLEventTypeUnknown
111 |
112 | 113 | 114 |

115 | Declared In BCLTypes.h. 116 |

117 | 118 |
119 | 120 |
BCLEventTypeEnter
121 |
122 | 123 | 124 |

125 | Declared In BCLTypes.h. 126 |

127 | 128 |
129 | 130 |
BCLEventTypeLeave
131 |
132 | 133 | 134 |

135 | Declared In BCLTypes.h. 136 |

137 | 138 |
139 | 140 |
BCLEventTypeRangeImmediate
141 |
142 | 143 | 144 |

145 | Declared In BCLTypes.h. 146 |

147 | 148 |
149 | 150 |
BCLEventTypeRangeNear
151 |
152 | 153 | 154 |

155 | Declared In BCLTypes.h. 156 |

157 | 158 |
159 | 160 |
BCLEventTypeRangeFar
161 |
162 | 163 | 164 |

165 | Declared In BCLTypes.h. 166 |

167 | 168 |
169 | 170 |
BCLEventTypeDwellTime
171 |
172 | 173 | 174 |

175 | Declared In BCLTypes.h. 176 |

177 | 178 |
179 | 180 |
BCLEventTypeTimer
181 |
182 | 183 | 184 |

185 | Declared In BCLTypes.h. 186 |

187 | 188 |
189 | 190 |
191 |
192 | 193 | 194 | 195 | 196 | 197 | 198 |
199 | 200 |
201 | 209 |
210 |
211 |
212 |
213 | 214 | 215 | 216 | -------------------------------------------------------------------------------- /help/html/css/scss/_index.scss: -------------------------------------------------------------------------------- 1 | .index-container { 2 | display: flex; 3 | flex-direction: row; 4 | flex-wrap: wrap; 5 | 6 | @media (max-width: $mobile-max-width) { 7 | flex-direction: column; 8 | } 9 | 10 | .index-column { 11 | flex: 1 1 33%; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /help/html/css/scss/_layout.scss: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | .clear { 6 | clear: both; 7 | } 8 | 9 | .clearfix { 10 | &:before, &:after { 11 | clear: both; 12 | display: table; 13 | content: ""; 14 | } 15 | } 16 | 17 | .xcode .hide-in-xcode { 18 | display: none; 19 | } 20 | 21 | body { 22 | font: 62.5% $body-font; 23 | background: $body-background; 24 | } 25 | 26 | h1, h2, h3 { 27 | font-weight: 300; 28 | color: #808080; 29 | } 30 | 31 | h1 { 32 | font-size: 2em; 33 | color: #000; 34 | } 35 | 36 | h4 { 37 | font-size: 13px; 38 | line-height: 1.5; 39 | margin: 21px 0 0 0; 40 | } 41 | 42 | a { 43 | color: $tint-color; 44 | text-decoration: none; 45 | } 46 | 47 | pre, code { 48 | font-family: $code-font; 49 | word-wrap: break-word; 50 | } 51 | 52 | pre > code, .method-declaration code { 53 | display: inline-block; 54 | font-size: .85em; 55 | padding: 4px 0 4px 10px; 56 | border-left: 5px solid rgba(0, 155, 51, .2); 57 | 58 | &:before { 59 | content: "Objective-C"; 60 | display: block; 61 | 62 | font: 9px/1 $body-font; 63 | color: #009b33; 64 | text-transform: uppercase; 65 | letter-spacing: 2px; 66 | padding-bottom: 6px; 67 | } 68 | } 69 | 70 | pre > code { 71 | font-size: inherit; 72 | } 73 | 74 | table, th, td { 75 | border: 1px solid #e9e9e9; 76 | } 77 | 78 | table { 79 | width: 100%; 80 | } 81 | 82 | th, td { 83 | padding: 7px; 84 | 85 | > :first-child { 86 | margin-top: 0; 87 | } 88 | 89 | > :last-child { 90 | margin-bottom: 0; 91 | } 92 | } 93 | 94 | .container { 95 | @extend .clearfix; 96 | 97 | max-width: 980px; 98 | padding: 0 10px; 99 | margin: 0 auto; 100 | 101 | @media (max-width: $mobile-max-width) { 102 | padding: 0; 103 | } 104 | } 105 | 106 | header { 107 | position: fixed; 108 | top: 0; 109 | left: 0; 110 | width: 100%; 111 | z-index: 2; 112 | 113 | background: #414141; 114 | color: #fff; 115 | font-size: 1.1em; 116 | line-height: 25px; 117 | letter-spacing: .05em; 118 | 119 | #library-title { 120 | float: left; 121 | } 122 | 123 | #developer-home { 124 | float: right; 125 | } 126 | 127 | h1 { 128 | font-size: inherit; 129 | font-weight: inherit; 130 | margin: 0; 131 | } 132 | 133 | p { 134 | margin: 0; 135 | } 136 | 137 | h1, a { 138 | color: inherit; 139 | } 140 | 141 | @media (max-width: $mobile-max-width) { 142 | position: absolute; 143 | 144 | .container { 145 | padding: 0 10px; 146 | } 147 | } 148 | } 149 | 150 | aside { 151 | position: fixed; 152 | top: 25px; 153 | left: 0; 154 | width: 100%; 155 | height: 25px; 156 | z-index: 2; 157 | 158 | font-size: 1.1em; 159 | 160 | @media (max-width: $mobile-max-width) { 161 | position: absolute; 162 | } 163 | 164 | #header-buttons { 165 | background: rgba(255, 255, 255, .8); 166 | margin: 0 1px; 167 | padding: 0; 168 | list-style: none; 169 | text-align: right; 170 | line-height: 32px; 171 | 172 | li { 173 | display: inline-block; 174 | cursor: pointer; 175 | padding: 0 10px; 176 | } 177 | 178 | label, select { 179 | cursor: inherit; 180 | } 181 | 182 | #on-this-page { 183 | position: relative; 184 | 185 | .chevron { 186 | display: inline-block; 187 | width: 14px; 188 | height: 4px; 189 | position: relative; 190 | 191 | .chevy { 192 | background: #878787; 193 | height: 2px; 194 | position: absolute; 195 | width: 10px; 196 | 197 | &.chevron-left { 198 | left: 0; 199 | transform: rotateZ(45deg) scale(0.6); 200 | } 201 | 202 | &.chevron-right { 203 | right: 0; 204 | transform: rotateZ(-45deg) scale(0.6); 205 | } 206 | } 207 | } 208 | 209 | #jump-to { 210 | opacity: 0; 211 | font-size: 16px; 212 | 213 | position: absolute; 214 | top: 5px; 215 | left: 0; 216 | width: 100%; 217 | height: 100%; 218 | } 219 | } 220 | } 221 | } 222 | 223 | article { 224 | margin-top: 25px; 225 | 226 | #content { 227 | @extend .clearfix; 228 | 229 | background: $content-background; 230 | border: 1px solid $content-border; 231 | padding: 15px 25px 30px 25px; 232 | 233 | font-size: 1.4em; 234 | line-height: 1.45; 235 | 236 | position: relative; 237 | 238 | @media (max-width: $mobile-max-width) { 239 | padding: 15px 10px 20px 10px; 240 | } 241 | 242 | .navigation-top { 243 | position: absolute; 244 | top: 15px; 245 | right: 25px; 246 | } 247 | 248 | .title { 249 | margin: 21px 0 0 0; 250 | padding: 15px 0; 251 | } 252 | 253 | p { 254 | color: #414141; 255 | margin: 0 0 15px 0; 256 | } 257 | 258 | th, td { 259 | p:last-child { 260 | margin-bottom: 0; 261 | } 262 | } 263 | 264 | main { 265 | ul { 266 | list-style: none; 267 | margin-left: 24px; 268 | margin-bottom: 12px; 269 | padding: 0; 270 | 271 | li { 272 | position: relative; 273 | padding-left: 1.3em; 274 | 275 | &:before { 276 | content: "\02022"; 277 | 278 | color: #414141; 279 | font-size: 1.08em; 280 | line-height: 1; 281 | 282 | position: absolute; 283 | left: 0; 284 | padding-top: 2px; 285 | } 286 | } 287 | } 288 | } 289 | 290 | footer { 291 | @extend .clearfix; 292 | 293 | .footer-copyright { 294 | margin: 70px 25px 10px 0; 295 | } 296 | 297 | p { 298 | font-size: .71em; 299 | color: #a0a0a0; 300 | } 301 | } 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /help/html/css/scss/_object.scss: -------------------------------------------------------------------------------- 1 | .section-specification { 2 | table { 3 | width: auto; 4 | 5 | th { 6 | text-align: left; 7 | } 8 | } 9 | } 10 | 11 | .method-title { 12 | margin-left: -15px; 13 | margin-bottom: 8px; 14 | transition: margin-left .3s ease-out; 15 | 16 | .section-method.hide & { 17 | margin-left: 0; 18 | } 19 | 20 | code { 21 | font-weight: 400; 22 | font-size: .85em; 23 | } 24 | } 25 | 26 | .method-info { 27 | background: $object-background; 28 | border-bottom: 1px solid $object-border; 29 | margin: 0 -25px; 30 | padding: 20px 25px 0 25px; 31 | transition: height .3s ease-out; 32 | 33 | position: relative; 34 | 35 | .pointy-thing { 36 | background: $content-background; 37 | height: 10px; 38 | border-bottom: 1px solid $object-border; 39 | margin: -20px -25px 16px -25px; 40 | 41 | &:before { 42 | display: inline-block; 43 | content: ""; 44 | 45 | background: $object-background; 46 | border: 1px solid $object-border; 47 | border-bottom: 0; 48 | border-right: 0; 49 | 50 | position: absolute; 51 | left: 21px; 52 | top: 3px; 53 | width: 12px; 54 | height: 12px; 55 | transform: rotate(45deg); 56 | } 57 | } 58 | 59 | .method-subsection { 60 | margin-bottom: 15px; 61 | 62 | .argument-name { 63 | width: 1px; 64 | text-align: right; 65 | 66 | code { 67 | color: #808080; 68 | font-style: italic; 69 | font-weight: 400; 70 | } 71 | } 72 | } 73 | } 74 | 75 | .section-method { 76 | &.hide .method-info { 77 | height: 0 !important; 78 | overflow: hidden; 79 | display: none; 80 | } 81 | 82 | &.hide.animating .method-info { 83 | display: block; 84 | } 85 | 86 | &.animating .method-info { 87 | overflow: hidden; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /help/html/css/scss/_print.scss: -------------------------------------------------------------------------------- 1 | @media print { 2 | body { 3 | background: #fff; 4 | padding: 8px; 5 | } 6 | 7 | header { 8 | position: static; 9 | background: #fff; 10 | color: #000; 11 | } 12 | 13 | aside { 14 | display: none; 15 | } 16 | 17 | .container { 18 | max-width: none; 19 | padding: 0; 20 | } 21 | 22 | article { 23 | margin-top: 0; 24 | 25 | #content { 26 | border: 0; 27 | background: #fff; 28 | padding: 15px 0 0 0; 29 | 30 | .title { 31 | margin-top: 0; 32 | padding-top: 0; 33 | } 34 | } 35 | } 36 | 37 | .method-info { 38 | &, & .pointy-thing { 39 | background: #fff; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /help/html/css/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | $body-font: -apple-system-font, "Helvetica Neue", Helvetica, sans-serif; 2 | $code-font: "Source Code Pro", Monaco, Menlo, Consolas, monospace; 3 | 4 | $body-background: #f2f2f2; 5 | $content-background: #fff; 6 | $content-border: #e9e9e9; 7 | $tint-color: #08c; 8 | $object-background: #f9f9f9; 9 | $object-border: #e9e9e9; 10 | 11 | $mobile-max-width: 650px; 12 | -------------------------------------------------------------------------------- /help/html/css/scss/_xcode.scss: -------------------------------------------------------------------------------- 1 | .xcode { 2 | header, aside { 3 | display: none; 4 | } 5 | 6 | .container { 7 | padding: 0; 8 | } 9 | 10 | article { 11 | margin-top: 0; 12 | 13 | #content { 14 | border: 0; 15 | margin: 0; 16 | } 17 | } 18 | 19 | .method-info { 20 | &, .section-method.hide & { 21 | max-height: auto; 22 | overflow: visible; 23 | 24 | &.hiding { 25 | display: block; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /help/html/css/scss/style.scss: -------------------------------------------------------------------------------- 1 | @import "variables", "normalize", "layout", "index", "object", "print", "xcode"; 2 | -------------------------------------------------------------------------------- /help/html/css/style.css: -------------------------------------------------------------------------------- 1 | html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}*{box-sizing:border-box}.clear{clear:both}.clearfix:before,.container:before,article #content:before,article #content footer:before,.clearfix:after,.container:after,article #content:after,article #content footer:after{clear:both;display:table;content:""}.xcode .hide-in-xcode{display:none}body{font:62.5% -apple-system-font,"Helvetica Neue",Helvetica,sans-serif;background:#f2f2f2}h1,h2,h3{font-weight:300;color:gray}h1{font-size:2em;color:#000}h4{font-size:13px;line-height:1.5;margin:21px 0 0}a{color:#08c;text-decoration:none}pre,code{font-family:Source Code Pro,Monaco,Menlo,Consolas,monospace;word-wrap:break-word}pre>code,.method-declaration code{display:inline-block;font-size:.85em;padding:4px 0 4px 10px;border-left:5px solid rgba(0,155,51,.2)}pre>code:before,.method-declaration code:before{content:"Objective-C";display:block;font:9px/1 -apple-system-font,"Helvetica Neue",Helvetica,sans-serif;color:#009b33;text-transform:uppercase;letter-spacing:2px;padding-bottom:6px}pre>code{font-size:inherit}table,th,td{border:1px solid #e9e9e9}table{width:100%}th,td{padding:7px}th>:first-child,td>:first-child{margin-top:0}th>:last-child,td>:last-child{margin-bottom:0}.container{max-width:980px;padding:0 10px;margin:0 auto}@media(max-width:650px){.container{padding:0}}header{position:fixed;top:0;left:0;width:100%;z-index:2;background:#414141;color:#fff;font-size:1.1em;line-height:25px;letter-spacing:.05em}header #library-title{float:left}header #developer-home{float:right}header h1{font-size:inherit;font-weight:inherit;margin:0}header p{margin:0}header h1,header a{color:inherit}@media(max-width:650px){header{position:absolute}header .container{padding:0 10px}}aside{position:fixed;top:25px;left:0;width:100%;height:25px;z-index:2;font-size:1.1em}aside #header-buttons{background:rgba(255,255,255,.8);margin:0 1px;padding:0;list-style:none;text-align:right;line-height:32px}aside #header-buttons li{display:inline-block;cursor:pointer;padding:0 10px}aside #header-buttons label,aside #header-buttons select{cursor:inherit}aside #header-buttons #on-this-page{position:relative}aside #header-buttons #on-this-page .chevron{display:inline-block;width:14px;height:4px;position:relative}aside #header-buttons #on-this-page .chevron .chevy{background:#878787;height:2px;position:absolute;width:10px}aside #header-buttons #on-this-page .chevron .chevy.chevron-left{left:0;-webkit-transform:rotateZ(45deg) scale(.6);transform:rotateZ(45deg) scale(.6)}aside #header-buttons #on-this-page .chevron .chevy.chevron-right{right:0;-webkit-transform:rotateZ(-45deg) scale(.6);transform:rotateZ(-45deg) scale(.6)}aside #header-buttons #on-this-page #jump-to{opacity:0;filter:alpha(opacity=0);font-size:16px;position:absolute;top:5px;left:0;width:100%;height:100%}article{margin-top:25px}article #content{background:#fff;border:1px solid #e9e9e9;padding:15px 25px 30px;font-size:1.4em;line-height:1.45;position:relative}@media(max-width:650px){article #content{padding:15px 10px 20px}}article #content .navigation-top{position:absolute;top:15px;right:25px}article #content .title{margin:21px 0 0;padding:15px 0}article #content p{color:#414141;margin:0 0 15px}article #content th p:last-child,article #content td p:last-child{margin-bottom:0}article #content main ul{list-style:none;margin-left:24px;margin-bottom:12px;padding:0}article #content main ul li{position:relative;padding-left:1.3em}article #content main ul li:before{content:"\02022";color:#414141;font-size:1.08em;line-height:1;position:absolute;left:0;padding-top:2px}article #content footer .footer-copyright{margin:70px 25px 10px 0}article #content footer p{font-size:.71em;color:#a0a0a0}.index-container{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}@media(max-width:650px){.index-container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}}.index-container .index-column{-webkit-box-flex:1;-webkit-flex:1 1 33%;-ms-flex:1 1 33%;flex:1 1 33%}.section-specification table{width:auto}.section-specification table th{text-align:left}.method-title{margin-left:-15px;margin-bottom:8px;-webkit-transition:margin-left .3s ease-out;transition:margin-left .3s ease-out}.section-method.hide .method-title{margin-left:0}.method-title code{font-weight:400;font-size:.85em}.method-info{background:#f9f9f9;border-bottom:1px solid #e9e9e9;margin:0 -25px;padding:20px 25px 0;-webkit-transition:height .3s ease-out;transition:height .3s ease-out;position:relative}.method-info .pointy-thing{background:#fff;height:10px;border-bottom:1px solid #e9e9e9;margin:-20px -25px 16px}.method-info .pointy-thing:before{display:inline-block;content:"";background:#f9f9f9;border:1px solid #e9e9e9;border-bottom:0;border-right:0;position:absolute;left:21px;top:3px;width:12px;height:12px;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.method-info .method-subsection{margin-bottom:15px}.method-info .method-subsection .argument-name{width:1px;text-align:right}.method-info .method-subsection .argument-name code{color:gray;font-style:italic;font-weight:400}.section-method.hide .method-info{height:0!important;overflow:hidden;display:none}.section-method.hide.animating .method-info{display:block}.section-method.animating .method-info{overflow:hidden}@media print{body{background:#fff;padding:8px}header{position:static;background:#fff;color:#000}aside{display:none}.container{max-width:none;padding:0}article{margin-top:0}article #content{border:0;background:#fff;padding:15px 0 0}article #content .title{margin-top:0;padding-top:0}.method-info,.method-info .pointy-thing{background:#fff}}.xcode header,.xcode aside{display:none}.xcode .container{padding:0}.xcode article{margin-top:0}.xcode article #content{border:0;margin:0}.xcode .section-method.hide .method-info,.xcode .section-method.hide.animating .method-info,.xcode .section-method.animating .method-info{height:auto!important;overflow:visible;display:block}.xcode .section-method.hide .method-title{margin-left:-15px} 2 | /*# sourceMappingURL=to.css.map */ -------------------------------------------------------------------------------- /help/html/hierarchy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | BeaconCtrl Hierarchy 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 |

17 | BeaconCtrl 18 |

19 | 20 |

21 | UP-NEXT 22 |

23 | 24 |
25 |
26 | 27 | 36 | 37 |
38 |
39 |
40 |
41 |

BeaconCtrl Hierarchy

42 | 43 | 44 |
45 |

Class Hierarchy

46 | 47 | 82 | 83 |
84 | 85 | 86 | 87 |
88 | 89 |

Protocol References

90 | 95 | 96 | 97 |

Constant References

98 | 105 | 106 | 107 |
108 | 109 | 110 |
111 | 119 |
120 |
121 |
122 |
123 |
124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /help/html/img/button_bar_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upnext/BeaconControl_iOS_SDK/86bfe82f8e8fbacdc711e27c1bedb39cf73c5cd7/help/html/img/button_bar_background.png -------------------------------------------------------------------------------- /help/html/img/disclosure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upnext/BeaconControl_iOS_SDK/86bfe82f8e8fbacdc711e27c1bedb39cf73c5cd7/help/html/img/disclosure.png -------------------------------------------------------------------------------- /help/html/img/disclosure_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upnext/BeaconControl_iOS_SDK/86bfe82f8e8fbacdc711e27c1bedb39cf73c5cd7/help/html/img/disclosure_open.png -------------------------------------------------------------------------------- /help/html/img/library_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upnext/BeaconControl_iOS_SDK/86bfe82f8e8fbacdc711e27c1bedb39cf73c5cd7/help/html/img/library_background.png -------------------------------------------------------------------------------- /help/html/img/title_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upnext/BeaconControl_iOS_SDK/86bfe82f8e8fbacdc711e27c1bedb39cf73c5cd7/help/html/img/title_background.png -------------------------------------------------------------------------------- /help/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | BeaconCtrl Reference 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 |

17 | BeaconCtrl 18 |

19 | 20 |

21 | UP-NEXT 22 |

23 | 24 |
25 |
26 | 27 | 36 | 37 |
38 |
39 |
40 |
41 |

BeaconCtrl Reference

42 | 43 | 44 | 45 |
46 | 47 | 48 | 49 |
50 |

Class References

51 | 68 |
69 | 70 | 71 | 72 |
73 | 74 |

Protocol References

75 | 80 | 81 | 82 | 83 |

Constant References

84 | 91 | 92 | 93 | 94 |
95 | 96 |
97 | 98 |
99 | 107 |
108 |
109 |
110 |
111 |
112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /help/html/js/script.js: -------------------------------------------------------------------------------- 1 | function $() { 2 | return document.querySelector.apply(document, arguments); 3 | } 4 | 5 | if (navigator.userAgent.indexOf("Xcode") != -1) { 6 | document.documentElement.classList.add("xcode"); 7 | } 8 | 9 | var jumpTo = $("#jump-to"); 10 | 11 | if (jumpTo) { 12 | jumpTo.addEventListener("change", function(e) { 13 | location.hash = this.options[this.selectedIndex].value; 14 | }); 15 | } 16 | 17 | function hashChanged() { 18 | if (/^#\/\/api\//.test(location.hash)) { 19 | var element = document.querySelector("a[name='" + location.hash.substring(1) + "']"); 20 | 21 | if (!element) { 22 | return; 23 | } 24 | 25 | element = element.parentNode; 26 | 27 | element.classList.remove("hide"); 28 | fixScrollPosition(element); 29 | } 30 | } 31 | 32 | function fixScrollPosition(element) { 33 | var scrollTop = element.offsetTop - 150; 34 | document.documentElement.scrollTop = scrollTop; 35 | document.body.scrollTop = scrollTop; 36 | } 37 | 38 | [].forEach.call(document.querySelectorAll(".section-method"), function(element) { 39 | element.classList.add("hide"); 40 | 41 | element.querySelector(".method-title a").addEventListener("click", function(e) { 42 | var info = element.querySelector(".method-info"), 43 | infoContainer = element.querySelector(".method-info-container"); 44 | 45 | element.classList.add("animating"); 46 | info.style.height = (infoContainer.clientHeight + 40) + "px"; 47 | fixScrollPosition(element); 48 | element.classList.toggle("hide"); 49 | if (element.classList.contains("hide")) { 50 | e.preventDefault(); 51 | } 52 | setTimeout(function() { 53 | element.classList.remove("animating"); 54 | }, 300); 55 | }); 56 | }); 57 | 58 | window.addEventListener("hashchange", hashChanged); 59 | hashChanged(); 60 | --------------------------------------------------------------------------------