├── .gitignore ├── LICENSE ├── README.md ├── RegionMonitoringSwift.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── RegionMonitoringSwift ├── AppDelegate.swift ├── Base.lproj │ └── Main.storyboard ├── BeaconManager.swift ├── CircleView.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── appIconIPad@1x.png │ │ ├── appIconIPad@2x.png │ │ └── appIconIPhone@2x.png │ ├── LaunchImage.launchimage │ │ └── Contents.json │ ├── beacons │ │ ├── beaconBlue.imageset │ │ │ ├── Contents.json │ │ │ └── beacon_blue.png │ │ ├── beaconGreen.imageset │ │ │ ├── Contents.json │ │ │ └── beacon_teal.png │ │ └── beaconPurple.imageset │ │ │ ├── Contents.json │ │ │ └── beacon_purple.png │ └── icons │ │ ├── menuIcon.imageset │ │ ├── Contents.json │ │ └── delta.png │ │ └── scanIcon.imageset │ │ ├── Contents.json │ │ └── scan.png ├── Info.plist ├── Utils.swift └── ViewController.swift ├── RegionMonitoringSwiftTests ├── Info.plist └── RegionMonitoringSwiftTests.swift ├── screenshot_region_monitoring_both.png ├── screenshot_region_monitoring_none.png └── screenshot_region_monitoring_purple.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * ---------------------------------------------------------------------------- 3 | * "MODIFIED BEER-WARE LICENSE" (Revision 42.3154): 4 | * wrote this file. As long as you retain this notice you 5 | * can do whatever you want with this stuff. 6 | * 7 | * Also, please don't abuse the shamelessly stolen artwork too much. Give them 8 | * credit at the very least! 9 | * ---------------------------------------------------------------------------- 10 | */ 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Region Monitoring (in Swift) 2 | ===================== 3 | 4 | iBeacon region monitoring, in Swift. The app uses two regions, defined by hardcoded iBeacons (purple and green estimote, respectively). The app recognizes the location as being in neither of these regions, either of them or both. It also sends local notifications on entering a region. 5 | 6 | ![purple](screenshot_region_monitoring_purple.png "Purple region") 7 | ![neither](screenshot_region_monitoring_none.png "Neither region") 8 | 9 | ## Design 10 | 11 | The app centers around a singleton BeaconManager class, which tells the app about beacons that have been discovered. This is more or less copied from my triangulation app (https://https://github.com/a34729t/TriangulatorSwift) with the addition or region entry/exit delegate methods. And of course, background mode is enabled, and the background capabilties are enabled (detects BLE accessories, and location updates). Local notificiation and bading is handled in Utils.swift and AppDelegate.swift. 12 | 13 | ## Testing 14 | 15 | This works on an iPhone 4S running iOS 7.1, with Estimote iBeacons. 16 | 17 | ## CoreLocation vs CoreBluetooth 18 | 19 | I am using CoreLocation, which is the only accepted way of interating with iBeacons. CoreBluetooth doesn't give you the background capabilities needed in a distance-aware app. 20 | 21 | CoreLocation gives you iBeacon class (proximity UUID), major and minor (think of them as ways to give an iBeacon a unique ID). CoreBluetooth gives you only a deviceUUID. In fact, the information available to CoreLocation and CoreBluetooth is mutually exclusive- they handle the iBeacon advertisements differently. 22 | 23 | ## Shamelessly Stolen Artwork 24 | 25 | * Estimote iBeacons: From http://www.uidesignbyadam.com/blog/ 26 | 27 | ## Credits 28 | 29 | * People who's artwork I stole (see above) 30 | * Me 31 | -------------------------------------------------------------------------------- /RegionMonitoringSwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 261A7F16195BB1D900718DCD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261A7F15195BB1D900718DCD /* AppDelegate.swift */; }; 11 | 261A7F18195BB1D900718DCD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261A7F17195BB1D900718DCD /* ViewController.swift */; }; 12 | 261A7F1B195BB1D900718DCD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 261A7F19195BB1D900718DCD /* Main.storyboard */; }; 13 | 261A7F1D195BB1D900718DCD /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 261A7F1C195BB1D900718DCD /* Images.xcassets */; }; 14 | 261A7F29195BB1D900718DCD /* RegionMonitoringSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261A7F28195BB1D900718DCD /* RegionMonitoringSwiftTests.swift */; }; 15 | 261A7F39195BB93100718DCD /* BeaconManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261A7F38195BB93100718DCD /* BeaconManager.swift */; }; 16 | 261A7F3C195BB9A900718DCD /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 261A7F3A195BB9A900718DCD /* LICENSE */; }; 17 | 261A7F3D195BB9A900718DCD /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = 261A7F3B195BB9A900718DCD /* README.md */; }; 18 | 261A7F3F195BE69900718DCD /* CircleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 261A7F3E195BE69900718DCD /* CircleView.swift */; }; 19 | 261A7F43195F217D00718DCD /* screenshot_region_monitoring_none.png in Resources */ = {isa = PBXBuildFile; fileRef = 261A7F40195F217D00718DCD /* screenshot_region_monitoring_none.png */; }; 20 | 261A7F44195F217D00718DCD /* screenshot_region_monitoring_purple.png in Resources */ = {isa = PBXBuildFile; fileRef = 261A7F41195F217D00718DCD /* screenshot_region_monitoring_purple.png */; }; 21 | 261A7F45195F217D00718DCD /* screenshot_region_monitoring_both.png in Resources */ = {isa = PBXBuildFile; fileRef = 261A7F42195F217D00718DCD /* screenshot_region_monitoring_both.png */; }; 22 | 26C6DC18195DED2800CDE4DF /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C6DC17195DED2800CDE4DF /* Utils.swift */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | 261A7F23195BB1D900718DCD /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 261A7F08195BB1D900718DCD /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = 261A7F0F195BB1D900718DCD; 31 | remoteInfo = RegionMonitoringSwift; 32 | }; 33 | /* End PBXContainerItemProxy section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 261A7F10195BB1D900718DCD /* RegionMonitoringSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RegionMonitoringSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 261A7F14195BB1D900718DCD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 261A7F15195BB1D900718DCD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 39 | 261A7F17195BB1D900718DCD /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 40 | 261A7F1A195BB1D900718DCD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 41 | 261A7F1C195BB1D900718DCD /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 42 | 261A7F22195BB1D900718DCD /* RegionMonitoringSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RegionMonitoringSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 261A7F27195BB1D900718DCD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 261A7F28195BB1D900718DCD /* RegionMonitoringSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionMonitoringSwiftTests.swift; sourceTree = ""; }; 45 | 261A7F38195BB93100718DCD /* BeaconManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BeaconManager.swift; sourceTree = ""; }; 46 | 261A7F3A195BB9A900718DCD /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 47 | 261A7F3B195BB9A900718DCD /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 48 | 261A7F3E195BE69900718DCD /* CircleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleView.swift; sourceTree = ""; }; 49 | 261A7F40195F217D00718DCD /* screenshot_region_monitoring_none.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screenshot_region_monitoring_none.png; sourceTree = ""; }; 50 | 261A7F41195F217D00718DCD /* screenshot_region_monitoring_purple.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screenshot_region_monitoring_purple.png; sourceTree = ""; }; 51 | 261A7F42195F217D00718DCD /* screenshot_region_monitoring_both.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screenshot_region_monitoring_both.png; sourceTree = ""; }; 52 | 26C6DC17195DED2800CDE4DF /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 261A7F0D195BB1D900718DCD /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | 261A7F1F195BB1D900718DCD /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXFrameworksBuildPhase section */ 71 | 72 | /* Begin PBXGroup section */ 73 | 261A7F07195BB1D900718DCD = { 74 | isa = PBXGroup; 75 | children = ( 76 | 261A7F3A195BB9A900718DCD /* LICENSE */, 77 | 261A7F3B195BB9A900718DCD /* README.md */, 78 | 261A7F40195F217D00718DCD /* screenshot_region_monitoring_none.png */, 79 | 261A7F41195F217D00718DCD /* screenshot_region_monitoring_purple.png */, 80 | 261A7F42195F217D00718DCD /* screenshot_region_monitoring_both.png */, 81 | 261A7F12195BB1D900718DCD /* RegionMonitoringSwift */, 82 | 261A7F25195BB1D900718DCD /* RegionMonitoringSwiftTests */, 83 | 261A7F11195BB1D900718DCD /* Products */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | 261A7F11195BB1D900718DCD /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 261A7F10195BB1D900718DCD /* RegionMonitoringSwift.app */, 91 | 261A7F22195BB1D900718DCD /* RegionMonitoringSwiftTests.xctest */, 92 | ); 93 | name = Products; 94 | sourceTree = ""; 95 | }; 96 | 261A7F12195BB1D900718DCD /* RegionMonitoringSwift */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 261A7F33195BB66A00718DCD /* iBeacon */, 100 | 261A7F32195BB65F00718DCD /* UI */, 101 | 261A7F1C195BB1D900718DCD /* Images.xcassets */, 102 | 261A7F13195BB1D900718DCD /* Supporting Files */, 103 | ); 104 | path = RegionMonitoringSwift; 105 | sourceTree = ""; 106 | }; 107 | 261A7F13195BB1D900718DCD /* Supporting Files */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 261A7F15195BB1D900718DCD /* AppDelegate.swift */, 111 | 261A7F14195BB1D900718DCD /* Info.plist */, 112 | ); 113 | name = "Supporting Files"; 114 | sourceTree = ""; 115 | }; 116 | 261A7F25195BB1D900718DCD /* RegionMonitoringSwiftTests */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 261A7F28195BB1D900718DCD /* RegionMonitoringSwiftTests.swift */, 120 | 261A7F26195BB1D900718DCD /* Supporting Files */, 121 | ); 122 | path = RegionMonitoringSwiftTests; 123 | sourceTree = ""; 124 | }; 125 | 261A7F26195BB1D900718DCD /* Supporting Files */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 261A7F27195BB1D900718DCD /* Info.plist */, 129 | ); 130 | name = "Supporting Files"; 131 | sourceTree = ""; 132 | }; 133 | 261A7F32195BB65F00718DCD /* UI */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 261A7F19195BB1D900718DCD /* Main.storyboard */, 137 | 261A7F17195BB1D900718DCD /* ViewController.swift */, 138 | 261A7F3E195BE69900718DCD /* CircleView.swift */, 139 | 26C6DC17195DED2800CDE4DF /* Utils.swift */, 140 | ); 141 | name = UI; 142 | sourceTree = ""; 143 | }; 144 | 261A7F33195BB66A00718DCD /* iBeacon */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 261A7F38195BB93100718DCD /* BeaconManager.swift */, 148 | ); 149 | name = iBeacon; 150 | sourceTree = ""; 151 | }; 152 | /* End PBXGroup section */ 153 | 154 | /* Begin PBXNativeTarget section */ 155 | 261A7F0F195BB1D900718DCD /* RegionMonitoringSwift */ = { 156 | isa = PBXNativeTarget; 157 | buildConfigurationList = 261A7F2C195BB1D900718DCD /* Build configuration list for PBXNativeTarget "RegionMonitoringSwift" */; 158 | buildPhases = ( 159 | 261A7F0C195BB1D900718DCD /* Sources */, 160 | 261A7F0D195BB1D900718DCD /* Frameworks */, 161 | 261A7F0E195BB1D900718DCD /* Resources */, 162 | ); 163 | buildRules = ( 164 | ); 165 | dependencies = ( 166 | ); 167 | name = RegionMonitoringSwift; 168 | productName = RegionMonitoringSwift; 169 | productReference = 261A7F10195BB1D900718DCD /* RegionMonitoringSwift.app */; 170 | productType = "com.apple.product-type.application"; 171 | }; 172 | 261A7F21195BB1D900718DCD /* RegionMonitoringSwiftTests */ = { 173 | isa = PBXNativeTarget; 174 | buildConfigurationList = 261A7F2F195BB1D900718DCD /* Build configuration list for PBXNativeTarget "RegionMonitoringSwiftTests" */; 175 | buildPhases = ( 176 | 261A7F1E195BB1D900718DCD /* Sources */, 177 | 261A7F1F195BB1D900718DCD /* Frameworks */, 178 | 261A7F20195BB1D900718DCD /* Resources */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | 261A7F24195BB1D900718DCD /* PBXTargetDependency */, 184 | ); 185 | name = RegionMonitoringSwiftTests; 186 | productName = RegionMonitoringSwiftTests; 187 | productReference = 261A7F22195BB1D900718DCD /* RegionMonitoringSwiftTests.xctest */; 188 | productType = "com.apple.product-type.bundle.unit-test"; 189 | }; 190 | /* End PBXNativeTarget section */ 191 | 192 | /* Begin PBXProject section */ 193 | 261A7F08195BB1D900718DCD /* Project object */ = { 194 | isa = PBXProject; 195 | attributes = { 196 | LastUpgradeCheck = 0600; 197 | ORGANIZATIONNAME = "Nicolas Flacco"; 198 | TargetAttributes = { 199 | 261A7F0F195BB1D900718DCD = { 200 | CreatedOnToolsVersion = 6.0; 201 | SystemCapabilities = { 202 | com.apple.BackgroundModes = { 203 | enabled = 1; 204 | }; 205 | }; 206 | }; 207 | 261A7F21195BB1D900718DCD = { 208 | CreatedOnToolsVersion = 6.0; 209 | TestTargetID = 261A7F0F195BB1D900718DCD; 210 | }; 211 | }; 212 | }; 213 | buildConfigurationList = 261A7F0B195BB1D900718DCD /* Build configuration list for PBXProject "RegionMonitoringSwift" */; 214 | compatibilityVersion = "Xcode 3.2"; 215 | developmentRegion = English; 216 | hasScannedForEncodings = 0; 217 | knownRegions = ( 218 | en, 219 | Base, 220 | ); 221 | mainGroup = 261A7F07195BB1D900718DCD; 222 | productRefGroup = 261A7F11195BB1D900718DCD /* Products */; 223 | projectDirPath = ""; 224 | projectRoot = ""; 225 | targets = ( 226 | 261A7F0F195BB1D900718DCD /* RegionMonitoringSwift */, 227 | 261A7F21195BB1D900718DCD /* RegionMonitoringSwiftTests */, 228 | ); 229 | }; 230 | /* End PBXProject section */ 231 | 232 | /* Begin PBXResourcesBuildPhase section */ 233 | 261A7F0E195BB1D900718DCD /* Resources */ = { 234 | isa = PBXResourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | 261A7F44195F217D00718DCD /* screenshot_region_monitoring_purple.png in Resources */, 238 | 261A7F3C195BB9A900718DCD /* LICENSE in Resources */, 239 | 261A7F43195F217D00718DCD /* screenshot_region_monitoring_none.png in Resources */, 240 | 261A7F1B195BB1D900718DCD /* Main.storyboard in Resources */, 241 | 261A7F45195F217D00718DCD /* screenshot_region_monitoring_both.png in Resources */, 242 | 261A7F1D195BB1D900718DCD /* Images.xcassets in Resources */, 243 | ); 244 | runOnlyForDeploymentPostprocessing = 0; 245 | }; 246 | 261A7F20195BB1D900718DCD /* Resources */ = { 247 | isa = PBXResourcesBuildPhase; 248 | buildActionMask = 2147483647; 249 | files = ( 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | /* End PBXResourcesBuildPhase section */ 254 | 255 | /* Begin PBXSourcesBuildPhase section */ 256 | 261A7F0C195BB1D900718DCD /* Sources */ = { 257 | isa = PBXSourcesBuildPhase; 258 | buildActionMask = 2147483647; 259 | files = ( 260 | 261A7F18195BB1D900718DCD /* ViewController.swift in Sources */, 261 | 261A7F3F195BE69900718DCD /* CircleView.swift in Sources */, 262 | 261A7F39195BB93100718DCD /* BeaconManager.swift in Sources */, 263 | 261A7F3D195BB9A900718DCD /* README.md in Sources */, 264 | 261A7F16195BB1D900718DCD /* AppDelegate.swift in Sources */, 265 | 26C6DC18195DED2800CDE4DF /* Utils.swift in Sources */, 266 | ); 267 | runOnlyForDeploymentPostprocessing = 0; 268 | }; 269 | 261A7F1E195BB1D900718DCD /* Sources */ = { 270 | isa = PBXSourcesBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | 261A7F29195BB1D900718DCD /* RegionMonitoringSwiftTests.swift in Sources */, 274 | ); 275 | runOnlyForDeploymentPostprocessing = 0; 276 | }; 277 | /* End PBXSourcesBuildPhase section */ 278 | 279 | /* Begin PBXTargetDependency section */ 280 | 261A7F24195BB1D900718DCD /* PBXTargetDependency */ = { 281 | isa = PBXTargetDependency; 282 | target = 261A7F0F195BB1D900718DCD /* RegionMonitoringSwift */; 283 | targetProxy = 261A7F23195BB1D900718DCD /* PBXContainerItemProxy */; 284 | }; 285 | /* End PBXTargetDependency section */ 286 | 287 | /* Begin PBXVariantGroup section */ 288 | 261A7F19195BB1D900718DCD /* Main.storyboard */ = { 289 | isa = PBXVariantGroup; 290 | children = ( 291 | 261A7F1A195BB1D900718DCD /* Base */, 292 | ); 293 | name = Main.storyboard; 294 | sourceTree = ""; 295 | }; 296 | /* End PBXVariantGroup section */ 297 | 298 | /* Begin XCBuildConfiguration section */ 299 | 261A7F2A195BB1D900718DCD /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | ALWAYS_SEARCH_USER_PATHS = NO; 303 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 304 | CLANG_CXX_LIBRARY = "libc++"; 305 | CLANG_ENABLE_MODULES = YES; 306 | CLANG_ENABLE_OBJC_ARC = YES; 307 | CLANG_WARN_BOOL_CONVERSION = YES; 308 | CLANG_WARN_CONSTANT_CONVERSION = YES; 309 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 310 | CLANG_WARN_EMPTY_BODY = YES; 311 | CLANG_WARN_ENUM_CONVERSION = YES; 312 | CLANG_WARN_INT_CONVERSION = YES; 313 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 314 | CLANG_WARN_UNREACHABLE_CODE = YES; 315 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 316 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 317 | COPY_PHASE_STRIP = NO; 318 | ENABLE_STRICT_OBJC_MSGSEND = YES; 319 | GCC_C_LANGUAGE_STANDARD = gnu99; 320 | GCC_DYNAMIC_NO_PIC = NO; 321 | GCC_OPTIMIZATION_LEVEL = 0; 322 | GCC_PREPROCESSOR_DEFINITIONS = ( 323 | "DEBUG=1", 324 | "$(inherited)", 325 | ); 326 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 327 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 328 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 329 | GCC_WARN_UNDECLARED_SELECTOR = YES; 330 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 331 | GCC_WARN_UNUSED_FUNCTION = YES; 332 | GCC_WARN_UNUSED_VARIABLE = YES; 333 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 334 | METAL_ENABLE_DEBUG_INFO = YES; 335 | ONLY_ACTIVE_ARCH = YES; 336 | SDKROOT = iphoneos; 337 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 338 | TARGETED_DEVICE_FAMILY = "1,2"; 339 | }; 340 | name = Debug; 341 | }; 342 | 261A7F2B195BB1D900718DCD /* Release */ = { 343 | isa = XCBuildConfiguration; 344 | buildSettings = { 345 | ALWAYS_SEARCH_USER_PATHS = NO; 346 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 347 | CLANG_CXX_LIBRARY = "libc++"; 348 | CLANG_ENABLE_MODULES = YES; 349 | CLANG_ENABLE_OBJC_ARC = YES; 350 | CLANG_WARN_BOOL_CONVERSION = YES; 351 | CLANG_WARN_CONSTANT_CONVERSION = YES; 352 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 353 | CLANG_WARN_EMPTY_BODY = YES; 354 | CLANG_WARN_ENUM_CONVERSION = YES; 355 | CLANG_WARN_INT_CONVERSION = YES; 356 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 357 | CLANG_WARN_UNREACHABLE_CODE = YES; 358 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 359 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 360 | COPY_PHASE_STRIP = YES; 361 | ENABLE_NS_ASSERTIONS = NO; 362 | ENABLE_STRICT_OBJC_MSGSEND = YES; 363 | GCC_C_LANGUAGE_STANDARD = gnu99; 364 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 365 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 366 | GCC_WARN_UNDECLARED_SELECTOR = YES; 367 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 368 | GCC_WARN_UNUSED_FUNCTION = YES; 369 | GCC_WARN_UNUSED_VARIABLE = YES; 370 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 371 | METAL_ENABLE_DEBUG_INFO = NO; 372 | SDKROOT = iphoneos; 373 | TARGETED_DEVICE_FAMILY = "1,2"; 374 | VALIDATE_PRODUCT = YES; 375 | }; 376 | name = Release; 377 | }; 378 | 261A7F2D195BB1D900718DCD /* Debug */ = { 379 | isa = XCBuildConfiguration; 380 | buildSettings = { 381 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 382 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 383 | INFOPLIST_FILE = RegionMonitoringSwift/Info.plist; 384 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 385 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 386 | PRODUCT_NAME = "$(TARGET_NAME)"; 387 | }; 388 | name = Debug; 389 | }; 390 | 261A7F2E195BB1D900718DCD /* Release */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 394 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 395 | INFOPLIST_FILE = RegionMonitoringSwift/Info.plist; 396 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 397 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 398 | PRODUCT_NAME = "$(TARGET_NAME)"; 399 | }; 400 | name = Release; 401 | }; 402 | 261A7F30195BB1D900718DCD /* Debug */ = { 403 | isa = XCBuildConfiguration; 404 | buildSettings = { 405 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/RegionMonitoringSwift.app/RegionMonitoringSwift"; 406 | FRAMEWORK_SEARCH_PATHS = ( 407 | "$(SDKROOT)/Developer/Library/Frameworks", 408 | "$(inherited)", 409 | ); 410 | GCC_PREPROCESSOR_DEFINITIONS = ( 411 | "DEBUG=1", 412 | "$(inherited)", 413 | ); 414 | INFOPLIST_FILE = RegionMonitoringSwiftTests/Info.plist; 415 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 416 | METAL_ENABLE_DEBUG_INFO = YES; 417 | PRODUCT_NAME = "$(TARGET_NAME)"; 418 | TEST_HOST = "$(BUNDLE_LOADER)"; 419 | }; 420 | name = Debug; 421 | }; 422 | 261A7F31195BB1D900718DCD /* Release */ = { 423 | isa = XCBuildConfiguration; 424 | buildSettings = { 425 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/RegionMonitoringSwift.app/RegionMonitoringSwift"; 426 | FRAMEWORK_SEARCH_PATHS = ( 427 | "$(SDKROOT)/Developer/Library/Frameworks", 428 | "$(inherited)", 429 | ); 430 | INFOPLIST_FILE = RegionMonitoringSwiftTests/Info.plist; 431 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 432 | METAL_ENABLE_DEBUG_INFO = NO; 433 | PRODUCT_NAME = "$(TARGET_NAME)"; 434 | TEST_HOST = "$(BUNDLE_LOADER)"; 435 | }; 436 | name = Release; 437 | }; 438 | /* End XCBuildConfiguration section */ 439 | 440 | /* Begin XCConfigurationList section */ 441 | 261A7F0B195BB1D900718DCD /* Build configuration list for PBXProject "RegionMonitoringSwift" */ = { 442 | isa = XCConfigurationList; 443 | buildConfigurations = ( 444 | 261A7F2A195BB1D900718DCD /* Debug */, 445 | 261A7F2B195BB1D900718DCD /* Release */, 446 | ); 447 | defaultConfigurationIsVisible = 0; 448 | defaultConfigurationName = Release; 449 | }; 450 | 261A7F2C195BB1D900718DCD /* Build configuration list for PBXNativeTarget "RegionMonitoringSwift" */ = { 451 | isa = XCConfigurationList; 452 | buildConfigurations = ( 453 | 261A7F2D195BB1D900718DCD /* Debug */, 454 | 261A7F2E195BB1D900718DCD /* Release */, 455 | ); 456 | defaultConfigurationIsVisible = 0; 457 | defaultConfigurationName = Release; 458 | }; 459 | 261A7F2F195BB1D900718DCD /* Build configuration list for PBXNativeTarget "RegionMonitoringSwiftTests" */ = { 460 | isa = XCConfigurationList; 461 | buildConfigurations = ( 462 | 261A7F30195BB1D900718DCD /* Debug */, 463 | 261A7F31195BB1D900718DCD /* Release */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | /* End XCConfigurationList section */ 469 | }; 470 | rootObject = 261A7F08195BB1D900718DCD /* Project object */; 471 | } 472 | -------------------------------------------------------------------------------- /RegionMonitoringSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RegionMonitoringSwift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RegionMonitoringSwift 4 | // 5 | // Created by Nicolas Flacco on 6/25/14. 6 | // Copyright (c) 2014 Nicolas Flacco. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool { 18 | // Override point for customization after application launch. 19 | application.applicationIconBadgeNumber = 0 20 | 21 | return true 22 | } 23 | 24 | func applicationWillResignActive(application: UIApplication) { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 27 | } 28 | 29 | func applicationDidEnterBackground(application: UIApplication) { 30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 32 | } 33 | 34 | func applicationWillEnterForeground(application: UIApplication) { 35 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 36 | application.applicationIconBadgeNumber = 0 37 | } 38 | 39 | func applicationDidBecomeActive(application: UIApplication) { 40 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 41 | } 42 | 43 | func applicationWillTerminate(application: UIApplication) { 44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 45 | } 46 | 47 | // Local notification stuff 48 | 49 | func application(application: UIApplication!, didReceiveLocalNotification notification: UILocalNotification!) { 50 | if (application.applicationState == UIApplicationState.Background) { 51 | application.applicationIconBadgeNumber++ // Application is not running, so badge it 52 | } 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /RegionMonitoringSwift/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /RegionMonitoringSwift/BeaconManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BeaconManager.swift 3 | // RegionMonitoringSwift 4 | // 5 | // Created by Nicolas Flacco on 6/25/14. 6 | // Copyright (c) 2014 Nicolas Flacco. All rights reserved. 7 | // 8 | 9 | import CoreLocation 10 | import UIKit 11 | 12 | // General search criteria for beacons that are broadcasting 13 | let BEACON_PROXIMITY_UUID = NSUUID(UUIDString: "B9407F30-F5F8-466E-AFF9-25556B57FE6D") 14 | 15 | // Beacons are hardcoded into our app so we can easily filter for them in a noisy environment 16 | let BEACON_PURPLE_MAJOR: CLBeaconMajorValue = 15071 17 | let BEACON_PURPLE_MINOR: CLBeaconMinorValue = 10507 18 | let BEACON_GREEN_MAJOR: CLBeaconMajorValue = 45565 19 | let BEACON_GREEN_MINOR: CLBeaconMinorValue = 64072 20 | 21 | protocol BeaconManagerDelegate { 22 | func insideRegion(regionIdentifier: String) 23 | func didEnterRegion(regionIdentifier: String) 24 | func didExitRegion(regionIdentifier: String) 25 | } 26 | 27 | class BeaconManager: NSObject, CLLocationManagerDelegate { 28 | var locationManager: CLLocationManager = CLLocationManager() 29 | let registeredBeaconMajor = [BEACON_GREEN_MAJOR, BEACON_PURPLE_MAJOR] 30 | 31 | let greenRegion: CLBeaconRegion = CLBeaconRegion(proximityUUID:BEACON_PROXIMITY_UUID, major: BEACON_GREEN_MAJOR, identifier:"green") 32 | let purpleRegion: CLBeaconRegion = CLBeaconRegion(proximityUUID: BEACON_PROXIMITY_UUID, major: BEACON_PURPLE_MAJOR, identifier: "purple") 33 | var delegate: BeaconManagerDelegate? 34 | 35 | class var sharedInstance:BeaconManager { 36 | return sharedBeaconManager 37 | } 38 | 39 | init() { 40 | super.init() 41 | locationManager.delegate = self 42 | } 43 | 44 | func start() { 45 | println("BM start"); 46 | locationManager.startMonitoringForRegion(greenRegion, desiredAccuracy: kCLLocationAccuracyBest) 47 | locationManager.startMonitoringForRegion(purpleRegion, desiredAccuracy: kCLLocationAccuracyBest) 48 | } 49 | 50 | func stop() { 51 | println("BM stop"); 52 | locationManager.stopMonitoringForRegion(greenRegion) 53 | locationManager.stopMonitoringForRegion(purpleRegion) 54 | } 55 | 56 | // CLLocationManagerDelegate methods 57 | 58 | func locationManager(manager: CLLocationManager!, didStartMonitoringForRegion region: CLRegion!) { 59 | println("BM didStartMonitoringForRegion") 60 | locationManager.requestStateForRegion(region) // should locationManager be manager? 61 | } 62 | 63 | func locationManager(manager: CLLocationManager, didEnterRegion:CLRegion) { 64 | println("BM didEnterRegion \(didEnterRegion.identifier)") 65 | delegate?.didEnterRegion(didEnterRegion.identifier) 66 | } 67 | 68 | func locationManager(manager: CLLocationManager, didExitRegion:CLRegion) { 69 | println("BM didExitRegion \(didExitRegion.identifier)") 70 | delegate?.didExitRegion(didExitRegion.identifier) 71 | } 72 | 73 | func locationManager(manager: CLLocationManager!, didDetermineState state: CLRegionState, forRegion region: CLRegion!) { 74 | println("BM didDetermineState \(state)"); 75 | 76 | switch state { 77 | case .Inside: 78 | println("BeaconManager:didDetermineState CLRegionState.Inside \(region.identifier)"); 79 | delegate?.insideRegion(region.identifier) 80 | case .Outside: 81 | println("BeaconManager:didDetermineState CLRegionState.Outside"); 82 | case .Unknown: 83 | println("BeaconManager:didDetermineState CLRegionState.Unknown"); 84 | default: 85 | println("BeaconManager:didDetermineState default"); 86 | } 87 | } 88 | 89 | func locationManager(manager: CLLocationManager!, didRangeBeacons beacons: CLBeacon[]!, inRegion region: CLBeaconRegion!) { 90 | // This is needed for region enter/exit 91 | } 92 | } 93 | 94 | 95 | let sharedBeaconManager = BeaconManager() -------------------------------------------------------------------------------- /RegionMonitoringSwift/CircleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CircleView.swift 3 | // RegionMonitoringSwift 4 | // 5 | // Created by Nicolas Flacco on 6/25/14. 6 | // Copyright (c) 2014 Nicolas Flacco. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import QuartzCore 12 | 13 | class CircleView: UIView { 14 | var width: CGFloat = 5.0 15 | 16 | init(frame: CGRect, width: CGFloat) { 17 | super.init(frame: frame) 18 | self.width = width 19 | 20 | // Set view to be transparent 21 | self.opaque = false; 22 | self.backgroundColor = UIColor(white: 0.0, alpha: 0.0); 23 | } 24 | 25 | override func drawRect(rect: CGRect) { 26 | let diameter:CGFloat = min(rect.size.width, rect.size.height) / 1.3 27 | let x:CGFloat = rect.size.width/2 - diameter/2 28 | let y:CGFloat = rect.size.height/2 - diameter/2 29 | 30 | var circleRect = CGRectMake(x, y, diameter, diameter) 31 | var cPath:UIBezierPath = UIBezierPath(rect: circleRect) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "size" : "60x60", 15 | "idiom" : "iphone", 16 | "filename" : "appIconIPhone@2x.png", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "idiom" : "ipad", 21 | "size" : "29x29", 22 | "scale" : "1x" 23 | }, 24 | { 25 | "idiom" : "ipad", 26 | "size" : "29x29", 27 | "scale" : "2x" 28 | }, 29 | { 30 | "idiom" : "ipad", 31 | "size" : "40x40", 32 | "scale" : "1x" 33 | }, 34 | { 35 | "idiom" : "ipad", 36 | "size" : "40x40", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "76x76", 41 | "idiom" : "ipad", 42 | "filename" : "appIconIPad@1x.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "76x76", 47 | "idiom" : "ipad", 48 | "filename" : "appIconIPad@2x.png", 49 | "scale" : "2x" 50 | } 51 | ], 52 | "info" : { 53 | "version" : 1, 54 | "author" : "xcode" 55 | } 56 | } -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/AppIcon.appiconset/appIconIPad@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a34729t/RegionMonitoringSwift/c15bc68b9636fd44db5a3bee1ba75c0998e735ff/RegionMonitoringSwift/Images.xcassets/AppIcon.appiconset/appIconIPad@1x.png -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/AppIcon.appiconset/appIconIPad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a34729t/RegionMonitoringSwift/c15bc68b9636fd44db5a3bee1ba75c0998e735ff/RegionMonitoringSwift/Images.xcassets/AppIcon.appiconset/appIconIPad@2x.png -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/AppIcon.appiconset/appIconIPhone@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a34729t/RegionMonitoringSwift/c15bc68b9636fd44db5a3bee1ba75c0998e735ff/RegionMonitoringSwift/Images.xcassets/AppIcon.appiconset/appIconIPhone@2x.png -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/beacons/beaconBlue.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "beacon_blue.png" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/beacons/beaconBlue.imageset/beacon_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a34729t/RegionMonitoringSwift/c15bc68b9636fd44db5a3bee1ba75c0998e735ff/RegionMonitoringSwift/Images.xcassets/beacons/beaconBlue.imageset/beacon_blue.png -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/beacons/beaconGreen.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "beacon_teal.png" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/beacons/beaconGreen.imageset/beacon_teal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a34729t/RegionMonitoringSwift/c15bc68b9636fd44db5a3bee1ba75c0998e735ff/RegionMonitoringSwift/Images.xcassets/beacons/beaconGreen.imageset/beacon_teal.png -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/beacons/beaconPurple.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "beacon_purple.png" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/beacons/beaconPurple.imageset/beacon_purple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a34729t/RegionMonitoringSwift/c15bc68b9636fd44db5a3bee1ba75c0998e735ff/RegionMonitoringSwift/Images.xcassets/beacons/beaconPurple.imageset/beacon_purple.png -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/icons/menuIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "delta.png" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/icons/menuIcon.imageset/delta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a34729t/RegionMonitoringSwift/c15bc68b9636fd44db5a3bee1ba75c0998e735ff/RegionMonitoringSwift/Images.xcassets/icons/menuIcon.imageset/delta.png -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/icons/scanIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "scan.png" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /RegionMonitoringSwift/Images.xcassets/icons/scanIcon.imageset/scan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a34729t/RegionMonitoringSwift/c15bc68b9636fd44db5a3bee1ba75c0998e735ff/RegionMonitoringSwift/Images.xcassets/icons/scanIcon.imageset/scan.png -------------------------------------------------------------------------------- /RegionMonitoringSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.flacco.projects.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIBackgroundModes 26 | 27 | bluetooth-central 28 | location 29 | 30 | UIMainStoryboardFile 31 | Main 32 | NSLocationWhenInUseUsageDescription 33 | The spirit of stack overflow is coders helping coders 34 | NSLocationAlwaysUsageDescription 35 | I have learned more on stack overflow than anything else 36 | UIRequiredDeviceCapabilities 37 | 38 | armv7 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /RegionMonitoringSwift/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // RegionMonitoringSwift 4 | // 5 | // Created by Nicolas Flacco on 6/27/14. 6 | // Copyright (c) 2014 Nicolas Flacco. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | // Image constants 13 | let menuIconImage = UIImage(named: "menuIcon.png") 14 | let scanIconImage = UIImage(named: "scanIcon.png") 15 | let beaconGreen = UIImage(named: "beaconGreen.png") 16 | let beaconPurple = UIImage(named: "beaconPurple.png") 17 | 18 | // Icons in on/off mode 19 | let menuImgOff = filledImageFrom(image: menuIconImage, UIColor.grayColor()) 20 | let menuImgOn = filledImageFrom(image: menuIconImage, UIColor.whiteColor()) 21 | let scanImgOff = filledImageFrom(image: scanIconImage, UIColor.grayColor()) 22 | let scanImgOn = filledImageFrom(image: scanIconImage, UIColor.whiteColor()) 23 | 24 | // local notifications 25 | let NOTIF_KEY = "WILLIAM_GIBSON_IS_AWESOME" 26 | 27 | func makeMenuButton(#image: UIImage, #left: Bool, #size: CGSize) -> UIButton { 28 | let menuWidth: CGFloat = 150/3; 29 | let menuHeight: CGFloat = 133/3; 30 | let menuPadding: CGFloat = 10; 31 | 32 | let button: UIButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton 33 | if (left) { 34 | button.frame = CGRectMake( menuPadding , size.height - menuHeight - menuPadding, menuWidth, menuHeight); 35 | } else { 36 | button.frame = CGRectMake(size.width - menuWidth - menuPadding, size.height - menuHeight - menuPadding, menuWidth, menuHeight); 37 | } 38 | button.setImage(image, forState: UIControlState.Normal) 39 | 40 | return button; 41 | 42 | } 43 | 44 | // Helpers 45 | // From http://stackoverflow.com/questions/845278/overlaying-a-uiimage-with-a-color?lq=1 46 | func filledImageFrom(#image:UIImage, color:UIColor) -> UIImage { 47 | // begin a new image context, to draw our colored image onto with the right scale 48 | UIGraphicsBeginImageContextWithOptions(image.size, false, UIScreen.mainScreen().scale) 49 | 50 | // get a reference to that context we created 51 | let context:CGContextRef = UIGraphicsGetCurrentContext() 52 | 53 | // set the fill color 54 | color.setFill() 55 | 56 | // translate/flip the graphics context (for transforming from CG* coords to UI* coords 57 | CGContextTranslateCTM(context, 0, image.size.height) 58 | CGContextScaleCTM(context, 1.0, -1.0) 59 | 60 | CGContextSetBlendMode(context, kCGBlendModeColorBurn) 61 | let rect:CGRect = CGRectMake(0, 0, image.size.width, image.size.height) 62 | CGContextDrawImage(context, rect, image.CGImage) 63 | 64 | CGContextSetBlendMode(context, kCGBlendModeSourceIn) 65 | CGContextAddRect(context, rect) 66 | CGContextDrawPath(context,kCGPathFill) 67 | 68 | // generate a new UIImage from the graphics context we drew onto 69 | let coloredImg:UIImage = UIGraphicsGetImageFromCurrentImageContext() 70 | UIGraphicsEndImageContext() 71 | 72 | //return the color-burned image 73 | return coloredImg 74 | } 75 | 76 | func sendNotification(message: String) { 77 | let notification:UILocalNotification = UILocalNotification() 78 | let timeZone = NSTimeZone.defaultTimeZone() 79 | 80 | notification.fireDate = NSDate() 81 | notification.timeZone = timeZone 82 | notification.alertBody = message; 83 | notification.alertAction = "Show"; //creates button that launches app 84 | notification.soundName = UILocalNotificationDefaultSoundName; 85 | notification.applicationIconBadgeNumber = UIApplication.sharedApplication().applicationIconBadgeNumber + 1 86 | 87 | // to pass information with notification 88 | let userDict:Dictionary = [NOTIF_KEY:message] 89 | notification.userInfo = userDict; 90 | UIApplication.sharedApplication().scheduleLocalNotification(notification); 91 | } -------------------------------------------------------------------------------- /RegionMonitoringSwift/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // RegionMonitoringSwift 4 | // 5 | // Created by Nicolas Flacco on 6/25/14. 6 | // Copyright (c) 2014 Nicolas Flacco. All rights reserved. 7 | // 8 | 9 | import CoreLocation 10 | import UIKit 11 | import QuartzCore 12 | 13 | class ViewController: UIViewController, BeaconManagerDelegate { 14 | // Singleton to manage location updates/ranging 15 | var beaconManager: BeaconManager? 16 | var beaconList = Dictionary() // major+minor -> yes or no 17 | 18 | // UI elements 19 | var greenBeaconView: UIImageView? 20 | var purpleBeaconView: UIImageView? 21 | var greenBeaconDualView: UIImageView? 22 | var purpleBeaconDualView: UIImageView? 23 | var circleView: UIView? 24 | var label: UILabel? 25 | var scanButton: UIButton? // this is always on screen 26 | 27 | // General config 28 | var scanMode: Bool = false 29 | 30 | // UI colors and images 31 | // let blueColor:UIColor = UIColor(red: 0.42, green: 0.75, blue: 0.87, alpha: 1.0) // No blue iBeacon 32 | let greenColor:UIColor = UIColor(red: 0.49, green: 0.64, blue: 0.55, alpha: 1.0) 33 | let purpleColor:UIColor = UIColor(red: 0.4, green: 0.34, blue: 0.65, alpha: 1.0) 34 | let bothColor:UIColor = UIColor(red: 0.8, green: 0.2, blue: 0.8, alpha: 1.0) 35 | let circleAlpha:CGFloat = 0.75 36 | let beaconGreen = UIImage(named: "beaconGreen.png") 37 | let beaconPurple = UIImage(named: "beaconPurple.png") 38 | 39 | 40 | override func viewDidLoad() { 41 | println("viewDidLoad") 42 | 43 | super.viewDidLoad() 44 | // Do any additional setup after loading the view 45 | 46 | // Configure UI 47 | self.label = drawLabel(UIColor.whiteColor(), message: "Not in any Region") 48 | self.circleView = drawCircle(UIColor.blackColor()) 49 | 50 | self.greenBeaconView = drawBeacon(beaconGreen) 51 | self.purpleBeaconView = drawBeacon(beaconPurple) 52 | self.greenBeaconDualView = drawBeacon(beaconGreen, left: true) 53 | self.purpleBeaconDualView = drawBeacon(beaconPurple, left: false) 54 | 55 | self.greenBeaconView!.hidden = true 56 | self.purpleBeaconView!.hidden = true 57 | self.greenBeaconDualView!.hidden = true 58 | self.purpleBeaconDualView!.hidden = true 59 | 60 | self.view.addSubview(self.circleView!) 61 | self.view.addSubview(self.greenBeaconView!) 62 | self.view.addSubview(self.purpleBeaconView!) 63 | self.view.addSubview(self.greenBeaconDualView!) // only when we are in both regions 64 | self.view.addSubview(self.purpleBeaconDualView!) // only when we are in both regions 65 | self.view.addSubview(self.label!) 66 | 67 | self.beaconManager = sharedBeaconManager 68 | if !CLLocationManager.locationServicesEnabled() { 69 | // TODO: Alert, once alerts work without crashing app 70 | } 71 | 72 | // Make scan button (antenna) 73 | let scanButton: UIButton = makeMenuButton(image: scanImgOff, left:false, size:self.view.frame.size) 74 | scanButton.addTarget(self, action: "scanButtonClicked:", forControlEvents: UIControlEvents.TouchUpInside) 75 | self.view.addSubview(scanButton) 76 | self.scanButton = scanButton 77 | 78 | } 79 | 80 | override func didReceiveMemoryWarning() { 81 | super.didReceiveMemoryWarning() 82 | // Dispose of any resources that can be recreated. 83 | } 84 | 85 | // Button Handler 86 | func scanButtonClicked(sender: UIButton!) { 87 | println("scanButtonClicked") 88 | 89 | // Change 1) color of button 2) enable/disable beacon manager 90 | if self.scanMode { 91 | self.scanButton!.setImage(scanImgOff, forState: UIControlState.Normal) 92 | self.beaconManager!.stop() 93 | self.beaconManager!.delegate = nil 94 | beaconList = Dictionary() // wipe out dictionary 95 | } else { 96 | self.scanButton!.setImage(scanImgOn, forState: UIControlState.Normal) 97 | self.beaconManager!.start() 98 | self.beaconManager!.delegate = self 99 | } 100 | 101 | // Toggle scan mode 102 | self.scanMode = !self.scanMode 103 | self.setUI() 104 | } 105 | 106 | // BeaconManager Delegates and Helpers 107 | func insideRegion(regionIdentifier: String) { 108 | // println("VC insideRegion \(regionIdentifier)") 109 | self.beaconList[regionIdentifier] = true 110 | self.setUI() 111 | } 112 | 113 | func didEnterRegion(regionIdentifier: String) { 114 | println("VC didEnterRegion \(regionIdentifier)") 115 | self.beaconList[regionIdentifier] = true 116 | self.setUI() 117 | sendNotification("Entered region \(regionIdentifier)") // Only send notification when we enter 118 | } 119 | 120 | func didExitRegion(regionIdentifier: String) { 121 | println("VC didExitRegion \(regionIdentifier)") 122 | self.beaconList[regionIdentifier] = nil 123 | self.setUI() 124 | } 125 | 126 | // Generate the proper UI depending on the color passed in 127 | func setUI() { 128 | // TODO: Refactor 129 | if let green = self.beaconList["green"] { 130 | if let purple = self.beaconList["purple"] { 131 | // green and purple 132 | self.greenBeaconView!.hidden = true 133 | self.purpleBeaconView!.hidden = true 134 | self.greenBeaconDualView!.hidden = false 135 | self.purpleBeaconDualView!.hidden = false 136 | self.circleView!.backgroundColor = bothColor 137 | self.label!.text = "Inside Both Regions" 138 | } else { 139 | // green but not purple 140 | self.greenBeaconView!.hidden = false 141 | self.purpleBeaconView!.hidden = true 142 | self.greenBeaconDualView!.hidden = true 143 | self.purpleBeaconDualView!.hidden = true 144 | self.circleView!.backgroundColor = greenColor 145 | self.label!.text = "Inside Green Region" 146 | } 147 | } else { 148 | if let purple = self.beaconList["purple"] { 149 | // purple but not green 150 | self.greenBeaconView!.hidden = true 151 | self.purpleBeaconView!.hidden = false 152 | self.greenBeaconDualView!.hidden = true 153 | self.purpleBeaconDualView!.hidden = true 154 | self.circleView!.backgroundColor = purpleColor 155 | self.label!.text = "Inside Purple Region" 156 | } else { 157 | // neither green nor purple 158 | self.greenBeaconView!.hidden = true 159 | self.purpleBeaconView!.hidden = true 160 | self.greenBeaconDualView!.hidden = true 161 | self.purpleBeaconDualView!.hidden = true 162 | self.circleView!.backgroundColor = UIColor.blackColor() 163 | self.label!.text = "Not in any Region" 164 | } 165 | } 166 | } 167 | 168 | // View Helpers (move to utils?) 169 | 170 | func drawLabel(color:UIColor, message:String) -> UILabel { 171 | var label = UILabel(frame: CGRectMake(0, 0, 400, 100)) 172 | label.center = CGPointMake(self.view.bounds.size.width / 2, 50) 173 | label.textAlignment = NSTextAlignment.Center 174 | label.text = message 175 | label.textColor = color 176 | label.font = UIFont.systemFontOfSize(30) 177 | return label 178 | } 179 | 180 | func drawCircle(color:UIColor) -> UIView { 181 | 182 | let diameter:CGFloat = min(self.view.bounds.size.width, self.view.bounds.size.height) / 1.1 183 | let x = (self.view.bounds.size.width / 2) - (diameter / 2) 184 | let y = (self.view.bounds.size.height / 1.7) - (diameter / 2) 185 | 186 | let circleView:UIView = UIView(frame: CGRectMake(x, y, diameter, diameter)) 187 | circleView.layer.cornerRadius = diameter / 2; 188 | circleView.backgroundColor = color 189 | circleView.alpha = circleAlpha 190 | 191 | return circleView; 192 | } 193 | 194 | func drawBeacon(image:UIImage) -> UIImageView { 195 | 196 | // let scale:CGFloat = min(self.view.bounds.size.width, self.view.bounds.size.height) 197 | let width = image.size.width 198 | let height = image.size.height 199 | let x = (self.view.bounds.size.width / 2) - (width / 2) 200 | let y = (self.view.bounds.size.height / 1.7) - (height / 2) 201 | 202 | let imageView: UIImageView = UIImageView(frame: CGRect(x: x, y: y, width: width, height: height)) 203 | imageView.image = image 204 | imageView.contentMode = UIViewContentMode.ScaleAspectFit 205 | 206 | return imageView; 207 | } 208 | 209 | func drawBeacon(image:UIImage, left:Bool) -> UIImageView { 210 | 211 | // let scale:CGFloat = min(self.view.bounds.size.width, self.view.bounds.size.height) 212 | let width = image.size.width 213 | let height = image.size.height 214 | var x, y: CGFloat 215 | if left { 216 | x = (self.view.bounds.size.width / 2) - (width / 2) - (width / 2.5) 217 | y = (self.view.bounds.size.height / 1.7) - (height / 2) - (height / 2.5) 218 | } else { 219 | x = (self.view.bounds.size.width / 2) - (width / 2) + (width / 2.5) 220 | y = (self.view.bounds.size.height / 1.7) - (height / 2) + (height / 2.5) 221 | } 222 | 223 | let imageView: UIImageView = UIImageView(frame: CGRect(x: x, y: y, width: width, height: height)) 224 | imageView.image = image 225 | imageView.contentMode = UIViewContentMode.ScaleAspectFit 226 | 227 | return imageView; 228 | } 229 | 230 | } 231 | 232 | -------------------------------------------------------------------------------- /RegionMonitoringSwiftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.flacco.projects.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /RegionMonitoringSwiftTests/RegionMonitoringSwiftTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RegionMonitoringSwiftTests.swift 3 | // RegionMonitoringSwiftTests 4 | // 5 | // Created by Nicolas Flacco on 6/25/14. 6 | // Copyright (c) 2014 Nicolas Flacco. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class RegionMonitoringSwiftTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | XCTAssert(true, "Pass") 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measureBlock() { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /screenshot_region_monitoring_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a34729t/RegionMonitoringSwift/c15bc68b9636fd44db5a3bee1ba75c0998e735ff/screenshot_region_monitoring_both.png -------------------------------------------------------------------------------- /screenshot_region_monitoring_none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a34729t/RegionMonitoringSwift/c15bc68b9636fd44db5a3bee1ba75c0998e735ff/screenshot_region_monitoring_none.png -------------------------------------------------------------------------------- /screenshot_region_monitoring_purple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a34729t/RegionMonitoringSwift/c15bc68b9636fd44db5a3bee1ba75c0998e735ff/screenshot_region_monitoring_purple.png --------------------------------------------------------------------------------