├── Geofence.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ ├── mac-0001.xcuserdatad │ │ ├── IDEFindNavigatorScopes.plist │ │ └── UserInterfaceState.xcuserstate │ │ ├── mac-00021.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── mind.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── mac-0001.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── Geofence.xcscheme │ │ └── xcschememanagement.plist │ ├── mac-00021.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ └── xcschememanagement.plist │ └── mind.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── GeoFanceDemo.xcscheme │ └── xcschememanagement.plist ├── Geofence ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Geofence.entitlements ├── Info.plist ├── Master │ ├── Constants.swift │ ├── CoreData │ │ ├── CoreData.swift │ │ ├── CoreDataContext+Extension.swift │ │ └── CoreDataObject+Extension.swift │ ├── Extension │ │ ├── ExtensionNSObject.swift │ │ └── ExtensionString.swift │ └── KeyboardManager.swift ├── MyLocation.gpx ├── ViewController.swift └── ViewController.xcdatamodeld │ └── ViewController.xcdatamodel │ └── contents ├── GeofenceTests ├── GeofenceTests.swift └── Info.plist ├── GeofenceUITests ├── GeofenceUITests.swift └── Info.plist ├── LICENSE └── README.md /Geofence.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B80CA53E202599A60090A761 /* ExtensionNSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80CA53D202599A60090A761 /* ExtensionNSObject.swift */; }; 11 | B80CA54020259A1D0090A761 /* ExtensionString.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80CA53F20259A1D0090A761 /* ExtensionString.swift */; }; 12 | B80CA54220259A730090A761 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80CA54120259A730090A761 /* Constants.swift */; }; 13 | B80CA54520259BDC0090A761 /* ViewController.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B80CA54320259BDC0090A761 /* ViewController.xcdatamodeld */; }; 14 | B80CA5472025BEF80090A761 /* MyLocation.gpx in Resources */ = {isa = PBXBuildFile; fileRef = B80CA5462025BEF80090A761 /* MyLocation.gpx */; }; 15 | B80CA55C2025D31F0090A761 /* KeyboardManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80CA55B2025D31F0090A761 /* KeyboardManager.swift */; }; 16 | B8C79D492028361000E46EAD /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B8C79D482028361000E46EAD /* MapKit.framework */; }; 17 | B8FE6B65202591600086CA88 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8FE6B64202591600086CA88 /* AppDelegate.swift */; }; 18 | B8FE6B67202591600086CA88 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8FE6B66202591600086CA88 /* ViewController.swift */; }; 19 | B8FE6B6A202591600086CA88 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B8FE6B68202591600086CA88 /* Main.storyboard */; }; 20 | B8FE6B6C202591600086CA88 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B8FE6B6B202591600086CA88 /* Assets.xcassets */; }; 21 | B8FE6B6F202591600086CA88 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B8FE6B6D202591600086CA88 /* LaunchScreen.storyboard */; }; 22 | B8FE6B9A202591CE0086CA88 /* CoreDataObject+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8FE6B96202591CE0086CA88 /* CoreDataObject+Extension.swift */; }; 23 | B8FE6B9B202591CE0086CA88 /* CoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8FE6B97202591CE0086CA88 /* CoreData.swift */; }; 24 | B8FE6B9C202591CE0086CA88 /* CoreDataContext+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8FE6B98202591CE0086CA88 /* CoreDataContext+Extension.swift */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | B80CA53D202599A60090A761 /* ExtensionNSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtensionNSObject.swift; sourceTree = ""; }; 29 | B80CA53F20259A1D0090A761 /* ExtensionString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtensionString.swift; sourceTree = ""; }; 30 | B80CA54120259A730090A761 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 31 | B80CA54420259BDC0090A761 /* ViewController.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = ViewController.xcdatamodel; sourceTree = ""; }; 32 | B80CA5462025BEF80090A761 /* MyLocation.gpx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = MyLocation.gpx; sourceTree = ""; }; 33 | B80CA5482025C1490090A761 /* Geofence.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Geofence.entitlements; sourceTree = ""; }; 34 | B80CA55B2025D31F0090A761 /* KeyboardManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardManager.swift; sourceTree = ""; }; 35 | B8C79D482028361000E46EAD /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; 36 | B8FE6B61202591600086CA88 /* Geofence.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Geofence.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | B8FE6B64202591600086CA88 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 38 | B8FE6B66202591600086CA88 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 39 | B8FE6B69202591600086CA88 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 40 | B8FE6B6B202591600086CA88 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 41 | B8FE6B6E202591600086CA88 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 42 | B8FE6B70202591600086CA88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | B8FE6B79202591600086CA88 /* GeofenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeofenceTests.swift; sourceTree = ""; }; 44 | B8FE6B7B202591600086CA88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | B8FE6B84202591600086CA88 /* GeofenceUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeofenceUITests.swift; sourceTree = ""; }; 46 | B8FE6B86202591600086CA88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | B8FE6B96202591CE0086CA88 /* CoreDataObject+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreDataObject+Extension.swift"; sourceTree = ""; }; 48 | B8FE6B97202591CE0086CA88 /* CoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreData.swift; sourceTree = ""; }; 49 | B8FE6B98202591CE0086CA88 /* CoreDataContext+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreDataContext+Extension.swift"; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | B8FE6B5E202591600086CA88 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | B8C79D492028361000E46EAD /* MapKit.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | B80CA53C2025999C0090A761 /* Extension */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | B80CA53F20259A1D0090A761 /* ExtensionString.swift */, 68 | B80CA53D202599A60090A761 /* ExtensionNSObject.swift */, 69 | ); 70 | path = Extension; 71 | sourceTree = ""; 72 | }; 73 | B8C79D45202835DB00E46EAD /* Frameworks */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | B8C79D482028361000E46EAD /* MapKit.framework */, 77 | ); 78 | name = Frameworks; 79 | sourceTree = ""; 80 | }; 81 | B8FE6B58202591600086CA88 = { 82 | isa = PBXGroup; 83 | children = ( 84 | B8FE6B63202591600086CA88 /* Geofence */, 85 | B8FE6B78202591600086CA88 /* GeofenceTests */, 86 | B8FE6B83202591600086CA88 /* GeofenceUITests */, 87 | B8FE6B62202591600086CA88 /* Products */, 88 | B8C79D45202835DB00E46EAD /* Frameworks */, 89 | ); 90 | sourceTree = ""; 91 | }; 92 | B8FE6B62202591600086CA88 /* Products */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | B8FE6B61202591600086CA88 /* Geofence.app */, 96 | ); 97 | name = Products; 98 | sourceTree = ""; 99 | }; 100 | B8FE6B63202591600086CA88 /* Geofence */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | B80CA5482025C1490090A761 /* Geofence.entitlements */, 104 | B80CA5462025BEF80090A761 /* MyLocation.gpx */, 105 | B8FE6B64202591600086CA88 /* AppDelegate.swift */, 106 | B80CA54320259BDC0090A761 /* ViewController.xcdatamodeld */, 107 | B8FE6B66202591600086CA88 /* ViewController.swift */, 108 | B8FE6B68202591600086CA88 /* Main.storyboard */, 109 | B8FE6B6B202591600086CA88 /* Assets.xcassets */, 110 | B8FE6B6D202591600086CA88 /* LaunchScreen.storyboard */, 111 | B8FE6B70202591600086CA88 /* Info.plist */, 112 | B8FE6B94202591BC0086CA88 /* Master */, 113 | ); 114 | path = Geofence; 115 | sourceTree = ""; 116 | }; 117 | B8FE6B78202591600086CA88 /* GeofenceTests */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | B8FE6B79202591600086CA88 /* GeofenceTests.swift */, 121 | B8FE6B7B202591600086CA88 /* Info.plist */, 122 | ); 123 | path = GeofenceTests; 124 | sourceTree = ""; 125 | }; 126 | B8FE6B83202591600086CA88 /* GeofenceUITests */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | B8FE6B84202591600086CA88 /* GeofenceUITests.swift */, 130 | B8FE6B86202591600086CA88 /* Info.plist */, 131 | ); 132 | path = GeofenceUITests; 133 | sourceTree = ""; 134 | }; 135 | B8FE6B94202591BC0086CA88 /* Master */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | B80CA55B2025D31F0090A761 /* KeyboardManager.swift */, 139 | B80CA54120259A730090A761 /* Constants.swift */, 140 | B80CA53C2025999C0090A761 /* Extension */, 141 | B8FE6B95202591C50086CA88 /* CoreData */, 142 | ); 143 | path = Master; 144 | sourceTree = ""; 145 | }; 146 | B8FE6B95202591C50086CA88 /* CoreData */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | B8FE6B97202591CE0086CA88 /* CoreData.swift */, 150 | B8FE6B98202591CE0086CA88 /* CoreDataContext+Extension.swift */, 151 | B8FE6B96202591CE0086CA88 /* CoreDataObject+Extension.swift */, 152 | ); 153 | path = CoreData; 154 | sourceTree = ""; 155 | }; 156 | /* End PBXGroup section */ 157 | 158 | /* Begin PBXNativeTarget section */ 159 | B8FE6B60202591600086CA88 /* Geofence */ = { 160 | isa = PBXNativeTarget; 161 | buildConfigurationList = B8FE6B89202591600086CA88 /* Build configuration list for PBXNativeTarget "Geofence" */; 162 | buildPhases = ( 163 | B8FE6B5D202591600086CA88 /* Sources */, 164 | B8FE6B5E202591600086CA88 /* Frameworks */, 165 | B8FE6B5F202591600086CA88 /* Resources */, 166 | ); 167 | buildRules = ( 168 | ); 169 | dependencies = ( 170 | ); 171 | name = Geofence; 172 | productName = GeoFanceDemo; 173 | productReference = B8FE6B61202591600086CA88 /* Geofence.app */; 174 | productType = "com.apple.product-type.application"; 175 | }; 176 | /* End PBXNativeTarget section */ 177 | 178 | /* Begin PBXProject section */ 179 | B8FE6B59202591600086CA88 /* Project object */ = { 180 | isa = PBXProject; 181 | attributes = { 182 | LastSwiftUpdateCheck = 0920; 183 | LastUpgradeCheck = 0920; 184 | ORGANIZATIONNAME = Mindinventory; 185 | TargetAttributes = { 186 | B8FE6B60202591600086CA88 = { 187 | CreatedOnToolsVersion = 9.2; 188 | ProvisioningStyle = Manual; 189 | SystemCapabilities = { 190 | com.apple.BackgroundModes = { 191 | enabled = 1; 192 | }; 193 | com.apple.Maps.iOS = { 194 | enabled = 1; 195 | }; 196 | com.apple.Push = { 197 | enabled = 0; 198 | }; 199 | }; 200 | }; 201 | }; 202 | }; 203 | buildConfigurationList = B8FE6B5C202591600086CA88 /* Build configuration list for PBXProject "Geofence" */; 204 | compatibilityVersion = "Xcode 8.0"; 205 | developmentRegion = en; 206 | hasScannedForEncodings = 0; 207 | knownRegions = ( 208 | en, 209 | Base, 210 | ); 211 | mainGroup = B8FE6B58202591600086CA88; 212 | productRefGroup = B8FE6B62202591600086CA88 /* Products */; 213 | projectDirPath = ""; 214 | projectRoot = ""; 215 | targets = ( 216 | B8FE6B60202591600086CA88 /* Geofence */, 217 | ); 218 | }; 219 | /* End PBXProject section */ 220 | 221 | /* Begin PBXResourcesBuildPhase section */ 222 | B8FE6B5F202591600086CA88 /* Resources */ = { 223 | isa = PBXResourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | B8FE6B6F202591600086CA88 /* LaunchScreen.storyboard in Resources */, 227 | B8FE6B6C202591600086CA88 /* Assets.xcassets in Resources */, 228 | B80CA5472025BEF80090A761 /* MyLocation.gpx in Resources */, 229 | B8FE6B6A202591600086CA88 /* Main.storyboard in Resources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXResourcesBuildPhase section */ 234 | 235 | /* Begin PBXSourcesBuildPhase section */ 236 | B8FE6B5D202591600086CA88 /* Sources */ = { 237 | isa = PBXSourcesBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | B8FE6B9B202591CE0086CA88 /* CoreData.swift in Sources */, 241 | B8FE6B67202591600086CA88 /* ViewController.swift in Sources */, 242 | B80CA54520259BDC0090A761 /* ViewController.xcdatamodeld in Sources */, 243 | B8FE6B9A202591CE0086CA88 /* CoreDataObject+Extension.swift in Sources */, 244 | B8FE6B65202591600086CA88 /* AppDelegate.swift in Sources */, 245 | B8FE6B9C202591CE0086CA88 /* CoreDataContext+Extension.swift in Sources */, 246 | B80CA55C2025D31F0090A761 /* KeyboardManager.swift in Sources */, 247 | B80CA54020259A1D0090A761 /* ExtensionString.swift in Sources */, 248 | B80CA53E202599A60090A761 /* ExtensionNSObject.swift in Sources */, 249 | B80CA54220259A730090A761 /* Constants.swift in Sources */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | /* End PBXSourcesBuildPhase section */ 254 | 255 | /* Begin PBXVariantGroup section */ 256 | B8FE6B68202591600086CA88 /* Main.storyboard */ = { 257 | isa = PBXVariantGroup; 258 | children = ( 259 | B8FE6B69202591600086CA88 /* Base */, 260 | ); 261 | name = Main.storyboard; 262 | sourceTree = ""; 263 | }; 264 | B8FE6B6D202591600086CA88 /* LaunchScreen.storyboard */ = { 265 | isa = PBXVariantGroup; 266 | children = ( 267 | B8FE6B6E202591600086CA88 /* Base */, 268 | ); 269 | name = LaunchScreen.storyboard; 270 | sourceTree = ""; 271 | }; 272 | /* End PBXVariantGroup section */ 273 | 274 | /* Begin XCBuildConfiguration section */ 275 | B8FE6B87202591600086CA88 /* Debug */ = { 276 | isa = XCBuildConfiguration; 277 | buildSettings = { 278 | ALWAYS_SEARCH_USER_PATHS = NO; 279 | CLANG_ANALYZER_NONNULL = YES; 280 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 281 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 282 | CLANG_CXX_LIBRARY = "libc++"; 283 | CLANG_ENABLE_MODULES = YES; 284 | CLANG_ENABLE_OBJC_ARC = YES; 285 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 286 | CLANG_WARN_BOOL_CONVERSION = YES; 287 | CLANG_WARN_COMMA = YES; 288 | CLANG_WARN_CONSTANT_CONVERSION = YES; 289 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 290 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 291 | CLANG_WARN_EMPTY_BODY = YES; 292 | CLANG_WARN_ENUM_CONVERSION = YES; 293 | CLANG_WARN_INFINITE_RECURSION = YES; 294 | CLANG_WARN_INT_CONVERSION = YES; 295 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 296 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 297 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 298 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 299 | CLANG_WARN_STRICT_PROTOTYPES = YES; 300 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 301 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 302 | CLANG_WARN_UNREACHABLE_CODE = YES; 303 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 304 | CODE_SIGN_IDENTITY = "iPhone Developer"; 305 | COPY_PHASE_STRIP = NO; 306 | DEBUG_INFORMATION_FORMAT = dwarf; 307 | ENABLE_STRICT_OBJC_MSGSEND = YES; 308 | ENABLE_TESTABILITY = YES; 309 | GCC_C_LANGUAGE_STANDARD = gnu11; 310 | GCC_DYNAMIC_NO_PIC = NO; 311 | GCC_NO_COMMON_BLOCKS = YES; 312 | GCC_OPTIMIZATION_LEVEL = 0; 313 | GCC_PREPROCESSOR_DEFINITIONS = ( 314 | "DEBUG=1", 315 | "$(inherited)", 316 | ); 317 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 318 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 319 | GCC_WARN_UNDECLARED_SELECTOR = YES; 320 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 321 | GCC_WARN_UNUSED_FUNCTION = YES; 322 | GCC_WARN_UNUSED_VARIABLE = YES; 323 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 324 | MTL_ENABLE_DEBUG_INFO = YES; 325 | ONLY_ACTIVE_ARCH = YES; 326 | SDKROOT = iphoneos; 327 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 328 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 329 | }; 330 | name = Debug; 331 | }; 332 | B8FE6B88202591600086CA88 /* Release */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | ALWAYS_SEARCH_USER_PATHS = NO; 336 | CLANG_ANALYZER_NONNULL = YES; 337 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 338 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 339 | CLANG_CXX_LIBRARY = "libc++"; 340 | CLANG_ENABLE_MODULES = YES; 341 | CLANG_ENABLE_OBJC_ARC = YES; 342 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 343 | CLANG_WARN_BOOL_CONVERSION = YES; 344 | CLANG_WARN_COMMA = YES; 345 | CLANG_WARN_CONSTANT_CONVERSION = YES; 346 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 347 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 348 | CLANG_WARN_EMPTY_BODY = YES; 349 | CLANG_WARN_ENUM_CONVERSION = YES; 350 | CLANG_WARN_INFINITE_RECURSION = YES; 351 | CLANG_WARN_INT_CONVERSION = YES; 352 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 353 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 354 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 355 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 356 | CLANG_WARN_STRICT_PROTOTYPES = YES; 357 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 358 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 359 | CLANG_WARN_UNREACHABLE_CODE = YES; 360 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 361 | CODE_SIGN_IDENTITY = "iPhone Developer"; 362 | COPY_PHASE_STRIP = NO; 363 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 364 | ENABLE_NS_ASSERTIONS = NO; 365 | ENABLE_STRICT_OBJC_MSGSEND = YES; 366 | GCC_C_LANGUAGE_STANDARD = gnu11; 367 | GCC_NO_COMMON_BLOCKS = YES; 368 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 369 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 370 | GCC_WARN_UNDECLARED_SELECTOR = YES; 371 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 372 | GCC_WARN_UNUSED_FUNCTION = YES; 373 | GCC_WARN_UNUSED_VARIABLE = YES; 374 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 375 | MTL_ENABLE_DEBUG_INFO = NO; 376 | SDKROOT = iphoneos; 377 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 378 | VALIDATE_PRODUCT = YES; 379 | }; 380 | name = Release; 381 | }; 382 | B8FE6B8A202591600086CA88 /* Debug */ = { 383 | isa = XCBuildConfiguration; 384 | buildSettings = { 385 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 386 | CODE_SIGN_IDENTITY = "iPhone Distribution"; 387 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 388 | CODE_SIGN_STYLE = Manual; 389 | DEVELOPMENT_TEAM = AR98BSB65E; 390 | INFOPLIST_FILE = "$(SRCROOT)/Geofence/Info.plist"; 391 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 392 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 393 | PRODUCT_BUNDLE_IDENTIFIER = com.mi.geofence; 394 | PRODUCT_NAME = "$(TARGET_NAME)"; 395 | PROVISIONING_PROFILE = "8f33188a-1558-45b1-aa5a-3c631cddeac6"; 396 | PROVISIONING_PROFILE_SPECIFIER = "GeofenceDemo 5 feb 2018"; 397 | SWIFT_VERSION = 5.0; 398 | TARGETED_DEVICE_FAMILY = "1,2"; 399 | }; 400 | name = Debug; 401 | }; 402 | B8FE6B8B202591600086CA88 /* Release */ = { 403 | isa = XCBuildConfiguration; 404 | buildSettings = { 405 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 406 | CODE_SIGN_IDENTITY = "iPhone Distribution"; 407 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 408 | CODE_SIGN_STYLE = Manual; 409 | DEVELOPMENT_TEAM = AR98BSB65E; 410 | INFOPLIST_FILE = "$(SRCROOT)/Geofence/Info.plist"; 411 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 412 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 413 | PRODUCT_BUNDLE_IDENTIFIER = com.mi.geofence; 414 | PRODUCT_NAME = "$(TARGET_NAME)"; 415 | PROVISIONING_PROFILE = "8f33188a-1558-45b1-aa5a-3c631cddeac6"; 416 | PROVISIONING_PROFILE_SPECIFIER = "GeofenceDemo 5 feb 2018"; 417 | SWIFT_VERSION = 5.0; 418 | TARGETED_DEVICE_FAMILY = "1,2"; 419 | }; 420 | name = Release; 421 | }; 422 | /* End XCBuildConfiguration section */ 423 | 424 | /* Begin XCConfigurationList section */ 425 | B8FE6B5C202591600086CA88 /* Build configuration list for PBXProject "Geofence" */ = { 426 | isa = XCConfigurationList; 427 | buildConfigurations = ( 428 | B8FE6B87202591600086CA88 /* Debug */, 429 | B8FE6B88202591600086CA88 /* Release */, 430 | ); 431 | defaultConfigurationIsVisible = 0; 432 | defaultConfigurationName = Release; 433 | }; 434 | B8FE6B89202591600086CA88 /* Build configuration list for PBXNativeTarget "Geofence" */ = { 435 | isa = XCConfigurationList; 436 | buildConfigurations = ( 437 | B8FE6B8A202591600086CA88 /* Debug */, 438 | B8FE6B8B202591600086CA88 /* Release */, 439 | ); 440 | defaultConfigurationIsVisible = 0; 441 | defaultConfigurationName = Release; 442 | }; 443 | /* End XCConfigurationList section */ 444 | 445 | /* Begin XCVersionGroup section */ 446 | B80CA54320259BDC0090A761 /* ViewController.xcdatamodeld */ = { 447 | isa = XCVersionGroup; 448 | children = ( 449 | B80CA54420259BDC0090A761 /* ViewController.xcdatamodel */, 450 | ); 451 | currentVersion = B80CA54420259BDC0090A761 /* ViewController.xcdatamodel */; 452 | path = ViewController.xcdatamodeld; 453 | sourceTree = ""; 454 | versionGroupType = wrapper.xcdatamodel; 455 | }; 456 | /* End XCVersionGroup section */ 457 | }; 458 | rootObject = B8FE6B59202591600086CA88 /* Project object */; 459 | } 460 | -------------------------------------------------------------------------------- /Geofence.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Geofence.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Geofence.xcodeproj/project.xcworkspace/xcuserdata/mac-0001.xcuserdatad/IDEFindNavigatorScopes.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Geofence.xcodeproj/project.xcworkspace/xcuserdata/mac-0001.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/iOS-Geofence-Demo/62dd790919b0224fa869f981a2facf3be173de6d/Geofence.xcodeproj/project.xcworkspace/xcuserdata/mac-0001.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Geofence.xcodeproj/project.xcworkspace/xcuserdata/mac-00021.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/iOS-Geofence-Demo/62dd790919b0224fa869f981a2facf3be173de6d/Geofence.xcodeproj/project.xcworkspace/xcuserdata/mac-00021.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Geofence.xcodeproj/project.xcworkspace/xcuserdata/mind.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/iOS-Geofence-Demo/62dd790919b0224fa869f981a2facf3be173de6d/Geofence.xcodeproj/project.xcworkspace/xcuserdata/mind.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Geofence.xcodeproj/xcuserdata/mac-0001.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Geofence.xcodeproj/xcuserdata/mac-0001.xcuserdatad/xcschemes/Geofence.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Geofence.xcodeproj/xcuserdata/mac-0001.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Geofence.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 1 13 | 14 | 15 | SuppressBuildableAutocreation 16 | 17 | B8FE6B60202591600086CA88 18 | 19 | primary 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Geofence.xcodeproj/xcuserdata/mac-00021.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Geofence.xcodeproj/xcuserdata/mac-00021.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Geofence.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Geofence.xcodeproj/xcuserdata/mind.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 40 | 52 | 53 | 54 | 56 | 68 | 69 | 83 | 84 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /Geofence.xcodeproj/xcuserdata/mind.xcuserdatad/xcschemes/GeoFanceDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Geofence.xcodeproj/xcuserdata/mind.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | GeoFanceDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | B8FE6B60202591600086CA88 16 | 17 | primary 18 | 19 | 20 | B8FE6B74202591600086CA88 21 | 22 | primary 23 | 24 | 25 | B8FE6B7F202591600086CA88 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Geofence/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Geofence 4 | 5 | // 6 | // Created by Mind on 03/02/18. 7 | // Copyright © 2018 Mindinventory. All rights reserved. 8 | // 9 | 10 | import UIKit 11 | import CoreLocation 12 | import UserNotifications 13 | 14 | @UIApplicationMain 15 | class AppDelegate: UIResponder, UIApplicationDelegate { 16 | 17 | var window: UIWindow? 18 | let locationManager = CLLocationManager() 19 | 20 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 21 | 22 | let center = UNUserNotificationCenter.current() 23 | center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in 24 | } 25 | self.enableLocationServices() 26 | return true 27 | } 28 | 29 | func applicationWillResignActive(_ application: UIApplication) { 30 | 31 | } 32 | 33 | func applicationDidEnterBackground(_ application: UIApplication) { 34 | 35 | } 36 | 37 | func applicationWillEnterForeground(_ application: UIApplication) { 38 | } 39 | 40 | func applicationDidBecomeActive(_ application: UIApplication) { 41 | } 42 | 43 | func applicationWillTerminate(_ application: UIApplication) { 44 | } 45 | 46 | 47 | func enableLocationServices() // to check status of locations 48 | { 49 | // locationManager.delegate = self 50 | 51 | locationManager.delegate = self 52 | 53 | switch CLLocationManager.authorizationStatus() { 54 | case .notDetermined: 55 | // Request when-in-use authorization initially 56 | locationManager.requestAlwaysAuthorization() 57 | break 58 | 59 | case .restricted, .denied: 60 | print("status restricted, .denied \(CLLocationManager.authorizationStatus())") 61 | // Disable location features 62 | 63 | break 64 | 65 | case .authorizedWhenInUse: 66 | // Enable basic location features 67 | print("status authorizedWhenInUse \(CLLocationManager.authorizationStatus())") 68 | break 69 | 70 | case .authorizedAlways: 71 | print("status authorizedAlways \(CLLocationManager.authorizationStatus())") 72 | // Enable any of your app's location features 73 | 74 | break 75 | @unknown default:break; 76 | } 77 | 78 | } 79 | 80 | // MARK: 81 | // MARK: Fire Local Notifications 82 | func fireNotification(obj:TblGeofence , isEnter:Bool) 83 | { 84 | 85 | print("notification will be triggered in five seconds..Hold on tight") 86 | let content = UNMutableNotificationContent() 87 | content.title = obj.title ?? "TitleMissing" 88 | content.subtitle = obj.msg! + "at lat = \(obj.latitude) long = \(obj.longitude)" 89 | content.body = "you are \(isEnter ? "enetr in" : "Exit from") \(obj.title ?? "TitleMissing")" 90 | content.sound = UNNotificationSound.default 91 | 92 | let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 0.1, repeats: false) 93 | let request = UNNotificationRequest(identifier:obj.identifier! + "++ \(isEnter ? "1" : "0") \(NSDate.timeIntervalSinceReferenceDate)", content: content, trigger: trigger) 94 | 95 | UNUserNotificationCenter.current().delegate = self 96 | UNUserNotificationCenter.current().add(request){(error) in 97 | 98 | if (error != nil){ 99 | 100 | print(error?.localizedDescription ?? "error in notifications") 101 | } 102 | } 103 | } 104 | 105 | 106 | // MARK: 107 | // MARK: GeoFance Management 108 | func registerGeoFance(obj : TblGeofence) { 109 | 110 | if locationManager.monitoredRegions.count >= 20 // check current monitored region Apple allowed only 20 at time 111 | { 112 | locationManager.stopMonitoring(for: locationManager.monitoredRegions.first!) // if have to add new one then need to remove older then 20 else it will not add new one 113 | } 114 | let centerCoordinate: CLLocationCoordinate2D = CLLocationCoordinate2DMake(obj.latitude, obj.longitude) 115 | let region = CLCircularRegion(center: centerCoordinate, radius: obj.range, identifier: obj.identifier!) // provide radius in meter. also provide uniq identifier for your region. 116 | region.notifyOnEntry = true // based on your requirements 117 | region.notifyOnExit = true 118 | 119 | locationManager.startMonitoring(for: region) // to star monitor region 120 | locationManager.requestState(for: region) // that will check if user is already inside location then it will fire notification instantly. 121 | } 122 | 123 | 124 | } 125 | 126 | extension AppDelegate: CLLocationManagerDelegate { 127 | 128 | func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) { 129 | if region is CLCircularRegion { 130 | 131 | // Here region.identifier is identifier which you provide during register geofence region. 132 | // Now you can perform action you want to perfrom. that will same for below didExitRegion method 133 | 134 | let geoFance = (TblGeofence.findOrCreate(dictionary: ["identifier":region.identifier]) as? TblGeofence)! 135 | if (!(geoFance.title?.isEmpty)!) 136 | { 137 | self.fireNotification(obj: geoFance, isEnter: true) 138 | } 139 | } 140 | } 141 | 142 | func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) { 143 | if region is CLCircularRegion { 144 | 145 | let geoFance = (TblGeofence.findOrCreate(dictionary: ["identifier":region.identifier]) as? TblGeofence)! 146 | if (!(geoFance.title?.isEmpty)!) 147 | { 148 | self.fireNotification(obj: geoFance, isEnter: false) 149 | } 150 | 151 | } 152 | } 153 | } 154 | 155 | extension AppDelegate:UNUserNotificationCenterDelegate{ 156 | 157 | func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { 158 | 159 | print("Tapped in notification") 160 | debugPrint("====================================") 161 | } 162 | 163 | //This is key callback to present notification while the app is in foreground 164 | func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { 165 | 166 | print("Notification being triggered") 167 | let strDevides = notification.request.identifier.components(separatedBy: "++ ") 168 | 169 | let geoFance = (TblGeofence.findOrCreate(dictionary: ["identifier":strDevides[0]]) as? TblGeofence)! 170 | 171 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(Int(0))) { 172 | let alert = UIAlertController( 173 | title: "\(geoFance.title ?? "BlankTitleRecevied")", 174 | message: "\(notification.request.content.subtitle) \n \(notification.request.content.body)", 175 | preferredStyle: UIAlertController.Style.alert 176 | ) 177 | 178 | alert.addAction(UIAlertAction(title: "Okey", style: .cancel, handler: { (alert) -> Void in 179 | 180 | })) 181 | 182 | UIApplication.topViewController()?.present(alert, animated: true, completion: nil) 183 | 184 | } 185 | 186 | //You can either present alert ,sound or increase badge while the app is in foreground too with ios 10 187 | //to distinguish between notifications 188 | if notification.request.identifier == "123456"{ 189 | 190 | completionHandler( [.alert,.sound,.badge]) 191 | 192 | } 193 | } 194 | 195 | } 196 | 197 | extension UIApplication { 198 | class func topViewController(base: UIViewController? = (UIApplication.shared.delegate as! AppDelegate).window?.rootViewController) -> UIViewController? { 199 | if let nav = base as? UINavigationController { 200 | return topViewController(base: nav.visibleViewController) 201 | } 202 | if let tab = base as? UITabBarController { 203 | if let selected = tab.selectedViewController { 204 | return topViewController(base: selected) 205 | } 206 | } 207 | if let presented = base?.presentedViewController { 208 | return topViewController(base: presented) 209 | } 210 | return base 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /Geofence/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Geofence/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Geofence/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 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 89 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /Geofence/Geofence.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Geofence/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Geofence 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSLocationAlwaysAndWhenInUseUsageDescription 26 | To send updates according to your location. 27 | NSLocationWhenInUseUsageDescription 28 | To send updates according to your location on forground 29 | UIBackgroundModes 30 | 31 | fetch 32 | location 33 | remote-notification 34 | 35 | UILaunchStoryboardName 36 | LaunchScreen 37 | UIMainStoryboardFile 38 | Main 39 | UIRequiredDeviceCapabilities 40 | 41 | armv7 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | 47 | UISupportedInterfaceOrientations~ipad 48 | 49 | UIInterfaceOrientationPortrait 50 | UIInterfaceOrientationPortraitUpsideDown 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Geofence/Master/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // DemoSwift 4 | // 5 | // Created by mac-0007 on 15/09/16. 6 | // Copyright © 2016 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | fileprivate func < (lhs: T?, rhs: T?) -> Bool { 12 | switch (lhs, rhs) { 13 | case let (l?, r?): 14 | return l < r 15 | case (nil, _?): 16 | return true 17 | default: 18 | return false 19 | } 20 | } 21 | 22 | fileprivate func >= (lhs: T?, rhs: T?) -> Bool { 23 | switch (lhs, rhs) { 24 | case let (l?, r?): 25 | return l >= r 26 | default: 27 | return !(lhs < rhs) 28 | } 29 | } 30 | 31 | //func print(_ items: Any..., separator: String = " ", terminator: String = "\n") 32 | //{ 33 | // 34 | //} 35 | 36 | 37 | let CURRENT_DEVICE = UIDevice.current 38 | 39 | let CScreen = UIScreen.main 40 | 41 | let CScreenBounds = CScreen.bounds 42 | 43 | let CScreenHeight = CScreenBounds.size.height 44 | 45 | let CScreenWidth = CScreenBounds.size.width 46 | 47 | let CScreenCenterX = CScreenWidth / 2.0 48 | 49 | let CScreenCenterY = CScreenHeight / 2.0 50 | 51 | let CScreenCenter = CGPoint(x: CScreenCenterX, y: CScreenCenterY) 52 | 53 | let CBundle = Bundle.main 54 | 55 | let CUserDefaults = UserDefaults.standard 56 | 57 | let CSharedApplication = UIApplication.shared 58 | 59 | let appDelegate = CSharedApplication.delegate as! AppDelegate 60 | 61 | let UserdefaultsKey = "user_auth_token" 62 | 63 | 64 | let GCDMainThread = DispatchQueue.main 65 | 66 | let GCDBackgroundThread = DispatchQueue.global(qos: DispatchQoS.QoSClass.default) 67 | 68 | let GCDBackgroundThreadLowPriority = DispatchQueue.global(qos: DispatchQoS.QoSClass.utility) 69 | 70 | let GCDBackgroundThreadHighPriority = DispatchQueue.global(qos: DispatchQoS.QoSClass.userInteractive) 71 | 72 | 73 | 74 | 75 | 76 | let iOS_NAME = UIDevice.current.systemName 77 | 78 | let iOS_VERSION = UIDevice.current.systemVersion 79 | //let IS_iOS11_OR_LATER = Double(iOS_VERSION) >= 11 80 | 81 | //let IS_iOS10 = Int(Double(iOS_VERSION)!) == 10 82 | //let IS_iOS10_OR_LATER = Double(iOS_VERSION) >= 10 83 | // 84 | //let IS_iOS9 = Int(Double(iOS_VERSION)!) == 9 85 | //let IS_iOS9_OR_LATER = Double(iOS_VERSION) >= 9 86 | 87 | //let IS_iOS8 = Int(Double(iOS_VERSION)!) == 8 88 | //let IS_iOS8_OR_LATER = Double(iOS_VERSION) >= 8 89 | // 90 | //let IS_iOS7 = Int(Double(iOS_VERSION)!) == 7 91 | //let IS_iOS7_OR_LATER = Double(iOS_VERSION) >= 7 92 | // 93 | //let IS_iOS6 = Int(Double(iOS_VERSION)!) == 6 94 | //let IS_iOS6_OR_LATER = Double(iOS_VERSION) >= 6 95 | 96 | 97 | let DEVICE_NAME = CURRENT_DEVICE.name 98 | 99 | let DEVICE_MODEL = CURRENT_DEVICE.model 100 | 101 | let IS_SIMULATOR = (TARGET_IPHONE_SIMULATOR == 1) 102 | 103 | let IS_IPHONE = DEVICE_MODEL.range(of: "iPhone") != nil 104 | 105 | let IS_IPOD = DEVICE_MODEL.range(of: "iPod") != nil 106 | 107 | let IS_IPAD = DEVICE_MODEL.range(of: "iPad") != nil 108 | 109 | 110 | let IS_IPHONE_4 = IS_IPHONE && CScreenHeight == 480 111 | let IS_IPHONE_5 = IS_IPHONE && CScreenHeight == 568 112 | let IS_IPHONE_6 = IS_IPHONE && CScreenHeight == 667 113 | let IS_IPHONE_6P = IS_IPHONE && CScreenHeight == 736 114 | 115 | 116 | let IS_IPAD_MINI = IS_IPAD && CScreenHeight == 512 117 | let IS_IPAD_MINI2 = IS_IPAD && CScreenHeight == 512 118 | let IS_IPAD_AIR = IS_IPAD && CScreenHeight == 1024 119 | let IS_IPAD_PRO = IS_IPAD && CScreenHeight == 1366 120 | 121 | let CBundleID = CBundle.bundleIdentifier 122 | let CBundleInfo = CBundle.infoDictionary 123 | let CAppVersion = CBundleInfo!["CFBundleShortVersionString"] 124 | let CAppBuild = CBundleInfo!["CFBundleVersion"] 125 | let CAppName = CBundleInfo!["CFBundleVersion"] 126 | 127 | let CCacheDirectory = NSHomeDirectory() + "/Library/Caches" 128 | let CDocumentDirectory = NSHomeDirectory() + "/Documents" 129 | let CLimit = "20" as AnyObject 130 | 131 | func IS_IPHONE_SIMULATOR() -> Bool 132 | { 133 | #if (arch(i386) || arch(x86_64)) 134 | return true 135 | #else 136 | return false 137 | #endif 138 | 139 | } 140 | 141 | func SYSTEM_VERSION_LESS_THAN(v: String) -> Any { 142 | return ((UIDevice.current.systemVersion.compare(v, options: .numeric, range: nil, locale: .current)) == .orderedAscending) 143 | } 144 | 145 | func CViewX(_ view:UIView) -> CGFloat { 146 | return view.frame.origin.x 147 | } 148 | 149 | func CViewY(_ view:UIView) -> CGFloat { 150 | return view.frame.origin.y 151 | } 152 | 153 | func CViewWidth(_ view:UIView) -> CGFloat { 154 | return view.frame.size.width 155 | } 156 | 157 | func CViewHeight(_ view:UIView) -> CGFloat { 158 | return view.frame.size.height 159 | } 160 | 161 | func CViewCenter(_ view:UIView) -> CGPoint { 162 | return view.center 163 | } 164 | 165 | func CViewCenterX(_ view:UIView) -> CGFloat { 166 | return CViewCenter(view).x 167 | } 168 | 169 | func CViewCenterY(_ view:UIView) -> CGFloat { 170 | return CViewCenter(view).y 171 | } 172 | 173 | 174 | 175 | 176 | 177 | func CViewSetX(_ view:UIView, x:CGFloat) -> Void { 178 | view.frame = CGRect(x: x, y: CViewY(view), width: CViewWidth(view), height: CViewHeight(view)) 179 | } 180 | 181 | func CViewSetY(_ view:UIView, y:CGFloat) -> Void { 182 | view.frame = CGRect(x: CViewX(view), y: y, width: CViewWidth(view), height: CViewHeight(view)) 183 | } 184 | 185 | func CViewSetWidth(_ view:UIView, width:CGFloat) -> Void { 186 | view.frame = CGRect(x: CViewX(view), y: CViewY(view), width: width, height: CViewHeight(view)) 187 | } 188 | 189 | func CViewSetHeight(_ view:UIView, height:CGFloat) -> Void { 190 | view.frame = CGRect(x: CViewX(view), y: CViewY(view), width: CViewWidth(view), height: height) 191 | } 192 | 193 | func CViewSetOrigin(_ view:UIView, x:CGFloat, y:CGFloat) -> Void { 194 | view.frame = CGRect(x: x, y: y, width: CViewWidth(view), height: CViewHeight(view)) 195 | } 196 | 197 | func CViewSetSize(_ view:UIView, width:CGFloat, height:CGFloat) -> Void { 198 | view.frame = CGRect(x: CViewX(view), y: CViewY(view), width: width, height: height) 199 | } 200 | 201 | func CViewSetFrame(_ view:UIView, x:CGFloat, y:CGFloat, width:CGFloat, height:CGFloat) -> Void { 202 | view.frame = CGRect(x: x, y: y, width: width, height: height) 203 | } 204 | 205 | func CViewSetCenter(_ view:UIView, x:CGFloat, y:CGFloat) -> Void { 206 | view.center = CGPoint(x: x, y: y) 207 | } 208 | 209 | func CViewSetCenterX(_ view:UIView, x:CGFloat) -> Void { 210 | view.center = CGPoint(x: x, y: CViewCenterY(view)) 211 | } 212 | 213 | func CViewSetCenterY(_ view:UIView, y:CGFloat) -> Void { 214 | view.center = CGPoint(x: CViewCenterX(view), y: y) 215 | } 216 | 217 | 218 | 219 | 220 | func CRectX(_ frame:CGRect) -> CGFloat { 221 | return frame.origin.x 222 | } 223 | 224 | func CRectY(_ frame:CGRect) -> CGFloat { 225 | return frame.origin.y 226 | } 227 | 228 | func CRectWidth(_ frame:CGRect) -> CGFloat { 229 | return frame.size.width 230 | } 231 | 232 | func CRectHeight(_ frame:CGRect) -> CGFloat { 233 | return frame.size.height 234 | } 235 | 236 | 237 | func CRGB(r red:CGFloat, g:CGFloat, b:CGFloat) -> UIColor { 238 | return UIColor(red: red/255.0, green: g/255.0, blue: b/255.0, alpha: 1.0) 239 | } 240 | 241 | func CRGBA(r red:CGFloat, g:CGFloat, b:CGFloat, a:CGFloat) -> UIColor { 242 | return UIColor(red: red/255.0, green: g/255.0, blue: b/255.0, alpha: a) 243 | } 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /Geofence/Master/CoreData/CoreData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreData.swift 3 | // DemoSwift 4 | // 5 | // Created by mac-0007 on 03/09/16. 6 | // Copyright © 2016 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 12 | // Consider refactoring the code to use the non-optional operators. 13 | fileprivate func < (lhs: T?, rhs: T?) -> Bool { 14 | switch (lhs, rhs) { 15 | case let (l?, r?): 16 | return l < r 17 | case (nil, _?): 18 | return true 19 | default: 20 | return false 21 | } 22 | } 23 | 24 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 25 | // Consider refactoring the code to use the non-optional operators. 26 | fileprivate func > (lhs: T?, rhs: T?) -> Bool { 27 | switch (lhs, rhs) { 28 | case let (l?, r?): 29 | return l > r 30 | default: 31 | return rhs < lhs 32 | } 33 | } 34 | 35 | 36 | 37 | 38 | //====================================================== 39 | 40 | typealias BlockVoid = () -> Void 41 | typealias BlockManagedObjectEnumeration = (_ managedObject:NSManagedObject, _ object:NSDictionary, _ idx:UInt, _ stop:Bool) -> Void 42 | typealias BlockManagedObjectResult = (_ managedObject:NSManagedObject?, _ error:NSError?) -> Void 43 | typealias BlockArrayResult = (_ objects:NSArray?, _ error:NSError?) -> Void 44 | typealias BlockCountResult = (_ count:Int, _ error:NSError?) -> Void 45 | typealias BlockChangesResult = (_ result:NSArray, _ type:NSManagedObjectChangeType) -> Void 46 | 47 | enum NSManagedObjectChangeType:Int 48 | { 49 | case insert 50 | case update 51 | case delete 52 | case all 53 | } 54 | 55 | enum BlockType:String 56 | { 57 | case observer = "observer" 58 | case block = "block" 59 | } 60 | 61 | class WrapperBlockVoid : NSObject 62 | { 63 | var block : BlockVoid 64 | init(block: @escaping BlockVoid) { 65 | self.block = block 66 | } 67 | } 68 | 69 | class WrapperBlockChangesResult : NSObject 70 | { 71 | var block : BlockChangesResult 72 | init(block: @escaping BlockChangesResult) { 73 | self.block = block 74 | } 75 | } 76 | 77 | //====================================================== 78 | 79 | 80 | 81 | 82 | 83 | class CoreData: NSObject 84 | { 85 | var primaryKeys:NSMutableDictionary? 86 | var arrChangesObserver:NSMutableArray? 87 | 88 | //MARK: 89 | //MARK:- CoreData Variable 90 | 91 | lazy var modelName: String = { 92 | // Default: Model 93 | return "Model" 94 | }() 95 | 96 | lazy var sqliteDatabaseName: String = { 97 | // Default: Data.sqlite 98 | return "Data.sqlite" 99 | }() 100 | 101 | lazy var initializeDatabaseName: String = { 102 | // Not used as default 103 | return "Data" 104 | }() 105 | 106 | lazy var databaseFileDirectory: String = { 107 | // Default: NSHomeDirectory().stringByAppendingString("Documents") 108 | return NSHomeDirectory() + "/Library/Caches" 109 | }() 110 | 111 | lazy var coreDataURL: URL = { 112 | return URL(fileURLWithPath: self.databaseFileDirectory+"/"+self.sqliteDatabaseName) 113 | }() 114 | 115 | 116 | 117 | //MARK: 118 | //MARK:- iCloud Variable 119 | 120 | var iCloudEnabledAppID: String? 121 | 122 | lazy var iCloudDatabaseDirectory: String = { 123 | return "Data.nosync" 124 | }() 125 | 126 | lazy var iCloudLogsDirectory: String = { 127 | return "Logs" 128 | }() 129 | 130 | 131 | 132 | 133 | //MARK: 134 | //MARK:- Core Data stack 135 | 136 | lazy var managedObjectModel: NSManagedObjectModel! = { 137 | // let modelURL = NSBundle.mainBundle().URLForResource(self.modelName, withExtension: "momd")! 138 | // return NSManagedObjectModel(contentsOfURL: modelURL)! 139 | 140 | return NSManagedObjectModel.mergedModel(from: nil) 141 | }() 142 | 143 | lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = { 144 | 145 | var dbURL = self.coreDataURL 146 | 147 | let options : NSMutableDictionary = NSMutableDictionary() 148 | options.setObject(FileProtectionType.none, forKey: FileAttributeKey.protectionKey as NSCopying) 149 | options.setObject(NSNumber(value: true as Bool), forKey: NSMigratePersistentStoresAutomaticallyOption as NSCopying) 150 | options.setObject(NSNumber(value: true as Bool), forKey: NSInferMappingModelAutomaticallyOption as NSCopying) 151 | 152 | if(!FileManager.default.fileExists(atPath: dbURL.path)) 153 | { 154 | let preloadPath = Bundle.main.path(forResource: self.initializeDatabaseName, ofType: "sqlite") 155 | 156 | if((preloadPath != nil) && FileManager.default.fileExists(atPath: preloadPath!)) 157 | { 158 | do { 159 | try FileManager.default.copyItem(at: URL(fileURLWithPath: preloadPath!), to: dbURL) 160 | } catch { 161 | print("Error: Couldn't copy preloaded data \(error)") 162 | } 163 | } 164 | } 165 | 166 | if((self.iCloudEnabledAppID) != nil) 167 | { 168 | let fileManager:FileManager = FileManager.default 169 | let iCloudRoot:URL? = fileManager.url(forUbiquityContainerIdentifier: nil) 170 | 171 | if(iCloudRoot != nil) 172 | { 173 | let strPath:String! = (iCloudRoot?.path)! + ("/"+self.iCloudLogsDirectory) 174 | let iCloudLogsPath:URL = URL(fileURLWithPath: strPath) 175 | 176 | if(!fileManager.fileExists(atPath: strPath)) 177 | { 178 | do { 179 | try fileManager.createDirectory(atPath: strPath, withIntermediateDirectories: true, attributes: nil) 180 | } catch { 181 | print("Error: Creating Database Directory \(error)") 182 | } 183 | } 184 | 185 | let iCloudData:String = strPath + ("/"+self.sqliteDatabaseName) 186 | options.setObject(self.iCloudEnabledAppID!, forKey: NSPersistentStoreUbiquitousContentNameKey as NSCopying) 187 | options.setObject(iCloudLogsPath, forKey: NSPersistentStoreUbiquitousContentURLKey as NSCopying) 188 | 189 | dbURL = URL(fileURLWithPath: iCloudData) 190 | } 191 | } 192 | 193 | let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) 194 | 195 | do { 196 | try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: dbURL, options: options as? [AnyHashable: Any]) 197 | } catch 198 | { 199 | // Report any error we got. 200 | var failureReason = "There was an error creating or loading the application's saved data." 201 | 202 | var dict = [String: AnyObject]() 203 | dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject 204 | dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject 205 | dict[NSUnderlyingErrorKey] = error as NSError 206 | 207 | let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) 208 | print("Unresolved error \(wrappedError), \(wrappedError.userInfo)") 209 | 210 | 211 | // abort() causes the application to generate a crash log and terminate. 212 | // You should not use this function in a shipping application, although it may be useful during development. 213 | #if DEBUG 214 | abort() 215 | #endif 216 | } 217 | 218 | return coordinator 219 | }() 220 | 221 | lazy var mainManagedObjectContext: NSManagedObjectContext = { 222 | var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) 223 | managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator 224 | return managedObjectContext 225 | }() 226 | 227 | lazy var privateManagedObjectContext: NSManagedObjectContext = { 228 | var managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) 229 | managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator 230 | return managedObjectContext 231 | }() 232 | 233 | lazy var childManagedObjectContext: NSManagedObjectContext = { 234 | var managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) 235 | managedObjectContext.parent = self.privateManagedObjectContext 236 | return managedObjectContext 237 | }() 238 | 239 | lazy var newManagedObjectContext: NSManagedObjectContext = { 240 | var managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) 241 | managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator 242 | return managedObjectContext 243 | }() 244 | 245 | 246 | 247 | //MARK: 248 | //MARK:- Singleton Object 249 | 250 | static let sharedInstance = CoreData.init() 251 | 252 | override init() 253 | { 254 | super.init() 255 | 256 | primaryKeys = NSMutableDictionary() 257 | arrChangesObserver = NSMutableArray() 258 | 259 | NotificationCenter.default.addObserver(forName: NSNotification.Name.NSManagedObjectContextDidSave, object: nil, queue: nil) 260 | { (notification) in 261 | 262 | let mObjectContext:NSManagedObjectContext = self.mainManagedObjectContext 263 | let nObjectContext:NSManagedObjectContext = notification.object as! NSManagedObjectContext 264 | 265 | if(mObjectContext != nObjectContext && nObjectContext.parent == nil && 266 | mObjectContext.persistentStoreCoordinator == nObjectContext.persistentStoreCoordinator) 267 | { 268 | mObjectContext.performAndWait({ 269 | mObjectContext.mergeChanges(fromContextDidSave: notification) 270 | }) 271 | 272 | self.observeChangesOnContextDidSave(notification: notification) 273 | } 274 | else if(mObjectContext == nObjectContext) { 275 | self.observeChangesOnContextDidSave(notification: notification) 276 | } 277 | } 278 | } 279 | 280 | 281 | //MARK: 282 | //MARK:- Instance Method 283 | 284 | fileprivate func observeChangesOnContextDidSave(notification:Notification) -> Void 285 | { 286 | if (arrChangesObserver != nil && arrChangesObserver?.count > 0) 287 | { 288 | self.performBlock(block: { 289 | let arrEntity:NSArray = NSSet.init(array: self.arrChangesObserver!.value(forKeyPath: "entity") as! [AnyObject]).allObjects as NSArray 290 | 291 | for entity in arrEntity 292 | { 293 | 294 | let oPredicate: NSPredicate = NSPredicate(format: "entity.name = %@",entity as! [AnyObject]) 295 | let arrUpdated: NSArray = (((notification.userInfo! as NSDictionary)["updated"]! as AnyObject).allObjects as NSArray).filtered(using: oPredicate) as NSArray 296 | let arrInserted: NSArray = (((notification.userInfo! as NSDictionary)["inserted"]! as AnyObject).allObjects as NSArray).filtered(using: oPredicate) as NSArray 297 | let arrDeleted: NSArray = (((notification.userInfo! as NSDictionary)["deleted"]! as AnyObject).allObjects as NSArray).filtered(using: oPredicate) as NSArray 298 | 299 | 300 | if(arrUpdated.count > 0 || arrInserted.count > 0 || arrDeleted.count > 0) 301 | { 302 | let arrTemp = self.arrChangesObserver!.filtered(using: NSPredicate(format: "entity = %@", entity as! NSObject)) 303 | for dict in arrTemp 304 | { 305 | let block = (dict as! NSDictionary)["block"] as! BlockChangesResult? 306 | let observer = (dict as! NSDictionary)["observer"] as! BlockVoid? 307 | var iPredicate = (dict as! NSDictionary)["predicate"] as! NSPredicate? 308 | let cPredicate = (dict as! NSDictionary)["changes"] as! NSPredicate? 309 | let type = (dict as! NSDictionary)["type"] as! NSManagedObjectChangeType 310 | 311 | var arrChanged:NSArray? 312 | 313 | if (iPredicate == nil) { 314 | iPredicate = NSPredicate(format: "1 == 1") 315 | } 316 | 317 | switch (type) 318 | { 319 | case .insert: 320 | arrChanged = arrInserted.filtered(using: iPredicate!) as NSArray 321 | break 322 | case .update: 323 | arrChanged = arrUpdated.filtered(using: iPredicate!) as NSArray 324 | break 325 | case .delete: 326 | arrChanged = arrDeleted.filtered(using: iPredicate!) as NSArray 327 | break 328 | case .all: 329 | 330 | let arrResults = NSMutableArray() 331 | arrResults.addObjects(from: arrInserted.filtered(using: iPredicate!)) 332 | 333 | if(observer != nil && arrResults.count > 0) { 334 | arrResults.addObjects(from: arrUpdated.filtered(using: iPredicate!)) 335 | } 336 | 337 | if(observer != nil && arrResults.count > 0) { 338 | arrResults.addObjects(from: arrDeleted.filtered(using: iPredicate!)) 339 | } 340 | 341 | arrChanged = arrResults 342 | 343 | break 344 | } 345 | 346 | if(cPredicate != nil) 347 | { 348 | 349 | arrChanged = arrChanged?.filtered(using: NSPredicate(format: "changedValues IN %@", (arrChanged?.value(forKeyPath: "changedValues") as! NSArray).filtered(using: cPredicate!))) as NSArray? 350 | } 351 | 352 | if(arrChanged?.count > 0) 353 | { 354 | self.performBlockOnMainThread(block: { 355 | if (observer != nil) { 356 | observer!() 357 | } 358 | else if(block != nil) 359 | { 360 | let arrMainValues = NSMutableArray() 361 | 362 | for object in arrChanged! 363 | { 364 | arrMainValues.add(CoreData.sharedInstance.mainManagedObjectContext.object(with: (object as! NSManagedObject).objectID)) 365 | } 366 | 367 | block!(arrMainValues, type) 368 | } 369 | }) 370 | } 371 | } 372 | } 373 | } 374 | }) 375 | } 376 | } 377 | 378 | class func saveContext() -> Void 379 | { 380 | CoreData.sharedInstance.saveContext() 381 | } 382 | 383 | func saveContext() -> Void 384 | { 385 | self.mainManagedObjectContext.saveContext() 386 | } 387 | 388 | func truncate() -> Void 389 | { 390 | let context:NSManagedObjectContext = self.newManagedObjectContext 391 | 392 | for entity:NSEntityDescription in self.managedObjectModel.entities 393 | { 394 | let aClass = NSClassFromString(entity.managedObjectClassName) as! NSManagedObject.Type 395 | aClass.deleteAllObjects(context: context, block: nil) 396 | } 397 | context.saveContext() 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /Geofence/Master/CoreData/CoreDataContext+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreDataContext+Extension.swift 3 | // DemoSwift 4 | // 5 | // Created by mac-0007 on 05/09/16. 6 | // Copyright © 2016 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | 13 | var kThread: UInt8 = 0 14 | extension NSManagedObjectContext 15 | { 16 | func saveContext() -> Void 17 | { 18 | if (self.hasChanges) 19 | { 20 | self.performSafeBlock(block: { 21 | do { 22 | try self.save() 23 | } catch { 24 | print("Unresolved error \(error)") 25 | if(self.parent != nil) { 26 | self.parent?.saveContext() 27 | } 28 | } 29 | }) 30 | } 31 | } 32 | 33 | func performSafeBlock(block:@escaping BlockVoid) -> Void 34 | { 35 | let thread:Thread? = self.object(forKey: &kThread) as? Thread 36 | 37 | if ((self.concurrencyType == .mainQueueConcurrencyType && Thread.isMainThread) || thread == Thread.current) { 38 | block() 39 | } 40 | else 41 | { 42 | self.perform({ 43 | if(thread == nil) { 44 | self.set(object: Thread.current, forKey: &kThread) 45 | } 46 | block() 47 | }) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Geofence/Master/CoreData/CoreDataObject+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreDataObject+Extension.swift 3 | // DemoSwift 4 | // 5 | // Created by mac-0007 on 05/09/16. 6 | // Copyright © 2016 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | extension NSManagedObject 13 | { 14 | class func findEntity() -> String 15 | { 16 | return NSStringFromClass(self).components(separatedBy: ".").last! 17 | } 18 | 19 | class func entityDescription() -> NSEntityDescription 20 | { 21 | return NSEntityDescription.entity(forEntityName: self.findEntity(), in: CoreData.sharedInstance.mainManagedObjectContext)! 22 | } 23 | 24 | class func entityContext() -> NSManagedObjectContext 25 | { 26 | if ((CoreData.sharedInstance.primaryKeys?.object(forKey: self.findEntity())) != nil) 27 | { 28 | return (CoreData.sharedInstance.primaryKeys?.object(forKey: self.findEntity()))! as! NSManagedObjectContext 29 | } else 30 | { 31 | let context:NSManagedObjectContext = CoreData.sharedInstance.childManagedObjectContext 32 | if #available(iOS 10.0, *) { 33 | CoreData.sharedInstance.primaryKeys?.setObject(context, forKey: self.entity()) 34 | } else { 35 | // Fallback on earlier versions 36 | } 37 | return context 38 | } 39 | } 40 | 41 | class func convert(object:NSManagedObject) -> NSManagedObject 42 | { 43 | if (object.managedObjectContext == self.entityContext()) { 44 | return object 45 | } 46 | 47 | if (object.objectID.isTemporaryID) 48 | { 49 | do { 50 | try object.managedObjectContext?.obtainPermanentIDs(for: [object]) 51 | } catch {} 52 | 53 | return self.entityContext().object(with: object.objectID) 54 | } else 55 | { 56 | return self.entityContext().object(with: object.objectID) 57 | } 58 | } 59 | 60 | 61 | 62 | class func performSafeBlock(block: @escaping BlockVoid) -> Void 63 | { 64 | self.entityContext().performSafeBlock { 65 | block() 66 | } 67 | } 68 | 69 | class func performBlockAndWait(block:@escaping BlockVoid) -> Void 70 | { 71 | self.entityContext().performAndWait { 72 | block() 73 | } 74 | } 75 | 76 | 77 | 78 | func entityContext() -> NSManagedObjectContext 79 | { 80 | return NSManagedObject.entityContext() 81 | } 82 | 83 | func editableObejct() -> NSManagedObject 84 | { 85 | if (self.managedObjectContext == self.entityContext()) { 86 | return self 87 | } else { 88 | return NSManagedObject.convert(object: self) 89 | } 90 | } 91 | 92 | 93 | 94 | class func convert(value:AnyObject, forAttribute attribute:String) -> AnyObject? 95 | { 96 | var value = value 97 | let attributeType:NSAttributeType = (self.entityDescription().attributesByName as NSDictionary).object(forKey: attribute)!.attributeType 98 | 99 | if (attributeType == .stringAttributeType && (value is NSNumber)) { 100 | value = value.stringValue as AnyObject 101 | } 102 | else if((attributeType == .integer16AttributeType || attributeType == .integer32AttributeType || attributeType == .integer64AttributeType || attributeType == .booleanAttributeType) && (value is String)) 103 | { 104 | value = NSNumber(value: value.intValue as Int) 105 | } 106 | else if((attributeType == .floatAttributeType || attributeType == .decimalAttributeType || attributeType == .doubleAttributeType) && (value is String)) 107 | { 108 | value = NSNumber(value: value.doubleValue as Double) 109 | } 110 | 111 | return value 112 | } 113 | 114 | func set(value:AnyObject?, forAttribute attribute:String) -> Void 115 | { 116 | 117 | if (value == nil) { 118 | return 119 | } 120 | 121 | self.setValue(NSManagedObject.convert(value: value!, forAttribute: attribute), forKey: attribute) 122 | } 123 | 124 | func setKeyValue(dictionary dict:NSDictionary?) -> Void 125 | { 126 | let attributes:NSDictionary = self.entity.attributesByName as NSDictionary 127 | for (key, _) in attributes { 128 | self.setValue(dict?.value(forKey: key as! String), forKey: key as! String) 129 | } 130 | } 131 | 132 | 133 | 134 | 135 | 136 | 137 | //MARK: 138 | //MARK:- Create NSManagedObject 139 | 140 | class func create(dictionary dict:NSDictionary?) -> NSManagedObject 141 | { 142 | return self.createManagedObject(dictionary: dict, context: nil) 143 | } 144 | 145 | class func create(dictionary dict:NSDictionary?, block:BlockManagedObjectResult?) -> Void 146 | { 147 | self.createManagedObject(dictionary: dict, context: nil, block: block) 148 | } 149 | 150 | class func create(dictionary dict:NSDictionary?, context:NSManagedObjectContext?) -> NSManagedObject 151 | { 152 | return self.createManagedObject(dictionary: dict, context: context) 153 | } 154 | 155 | class func create(dictionary dict:NSDictionary?, context:NSManagedObjectContext?, block:BlockManagedObjectResult?) -> Void 156 | { 157 | self.createManagedObject(dictionary: dict, context: context, block: block) 158 | } 159 | 160 | class func update(dictionary dict:NSDictionary?, predicate:NSPredicate?, context:NSManagedObjectContext?) -> Void 161 | { 162 | var context = context 163 | if (context == nil) { 164 | context = CoreData.sharedInstance.mainManagedObjectContext 165 | } 166 | 167 | context?.performSafeBlock(block: { 168 | 169 | if(NSClassFromString("NSBatchUpdateRequest") != nil) 170 | { 171 | let batchUpdateRequest = NSBatchUpdateRequest(entityName: self.findEntity()) 172 | batchUpdateRequest.resultType = .updatedObjectIDsResultType 173 | batchUpdateRequest.propertiesToUpdate = dict as? [AnyHashable: Any] 174 | 175 | if (predicate != nil) { 176 | batchUpdateRequest.predicate = predicate 177 | } 178 | 179 | do 180 | { 181 | let batchUpdateResult = try context?.execute(batchUpdateRequest) as? NSBatchUpdateResult 182 | let objectIDs = batchUpdateResult!.result 183 | 184 | for objectID in objectIDs as! NSArray 185 | { 186 | let managedObject = context?.object(with: objectID as! NSManagedObjectID) 187 | if (managedObject != nil) { 188 | context?.refresh(managedObject!, mergeChanges: false) 189 | } 190 | } 191 | } 192 | catch 193 | { 194 | print("Execute NSBatchUpdateRequest \(error)") 195 | } 196 | } 197 | else 198 | { 199 | let result = self.fetch(predicate: predicate, sortDescriptors: nil, context: context) 200 | for (key, value) in dict! { 201 | result?.setValue(value, forKeyPath: key as! String) 202 | } 203 | 204 | context?.saveContext() 205 | } 206 | }) 207 | } 208 | 209 | fileprivate class func createManagedObject(dictionary dict:NSDictionary?, context:NSManagedObjectContext?) -> NSManagedObject 210 | { 211 | var context = context 212 | if (context == nil) { 213 | context = CoreData.sharedInstance.mainManagedObjectContext 214 | } 215 | 216 | let managedObject = NSEntityDescription.insertNewObject(forEntityName: self.findEntity(), into: context!) 217 | managedObject.setKeyValue(dictionary: dict) 218 | 219 | return managedObject 220 | } 221 | 222 | fileprivate class func createManagedObject(dictionary dict:NSDictionary?, context:NSManagedObjectContext?, block:BlockManagedObjectResult?) -> Void 223 | { 224 | var context = context 225 | if (context == nil) { 226 | context = CoreData.sharedInstance.mainManagedObjectContext 227 | } 228 | 229 | context?.performSafeBlock(block: { 230 | 231 | let object = self.createManagedObject(dictionary: dict, context: context) 232 | if (block != nil) { 233 | block!(object, nil) 234 | } 235 | }) 236 | } 237 | 238 | 239 | 240 | 241 | //MARK: 242 | //MARK:- FindOrCreate NSManagedObject 243 | 244 | class func findOrCreate(dictionary dict:NSDictionary?) -> NSManagedObject 245 | { 246 | return self.findOrCreate(dictionary: dict, context: nil) 247 | } 248 | 249 | class func findOrCreate(dictionary dict:NSDictionary?, block:@escaping BlockManagedObjectResult) -> Void 250 | { 251 | self.findOrCreate(dictionary: dict, context: nil, block: block) 252 | } 253 | 254 | class func findOrCreate(dictionary dict:NSDictionary?, context:NSManagedObjectContext?) -> NSManagedObject 255 | { 256 | var predicates = [NSPredicate]() 257 | print(predicates) 258 | if (dict != nil) 259 | { 260 | for (key, value) in dict! { 261 | predicates.append(NSPredicate(format: "%K == %@", key as! NSObject, value as! NSObject)) 262 | } 263 | } 264 | 265 | return self.findOrCreateManagedObject(predicate: NSCompoundPredicate(andPredicateWithSubpredicates: predicates), dict: dict, context: context) 266 | } 267 | 268 | class func findOrCreate(dictionary dict:NSDictionary?, context:NSManagedObjectContext?, block:@escaping BlockManagedObjectResult) -> Void 269 | { 270 | var predicates = [NSPredicate]() 271 | 272 | if (dict != nil) 273 | { 274 | for (key, value) in dict! { 275 | predicates.append(NSPredicate(format: "%K == %@", key as! NSObject, value as! NSObject)) 276 | } 277 | } 278 | 279 | self.findOrCreateManagedObject(predicate: NSCompoundPredicate(andPredicateWithSubpredicates: predicates), dict: dict, context: context, block: block) 280 | } 281 | 282 | fileprivate class func findOrCreateManagedObject(predicate:NSPredicate?) -> NSManagedObject 283 | { 284 | return self.findOrCreateManagedObject(predicate: predicate, dict: nil, context: nil) 285 | } 286 | 287 | fileprivate class func findOrCreateManagedObject(predicate:NSPredicate?, block:BlockManagedObjectResult?) -> Void 288 | { 289 | self.findOrCreateManagedObject(predicate: predicate, dict: nil, context: nil, block: block) 290 | } 291 | 292 | fileprivate class func findOrCreateManagedObject(predicate:NSPredicate?, dict:NSDictionary) -> NSManagedObject 293 | { 294 | return self.findOrCreateManagedObject(predicate: predicate, dict: dict, context: nil) 295 | } 296 | 297 | fileprivate class func findOrCreateManagedObject(predicate:NSPredicate?, dict:NSDictionary, block:BlockManagedObjectResult?) -> Void 298 | { 299 | self.findOrCreateManagedObject(predicate: predicate, dict: dict, context: nil, block: block) 300 | } 301 | 302 | fileprivate class func findOrCreateManagedObject(predicate:NSPredicate?, dict:NSDictionary?, context:NSManagedObjectContext?, block:BlockManagedObjectResult?) -> Void 303 | { 304 | var context = context 305 | if (context == nil) { 306 | context = CoreData.sharedInstance.mainManagedObjectContext 307 | } 308 | 309 | context?.performSafeBlock(block: { 310 | let object = self.findOrCreateManagedObject(predicate: predicate, dict: dict, context: context) 311 | if (block != nil) { 312 | block!(object, nil) 313 | } 314 | }) 315 | } 316 | 317 | fileprivate class func findOrCreateManagedObject(predicate:NSPredicate?, dict:NSDictionary?, context:NSManagedObjectContext?) -> NSManagedObject 318 | { 319 | var managedObject:NSManagedObject? 320 | 321 | var context = context 322 | if (context == nil) { 323 | context = CoreData.sharedInstance.mainManagedObjectContext 324 | } 325 | 326 | if (predicate != nil) 327 | { 328 | let fetchRequest = self.fetchRequest(predicate: predicate, sortDescriptors: nil, fetchLimit: 1) 329 | 330 | do 331 | { 332 | let result = try context!.fetch(fetchRequest) 333 | managedObject = result.last as? NSManagedObject 334 | } 335 | catch 336 | { 337 | print("Execute fetchRequest \(error)") 338 | } 339 | } 340 | 341 | if (managedObject == nil) { 342 | managedObject = self.create(dictionary: dict, context: context) 343 | } 344 | 345 | return managedObject! 346 | } 347 | 348 | 349 | 350 | 351 | 352 | //MARK: 353 | //MARK:- Fetch Count 354 | 355 | class func count(predicate:NSPredicate?) -> Int 356 | { 357 | return self.count(predicate: predicate, context: nil) 358 | } 359 | 360 | class func count(predicate:NSPredicate?, block:BlockCountResult?) -> Void 361 | { 362 | self.count(predicate: predicate, context: nil, block: block) 363 | } 364 | 365 | class func count(predicate:NSPredicate?, context:NSManagedObjectContext?) -> Int 366 | { 367 | var context = context 368 | if (context == nil) { 369 | context = CoreData.sharedInstance.mainManagedObjectContext 370 | } 371 | 372 | let fetchRequest:NSFetchRequest = self.fetchRequest(predicate: predicate, sortDescriptors: nil, fetchLimit: 0) 373 | fetchRequest.includesPropertyValues = false 374 | let count = try! context?.count(for: fetchRequest) 375 | 376 | return count! 377 | } 378 | 379 | class func count(predicate:NSPredicate?, context:NSManagedObjectContext?, block:BlockCountResult?) -> Void 380 | { 381 | var context = context 382 | if (context == nil) { 383 | context = CoreData.sharedInstance.mainManagedObjectContext 384 | } 385 | 386 | context?.performSafeBlock(block: { 387 | let count:Int = self.count(predicate: predicate, context: nil) 388 | 389 | if (block != nil) { 390 | block!(count, nil) 391 | } 392 | }) 393 | } 394 | 395 | 396 | 397 | 398 | //MARK: 399 | //MARK:- Fetch Objects 400 | 401 | class func fetchAllObjects() -> NSArray? 402 | { 403 | return self.fetch(predicate: nil, sortDescriptors: nil) 404 | } 405 | 406 | class func fetchAllObjects(block:BlockArrayResult?) -> Void 407 | { 408 | self.fetch(predicate: nil, sortDescriptors: nil, block: block) 409 | } 410 | 411 | class func fetch(predicate:NSPredicate?) -> NSArray? 412 | { 413 | return self.fetch(predicate: predicate, sortDescriptors: nil) 414 | } 415 | 416 | class func fetch(predicate:NSPredicate?, block:BlockArrayResult?) -> Void 417 | { 418 | self.fetch(predicate: predicate, sortDescriptors: nil, block: block) 419 | } 420 | 421 | class func fetch(predicate:NSPredicate?, orderBy:String?, ascending:Bool) -> NSArray? 422 | { 423 | var sortDescriptors:[NSSortDescriptor]? 424 | 425 | if (orderBy != nil && (orderBy?.blank())!) { 426 | sortDescriptors = [NSSortDescriptor(key: orderBy, ascending: ascending)] 427 | } 428 | 429 | return self.fetch(predicate: predicate, sortDescriptors: sortDescriptors, context: nil, fetchLimit: 0) 430 | } 431 | 432 | class func fetch(predicate:NSPredicate?, orderBy:String?, ascending:Bool, block:BlockArrayResult?) -> Void 433 | { 434 | var sortDescriptors:[NSSortDescriptor]? 435 | 436 | if (orderBy != nil && (orderBy?.blank())!) { 437 | sortDescriptors = [NSSortDescriptor(key: orderBy, ascending: ascending)] 438 | } 439 | 440 | self.fetch(predicate: predicate, sortDescriptors: sortDescriptors, context: nil, fetchLimit: 0, block: block) 441 | } 442 | 443 | class func fetch(predicate:NSPredicate?, sortDescriptors:[NSSortDescriptor]?) -> NSArray? 444 | { 445 | return self.fetch(predicate: predicate, sortDescriptors: sortDescriptors, context: nil, fetchLimit: 0) 446 | } 447 | 448 | class func fetch(predicate:NSPredicate?, sortDescriptors:[NSSortDescriptor]?, block:BlockArrayResult?) -> Void 449 | { 450 | self.fetch(predicate: predicate, sortDescriptors: sortDescriptors, context: nil, fetchLimit: 0, block: block) 451 | } 452 | 453 | class func fetch(predicate:NSPredicate?, sortDescriptors:[NSSortDescriptor]?, fetchLimit:Int) -> NSArray? 454 | { 455 | return self.fetch(predicate: predicate, sortDescriptors: sortDescriptors, context: nil, fetchLimit: fetchLimit) 456 | } 457 | 458 | class func fetch(predicate:NSPredicate?, sortDescriptors:[NSSortDescriptor]?, fetchLimit:Int, block:BlockArrayResult?) -> Void 459 | { 460 | self.fetch(predicate: predicate, sortDescriptors: sortDescriptors, context: nil, fetchLimit: fetchLimit, block: block) 461 | } 462 | 463 | class func fetch(predicate:NSPredicate?, sortDescriptors:[NSSortDescriptor]?, context:NSManagedObjectContext?) -> NSArray? 464 | { 465 | return self.fetch(predicate: predicate, sortDescriptors: sortDescriptors, context: context, fetchLimit: 0) 466 | } 467 | 468 | class func fetch(predicate:NSPredicate?, sortDescriptors:[NSSortDescriptor]?, context:NSManagedObjectContext?, block:BlockArrayResult?) -> Void 469 | { 470 | self.fetch(predicate: predicate, sortDescriptors: sortDescriptors, context: context, fetchLimit: 0, block: block) 471 | } 472 | 473 | class func fetch(predicate:NSPredicate?, sortDescriptors:[NSSortDescriptor]?, context:NSManagedObjectContext?, fetchLimit:Int) -> NSArray? 474 | { 475 | var context = context 476 | if (context == nil) { 477 | context = CoreData.sharedInstance.mainManagedObjectContext 478 | } 479 | 480 | var result = NSArray() 481 | let fetchRequest = self.fetchRequest(predicate: predicate, sortDescriptors: sortDescriptors, fetchLimit: fetchLimit) 482 | 483 | do { 484 | result = try context!.fetch(fetchRequest) as NSArray 485 | } catch { 486 | print("Execute fetchRequest \(error)") 487 | } 488 | 489 | return result 490 | } 491 | 492 | class func fetch(predicate:NSPredicate?, sortDescriptors:[NSSortDescriptor]?, context:NSManagedObjectContext?, fetchLimit:Int, block:BlockArrayResult?) -> Void 493 | { 494 | var context = context 495 | if (context == nil) { 496 | context = CoreData.sharedInstance.mainManagedObjectContext 497 | } 498 | 499 | context?.performSafeBlock(block: { 500 | 501 | let result = self.fetch(predicate: predicate, sortDescriptors: sortDescriptors, context: context, fetchLimit: fetchLimit) 502 | 503 | if (block != nil) { 504 | block!(result, nil) 505 | } 506 | }) 507 | } 508 | 509 | class func fetchRequest(predicate:NSPredicate?, sortDescriptors:[NSSortDescriptor]?, fetchLimit:Int) -> NSFetchRequest 510 | { 511 | let fetchRequest:NSFetchRequest = NSFetchRequest(entityName: self.findEntity()) 512 | 513 | if (predicate != nil) { 514 | fetchRequest.predicate = predicate 515 | } 516 | 517 | if (sortDescriptors != nil) { 518 | fetchRequest.sortDescriptors = sortDescriptors 519 | } 520 | 521 | fetchRequest.fetchLimit = fetchLimit 522 | 523 | return fetchRequest 524 | } 525 | 526 | class func fetchAsynchronously(predicate:NSPredicate?, sortDescriptors:[NSSortDescriptor]?, context:NSManagedObjectContext?, fetchLimit:Int, block:BlockArrayResult?) -> Void 527 | { 528 | var context = context 529 | if (context == nil) { 530 | context = CoreData.sharedInstance.mainManagedObjectContext 531 | } 532 | 533 | if (NSClassFromString("NSAsynchronousFetchRequest") != nil) 534 | { 535 | let fetchRequest = self.fetchRequest(predicate: predicate, sortDescriptors: sortDescriptors, fetchLimit: fetchLimit) 536 | let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest, completionBlock: { (result) in 537 | 538 | if (block != nil) { 539 | block!(result.finalResult as NSArray?, nil) 540 | } 541 | }) 542 | 543 | context?.performSafeBlock(block: { 544 | do 545 | { 546 | let asynchronousFetchResult = try context?.execute(asynchronousFetchRequest) 547 | print("NSAsynchronousFetchResult result: \(String(describing: asynchronousFetchResult))") 548 | } 549 | catch 550 | { 551 | print("NSAsynchronousFetchResult error: \(error)") 552 | } 553 | }) 554 | } 555 | else 556 | { 557 | assert(context?.concurrencyType != .privateQueueConcurrencyType, "Use context with concurrencyType PrivateQueueConcurrencyType, otherwise it will not fetch data Asynchronously.") 558 | 559 | context?.performSafeBlock(block: { 560 | let result = self.fetch(predicate: predicate, sortDescriptors: sortDescriptors, context: context, fetchLimit: fetchLimit) 561 | 562 | if (block != nil) { 563 | block!(result, nil) 564 | } 565 | }) 566 | } 567 | } 568 | 569 | 570 | 571 | 572 | 573 | 574 | //MARK: 575 | //MARK:- Delete Objects 576 | 577 | class func deleteAllObjects() -> Void 578 | { 579 | self.deleteAllObjects(context: nil) 580 | } 581 | 582 | class func deleteAllObjects(context:NSManagedObjectContext?) -> Void 583 | { 584 | self.deleteObjects(predicate: nil, context: context) 585 | } 586 | 587 | class func deleteAllObjects(context:NSManagedObjectContext?, block:BlockVoid?) -> Void 588 | { 589 | self.deleteObjects(predicate: nil, context: context, block: block) 590 | } 591 | 592 | class func deleteObjects(predicate:NSPredicate?) -> Void 593 | { 594 | self.deleteObjects(predicate: predicate, context: CoreData.sharedInstance.mainManagedObjectContext) 595 | } 596 | 597 | class func deleteObjects(predicate:NSPredicate?, block:BlockVoid?) -> Void 598 | { 599 | self.deleteObjects(predicate: predicate, context: CoreData.sharedInstance.mainManagedObjectContext, block: block) 600 | } 601 | 602 | class func deleteObjects(predicate:NSPredicate?, context:NSManagedObjectContext?) -> Void 603 | { 604 | var context = context 605 | if (context == nil) { 606 | context = CoreData.sharedInstance.mainManagedObjectContext 607 | } 608 | 609 | let fetchRequest:NSFetchRequest = self.fetchRequest(predicate: predicate, sortDescriptors: nil, fetchLimit: 0) 610 | 611 | if (NSClassFromString("NSBatchDeleteRequest") != nil) 612 | { 613 | // iOS 9 and later 614 | if #available(iOS 9.0, *) { 615 | let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) 616 | 617 | do { 618 | try context?.execute(deleteRequest) 619 | context?.saveContext() 620 | } catch { 621 | print("Execute NSBatchDeleteRequest \(error)") 622 | } 623 | } 624 | } 625 | else 626 | { 627 | fetchRequest.includesPropertyValues = false 628 | 629 | do 630 | { 631 | let result = try context!.fetch(fetchRequest) 632 | 633 | for managedObject in result { 634 | context?.delete(managedObject as! NSManagedObject) 635 | } 636 | 637 | context?.saveContext() 638 | } 639 | catch 640 | { 641 | print("Execute NSFetchRequest \(error)") 642 | } 643 | } 644 | } 645 | 646 | class func deleteObjects(predicate:NSPredicate?, context:NSManagedObjectContext?, block:BlockVoid?) -> Void 647 | { 648 | var context = context 649 | if (context == nil) { 650 | context = CoreData.sharedInstance.mainManagedObjectContext 651 | } 652 | 653 | context?.performSafeBlock(block: { 654 | self.deleteObjects(predicate: predicate, context: nil) 655 | if(block != nil) { 656 | block!() 657 | } 658 | }) 659 | } 660 | 661 | func deleteObject() -> Void 662 | { 663 | self.managedObjectContext?.delete(self) 664 | self.managedObjectContext?.saveContext() 665 | } 666 | 667 | func deleteObject(block:BlockVoid?) -> Void 668 | { 669 | self.managedObjectContext?.performSafeBlock(block: { 670 | self.deleteObject() 671 | 672 | if(block != nil) { 673 | block!() 674 | } 675 | }) 676 | } 677 | 678 | 679 | 680 | 681 | //MARK: 682 | //MARK:- Observer NSManagedObject 683 | 684 | 685 | func observeChanges(block:BlockVoid?) -> Void 686 | { 687 | assert((self.managedObjectContext?.isEqual(CoreData.sharedInstance.mainManagedObjectContext))!, "You should observer changes on mainManagedObjectContext Only.") 688 | 689 | if (self.objectID.isTemporaryID) { 690 | self.managedObjectContext?.saveContext() 691 | } 692 | 693 | NSManagedObject.changedObjects(changeType: .update, predicate: NSPredicate(format: "objectID = %@", self.objectID)) { 694 | if(block != nil) { 695 | block!() 696 | } 697 | } 698 | } 699 | 700 | class func changedObjects(block:BlockChangesResult?) -> Void 701 | { 702 | self.changedObjects(changeType:.all, predicate: nil, block: block) 703 | } 704 | 705 | class func changedObjects(block:BlockVoid?) -> Void 706 | { 707 | self.changedObjects(changeType:.all, predicate: nil, block: block!) 708 | } 709 | 710 | class func changedObjects(predicate:NSPredicate?, block:BlockChangesResult?) -> Void 711 | { 712 | self.changedObjects(changeType:.all, predicate: predicate, block: block) 713 | } 714 | 715 | class func changedObjects(predicate:NSPredicate?, block:BlockVoid?) -> Void 716 | { 717 | self.changedObjects(changeType:.all, predicate: predicate, block: block!) 718 | } 719 | 720 | class func changedObjects(changeType type:NSManagedObjectChangeType, block:BlockChangesResult?) -> Void 721 | { 722 | self.changedObjects(changeType: type, predicate: nil, block: block) 723 | } 724 | 725 | 726 | 727 | class func changedObjects(changeType type:NSManagedObjectChangeType, block:BlockVoid?) -> Void 728 | { 729 | self.changedObjects(changeType: type, predicate: nil, block: block!) 730 | } 731 | 732 | class func changedObjects(changeType type:NSManagedObjectChangeType, predicate:NSPredicate?, block:BlockChangesResult?) -> Void 733 | { 734 | self.changedObjects(changeType: type, predicate: predicate, block: WrapperBlockChangesResult.init(block: block!), blockType: BlockType.block, changePredicate: nil) 735 | } 736 | 737 | class func changedObjects(changeType type:NSManagedObjectChangeType, predicate:NSPredicate?, block:BlockVoid?) -> Void 738 | { 739 | self.changedObjects(changeType: type, predicate: predicate, block: WrapperBlockVoid.init(block: block!), blockType: BlockType.observer, changePredicate: nil) 740 | } 741 | 742 | class func changedObjects(changeType type:NSManagedObjectChangeType, predicate:NSPredicate?, changePredicate:NSPredicate?, block:BlockVoid?) -> Void 743 | { 744 | self.changedObjects(changeType: type, predicate: predicate, block: WrapperBlockVoid.init(block: block!), blockType: BlockType.observer, changePredicate: changePredicate) 745 | } 746 | 747 | class func changedObjects(changeType type:NSManagedObjectChangeType, predicate:NSPredicate?, properties:NSArray?, block:@escaping BlockChangesResult) -> Void 748 | { 749 | var changePredicate:NSPredicate? 750 | 751 | for property in properties! 752 | { 753 | let tempPredicate = NSPredicate(format: "%K != nil", property as! [AnyObject]) 754 | 755 | if (changePredicate == nil) { 756 | changePredicate = tempPredicate 757 | } else { 758 | changePredicate = NSCompoundPredicate(orPredicateWithSubpredicates: [changePredicate!, tempPredicate]) 759 | } 760 | } 761 | 762 | self.changedObjects(changeType: type, predicate: predicate, block: WrapperBlockChangesResult.init(block: block), blockType: BlockType.block, changePredicate: nil) 763 | } 764 | 765 | class func changedObjects(changeType type:NSManagedObjectChangeType, predicate:NSPredicate?, block:AnyObject, blockType:BlockType, changePredicate:NSPredicate?) -> Void 766 | { 767 | let dict = NSMutableDictionary() 768 | dict.setValue(self.findEntity(), forKey: "entity") 769 | dict.setValue(NSNumber(value: type.rawValue as Int), forKey: "type") 770 | dict.setValue(block, forKey: blockType.rawValue) 771 | 772 | if (predicate != nil) { 773 | dict.setValue(predicate, forKey: "predicate") 774 | } 775 | 776 | if (changePredicate != nil) { 777 | dict.setValue(changePredicate, forKey: "changes") 778 | } 779 | 780 | if (CoreData.sharedInstance.arrChangesObserver?.contains(dict) == false) { 781 | CoreData.sharedInstance.arrChangesObserver?.add(dict) 782 | } 783 | } 784 | 785 | class func deleteObservers() -> Void 786 | { 787 | 788 | let array = CoreData.sharedInstance.arrChangesObserver?.filtered(using: (NSPredicate(format: "entity == %@", self.findEntity()))) 789 | 790 | CoreData.sharedInstance.arrChangesObserver?.removeObjects(in: array!) 791 | } 792 | 793 | class func deleteObserver(block:BlockVoid?) -> Void 794 | { 795 | 796 | let array = CoreData.sharedInstance.arrChangesObserver?.filtered(using: (NSPredicate(format: "entity == %@ && observer == %@", self.findEntity(), WrapperBlockVoid.init(block: block!)))) 797 | 798 | CoreData.sharedInstance.arrChangesObserver?.removeObjects(in: array!) 799 | } 800 | 801 | class func deleteObserver(block:BlockChangesResult?) -> Void 802 | { 803 | 804 | let array = CoreData.sharedInstance.arrChangesObserver?.filtered(using: (NSPredicate(format: "entity == %@ && block == %@", self.findEntity(), WrapperBlockChangesResult.init(block: block!)))) 805 | 806 | CoreData.sharedInstance.arrChangesObserver?.removeObjects(in: array!) 807 | } 808 | } 809 | -------------------------------------------------------------------------------- /Geofence/Master/Extension/ExtensionNSObject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionNSObject.swift 3 | // DemoSwift 4 | // 5 | // Created by mac-0007 on 28/09/16. 6 | // Copyright © 2016 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class MultiCastDelegate: NSObject 12 | { 13 | var _delegates = NSMutableArray() 14 | 15 | func add(delegate:AnyObject) -> Void 16 | { 17 | if (!_delegates.contains(delegate)) { 18 | _delegates.add(delegate) 19 | } 20 | } 21 | 22 | func remove(delegate:AnyObject) -> Void 23 | { 24 | if (_delegates.contains(delegate)) { 25 | _delegates.remove(delegate) 26 | } 27 | } 28 | 29 | override func responds(to aSelector: Selector) -> Bool 30 | { 31 | if (super.responds(to: aSelector)) { 32 | return true 33 | } 34 | 35 | for delegate in _delegates 36 | { 37 | if ((delegate as AnyObject).responds(to: aSelector)) { 38 | return true 39 | } 40 | } 41 | 42 | return false 43 | } 44 | 45 | // override func methodSignatureForSelector(aSelector: Selector) -> NSMethodSignature! { 46 | // _delegates.enumerateObjectsUsingBlock { (delegate, index, finish) in 47 | // if (delegate.respondsToSelector(aSelector)) { 48 | // return delegate.methodForSelector(aSelector) 49 | // } 50 | // } 51 | // } 52 | } 53 | 54 | 55 | extension NSObject 56 | { 57 | // MARK: 58 | // MARK:- Property 59 | 60 | func isNull() -> Bool 61 | { 62 | if (self.isEqual(NSNull()) || self is NSNull) { 63 | return true 64 | } 65 | 66 | if (self is String) { 67 | if ((self as! String).count == 0) { 68 | return true 69 | } 70 | } 71 | 72 | if (self is NSArray) { 73 | if ((self as! NSArray).count == 0) { 74 | return true 75 | } 76 | } 77 | 78 | if (self is NSDictionary) { 79 | if ((self as! NSDictionary).count == 0) { 80 | return true 81 | } 82 | } 83 | 84 | return false 85 | } 86 | 87 | 88 | 89 | 90 | /** 91 | Method from NSObject Extension 92 | */ 93 | 94 | func set(object anObj:AnyObject?, forKey:UnsafeRawPointer) -> Void 95 | { 96 | objc_setAssociatedObject(self, forKey, anObj, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 97 | } 98 | 99 | 100 | /** 101 | Method from NSObject Extension 102 | */ 103 | 104 | func object(forKey key:UnsafeRawPointer) -> AnyObject? 105 | { 106 | return objc_getAssociatedObject(self, key) as AnyObject? 107 | } 108 | 109 | 110 | 111 | // (Int)integer 112 | 113 | func set(Int integerValue:Int, key:UnsafeRawPointer) -> Void 114 | { 115 | self.set(object: NSNumber(value: integerValue as Int), forKey: key) 116 | } 117 | 118 | func int(forKey key:String) -> Int 119 | { 120 | return self.object(forKey: key)!.intValue 121 | } 122 | 123 | 124 | 125 | 126 | 127 | // (float)floatValue 128 | 129 | func set(Float floatValue:Float, key:UnsafeRawPointer) -> Void 130 | { 131 | self.set(object: NSNumber(value: floatValue as Float), forKey: key) 132 | } 133 | 134 | func float(forKey key:String) -> Float 135 | { 136 | return self.object(forKey: key)!.floatValue 137 | } 138 | 139 | 140 | 141 | 142 | 143 | // (double)doubleValue 144 | 145 | func set(Double doubleValue:Double, key:UnsafeRawPointer) -> Void 146 | { 147 | self.set(object: NSNumber(value: doubleValue as Double), forKey: key) 148 | } 149 | 150 | func double(forKey key:String) -> Double 151 | { 152 | return self.object(forKey: key)!.doubleValue 153 | } 154 | 155 | 156 | 157 | 158 | 159 | // (BOOL)boolean 160 | 161 | func set(Bool boolValue:Bool, key:UnsafeRawPointer) -> Void 162 | { 163 | self.set(object: NSNumber(value: boolValue as Bool), forKey: key) 164 | } 165 | 166 | func bool(forKey key:String) -> Bool 167 | { 168 | return self.object(forKey: key)!.boolValue 169 | } 170 | 171 | func stringValue(forJSON key: String) -> String { 172 | var value = self.value(forKey: key) 173 | 174 | if value == nil 175 | { 176 | return "" 177 | } 178 | if value as AnyObject? === NSNull() 179 | { 180 | value = nil 181 | } 182 | 183 | if !(value is String) { 184 | value = "\(value!)" 185 | } 186 | return value as? String ?? "" 187 | } 188 | 189 | 190 | 191 | // MARK: 192 | // MARK:- Perform Block 193 | 194 | func performBlock(block: @escaping BlockVoid) -> Void 195 | { 196 | GCDBackgroundThread.async { 197 | block() 198 | } 199 | } 200 | 201 | func performBlockOnMainThread(block:@escaping BlockVoid) -> Void 202 | { 203 | GCDMainThread.async { 204 | block() 205 | } 206 | } 207 | 208 | func performBlockOnMainThread(block:@escaping BlockVoid, afterDelay:UInt64) -> Void 209 | { 210 | GCDMainThread.asyncAfter(deadline: DispatchTime.now() + Double(Int64(afterDelay * NSEC_PER_SEC)) / Double(NSEC_PER_SEC), execute: { 211 | block() 212 | }) 213 | } 214 | 215 | func performBlockOnBackgroundThread(block:@escaping BlockVoid, afterDelay:UInt64) -> Void 216 | { 217 | GCDBackgroundThread.asyncAfter(deadline: DispatchTime.now() + Double(Int64(afterDelay * NSEC_PER_SEC)) / Double(NSEC_PER_SEC), execute: { 218 | block() 219 | }) 220 | } 221 | 222 | func performBlockWithHighPriority(block:@escaping BlockVoid) -> Void 223 | { 224 | GCDBackgroundThreadHighPriority.async { 225 | block() 226 | } 227 | } 228 | 229 | func performBlockWithLowPriority(block:@escaping BlockVoid) -> Void 230 | { 231 | GCDBackgroundThreadLowPriority.async { 232 | block() 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /Geofence/Master/Extension/ExtensionString.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionString.swift 3 | // DemoSwift 4 | // 5 | // Created by mac-0007 on 27/09/16. 6 | // Copyright © 2016 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension String { 13 | 14 | func trim() -> String? 15 | { 16 | return self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 17 | } 18 | 19 | func blank() -> Bool 20 | { 21 | return self.trim()!.count == 0 22 | } 23 | 24 | func validEmail() -> Bool 25 | { 26 | 27 | let regex = "^[+\\w\\.\\-']+@[a-zA-Z0-9-]+(\\.[a-zA-Z]{2,})+$" 28 | let predicate = NSPredicate(format: "SELF MATCHES %@", regex) 29 | return predicate.evaluate(with: self.trim()) 30 | } 31 | 32 | func validPhone() -> Bool 33 | { 34 | if(self.count < 14) 35 | { 36 | return false 37 | } 38 | return true 39 | 40 | let regex = "^((\\+)|(00))[0-9]{6,14}$" 41 | let predicate = NSPredicate(format: "SELF MATCHES %@", regex) 42 | return predicate.evaluate(with: self.trim()) 43 | } 44 | 45 | func validPassword() -> Bool 46 | { 47 | return (self.trim()?.count)!>=6 48 | } 49 | 50 | func validNumber() -> Bool 51 | { 52 | let regex = "[0-9]" 53 | let predicate = NSPredicate(format: "SELF MATCHES %@", regex) 54 | return predicate.evaluate(with: self.trim()) 55 | } 56 | 57 | func validDouble() -> Bool 58 | { 59 | let regex = "^\\d+(\\.\\d+)?" 60 | let predicate = NSPredicate(format: "SELF MATCHES %@", regex) 61 | return predicate.evaluate(with: self.trim()) 62 | } 63 | 64 | func validURL() -> Bool 65 | { 66 | let regex = "(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+" 67 | let predicate = NSPredicate(format: "SELF MATCHES %@", regex) 68 | return predicate.evaluate(with: self.trim()) 69 | } 70 | 71 | 72 | 73 | 74 | func localURL() -> Bool 75 | { 76 | return !self.serverURL() 77 | } 78 | 79 | func serverURL() -> Bool 80 | { 81 | return !(self.index(of: "http:") == NSNotFound && self.index(of: "https:") == NSNotFound) 82 | } 83 | 84 | func URL() -> Foundation.URL? { 85 | if self.serverURL() { 86 | return Foundation.URL(string: self) 87 | } else { 88 | return Foundation.URL(fileURLWithPath: self) 89 | } 90 | } 91 | 92 | 93 | 94 | 95 | func index(of string: String) -> Int 96 | { 97 | return (self as NSString).range(of: string).location 98 | } 99 | 100 | func bundlePath() -> String? 101 | { 102 | return self.bundlePath(type: nil) 103 | } 104 | 105 | func bundlePath(type t:String?) -> String? 106 | { 107 | return Bundle.main.path(forResource: self, ofType: t)! 108 | } 109 | 110 | func homeDirPath() -> String 111 | { 112 | return NSHomeDirectory() 113 | } 114 | 115 | func documentDirPath() -> String? 116 | { 117 | return self.homeDirPath()+"/Documents" 118 | } 119 | 120 | func libraryDirPath() -> String? 121 | { 122 | return self.homeDirPath()+"/Library" 123 | } 124 | 125 | func cacheDirPath() -> String? 126 | { 127 | return self.homeDirPath()+"/Caches" 128 | } 129 | 130 | 131 | 132 | 133 | func truncate(by ellipsis:String, width:CGFloat, font:UIFont) -> String 134 | { 135 | if ((self as NSString).size(withAttributes: [NSAttributedString.Key.font:font]).width < width) { 136 | return self 137 | } 138 | 139 | var width = width 140 | let truncatedString = NSMutableString(string: "\(self)\(ellipsis)") 141 | 142 | width -= (ellipsis as NSString).size(withAttributes: [NSAttributedString.Key.font:font]).width 143 | 144 | var range = truncatedString.range(of: ellipsis) 145 | range.length = 1 146 | 147 | while (truncatedString.size(withAttributes: [NSAttributedString.Key.font:font]).width > width && range.location > 0) 148 | { 149 | range.location -= 1 150 | truncatedString.deleteCharacters(in: range) 151 | } 152 | 153 | return truncatedString as String 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /Geofence/Master/KeyboardManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardManager.swift 3 | // KeyboardDemo 4 | // 5 | // Created by Mind-0002 on 21/04/17. 6 | // Copyright © 2017 Mind. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | protocol keyboardManagerDelegate :class { 13 | func keyboardWillShow(notification:Notification , keyboardHeight:CGFloat) 14 | func keyboardDidHide(notification:Notification) 15 | } 16 | 17 | class KeyboardManager { 18 | 19 | private init() {} 20 | 21 | private static var keyboardmanager:KeyboardManager = { 22 | let keyboardmanager = KeyboardManager() 23 | return keyboardmanager 24 | }() 25 | 26 | static func shared() -> KeyboardManager { 27 | return keyboardmanager 28 | } 29 | 30 | weak var delegate:keyboardManagerDelegate? 31 | 32 | static func enableKeyboardNotification() { 33 | 34 | NotificationCenter.default.addObserver(KeyboardManager.shared, selector: #selector(self.keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil) 35 | 36 | NotificationCenter.default.addObserver(KeyboardManager.shared(), selector: #selector(self.keyboardDidHide(notification:)), name: UIResponder.keyboardDidHideNotification, object: nil) 37 | } 38 | 39 | static func disableKeyboardNotification() { 40 | NotificationCenter.default.removeObserver(keyboardmanager, name: UIResponder.keyboardWillShowNotification, object: nil) 41 | 42 | NotificationCenter.default.removeObserver(keyboardmanager, name: UIResponder.keyboardDidHideNotification, object: nil) 43 | } 44 | 45 | @objc private func keyboardWillShow(notification:Notification) { 46 | 47 | let info = notification.userInfo 48 | let keyboardHeight = (info?[UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue.height 49 | 50 | delegate?.keyboardWillShow(notification: notification, keyboardHeight: keyboardHeight) 51 | } 52 | 53 | @objc private func keyboardDidHide(notification:Notification) { 54 | delegate?.keyboardDidHide(notification: notification) 55 | } 56 | 57 | } 58 | 59 | -------------------------------------------------------------------------------- /Geofence/MyLocation.gpx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | // MI 13 | // MI Prking 14 | // MI Exit 1 15 | // MI Mian Exit 16 | // MI okffice 17 | 18 | // Turn 1 Triangle 19 | // Turn 2 Way to bhuyangdev 20 | 21 | // bhuyang 22 | 23 | // Way one Dubey Turn 24 | // C.p 25 | // Trun Shaibagh 26 | // enter Shaibagh Milk 27 | // Home 28 | 29 | 30 | //Amul 31 | 32 | //ArjunGrace 33 | //Home 34 | 35 | 36 | //Subhash 37 | 38 | 39 | 40 | 41 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Geofence/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Geofence 4 | // 5 | // Created by Mind on 03/02/18. 6 | // Copyright © 2018 Mindinventory. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | @IBOutlet weak var txtLat: UITextField! 14 | @IBOutlet weak var txtLong: UITextField! 15 | @IBOutlet weak var txtRange: UITextField! 16 | @IBOutlet weak var txtTitle: UITextField! 17 | @IBOutlet weak var txtMsg: UITextField! 18 | @IBOutlet weak var btnAddnearBy: UIButton! 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | } 23 | 24 | override func didReceiveMemoryWarning() { 25 | super.didReceiveMemoryWarning() 26 | 27 | } 28 | @IBAction func btnAddNearByClk() 29 | { 30 | 31 | let lati = [23.057582,23.057598,23.057347,23.057361,23.057391,23.059445,23.059445,23.052384,23.061001,23.062675,23.065157,23.065206,23.064434] 32 | let longg = [72.534458,72.534541,72.534482,72.534176,72.534724,72.534302,72.534302,72.533718,72.537140,72.537929,72.539007,72.538052,72.537143] 33 | 34 | let Titlls = ["MI","MI Prking","MI Exit 1","MI Mian Exit","MI okffice","Turn 1 Triangle","Turn 2 Way to bhuyangdev","bhuyang","Way one Dubey Turn","C.p","Trun Shaibagh","enter Shaibagh Milk","Home"] 35 | 36 | 37 | let radius = [100.0,50,300,20,30,240,50,100,110,150,170,200,300] 38 | 39 | 40 | for i in 0.. Void in 113 | 114 | })) 115 | 116 | self.present(alert, animated: true, completion: nil) 117 | } 118 | 119 | 120 | } 121 | 122 | -------------------------------------------------------------------------------- /Geofence/ViewController.xcdatamodeld/ViewController.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /GeofenceTests/GeofenceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeofenceTests.swift 3 | // GeofenceTests 4 | // 5 | // Created by Mind on 03/02/18. 6 | // Copyright © 2018 Mindinventory. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Geofence 11 | 12 | class GeofenceTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /GeofenceTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /GeofenceUITests/GeofenceUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeofenceUITests.swift 3 | // GeofenceUITests 4 | // 5 | // Created by Mind on 03/02/18. 6 | // Copyright © 2018 Mindinventory. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class GeofenceUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /GeofenceUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mindinventory 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOS Geofence Demo 2 | You can setup your own geofence by providing latitude and longitude, and it will notify your app when the device enters or leaves the geofence. 3 | 4 | Implementation of geofencing can be useful in various cases, You can define geofence that can trigger a notification whenever user enters or leaves the restaurant, Shopping center or particular region. 5 | 6 | In this geofencing tutorial, you will learn how to use region monitoring in iOS with Swift 4.0 – using the Region Monitoring API in Core Location. 7 | 8 | # Requirements 9 | Minimum OS 10.0 and later 10 | 11 | # Tip 12 | - To test in simulator you can use .GPX file (add GPX file file-> new -> under resource section there will option of GPX file) 13 | - For using GPX file when app is running in simulator there is option in xCode (debug -> simulate location -> select your GPX file) 14 | - In our demo we are using core data to store region data for testing perpose. our core data model looks like below: 15 | 16 | 17 | extension TblGeofence { 18 | @nonobjc public class func fetchRequest() -> NSFetchRequest { 19 | return NSFetchRequest(entityName: "TblGeofence") 20 | } 21 | 22 | @NSManaged public var identifier: String? 23 | @NSManaged public var isFiredOn: Date? 24 | @NSManaged public var latitude: Double 25 | @NSManaged public var longitude: Double 26 | @NSManaged public var msg: String? 27 | @NSManaged public var range: Double 28 | @NSManaged public var title: String? 29 | 30 | } 31 | 32 | 33 | # Manual Installation 34 | 1) First thing we need to do is setup Location manager and Location permissions. 35 | 36 | class AppDelegate: UIResponder, UIApplicationDelegate { 37 | let locationManager = CLLocationManager() 38 | self.enableLocationServices() 39 | } 40 | 41 | 2) Next we need to add Following methode in Appdelegate. 42 | 43 | func enableLocationServices() // to check status of locations 44 | { 45 | locationManager.delegate = self 46 | 47 | switch CLLocationManager.authorizationStatus() { 48 | case .notDetermined: 49 | // Request when-in-use authorization initially 50 | locationManager.requestAlwaysAuthorization() 51 | break 52 | 53 | case .restricted, .denied: 54 | print("status restricted, .denied \(CLLocationManager.authorizationStatus())") 55 | // Disable location features 56 | 57 | break 58 | 59 | case .authorizedWhenInUse: 60 | // Enable basic location features 61 | print("status authorizedWhenInUse \(CLLocationManager.authorizationStatus())") 62 | break 63 | 64 | case .authorizedAlways: 65 | print("status authorizedAlways \(CLLocationManager.authorizationStatus())") 66 | // Enable any of your app's location features 67 | 68 | break 69 | } 70 | 71 | } 72 | 73 | 3) Now set the delegate of the locationManager instance so that we can receive callbacks to respective delegate methods. 74 | 75 | extension AppDelegate: CLLocationManagerDelegate { 76 | 77 | func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) { 78 | if region is CLCircularRegion { 79 | 80 | // Here region.identifier is identifier which you provide during register geofence region. 81 | // Now you can perform action you want to perfrom. that will same for below didExitRegion method 82 | 83 | let geoFance = (TblGeofence.findOrCreate(dictionary: ["identifier":region.identifier]) as? TblGeofence)! 84 | if (!(geoFance.title?.isEmpty)!) 85 | { 86 | self.fireNotification(obj: geoFance, isEnter: true) 87 | } 88 | } 89 | } 90 | 91 | func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) { 92 | if region is CLCircularRegion { 93 | 94 | let geoFance = (TblGeofence.findOrCreate(dictionary: ["identifier":region.identifier]) as? TblGeofence)! 95 | if (!(geoFance.title?.isEmpty)!) 96 | { 97 | self.fireNotification(obj: geoFance, isEnter: false) 98 | } 99 | 100 | } 101 | } 102 | } 103 | 104 | 4) Registering Your Geofences with your location and radios of monitoring region 105 | 106 | func registerGeoFance(obj : TblGeofence) { 107 | 108 | if locationManager.monitoredRegions.count >= 20 // check current monitored region Apple allowed only 20 at time 109 | { 110 | locationManager.stopMonitoring(for: locationManager.monitoredRegions.first!) // if have to add new one then need to remove older then 20 else it will not add new one 111 | } 112 | let centerCoordinate: CLLocationCoordinate2D = CLLocationCoordinate2DMake(obj.latitude, obj.longitude) 113 | let region = CLCircularRegion(center: centerCoordinate, radius: obj.range, identifier: obj.identifier!) // provide radius in meter. also provide uniq identifier for your region. 114 | region.notifyOnEntry = true // based on your requirements 115 | region.notifyOnExit = true 116 | 117 | locationManager.startMonitoring(for: region) // to star monitor region 118 | locationManager.requestState(for: region) // that will check if user is already inside location then it will fire notification instantly. 119 | } 120 | 121 | 5) To trigger notification when enter or leave a registered region, we can use Local Notifications using UNUserNotification framework which is supported only in iOS 10 or later versions 122 | 123 | func fireNotification(obj:TblGeofence , isEnter:Bool) 124 | { 125 | 126 | print("notification will be triggered in five seconds..Hold on tight") 127 | let content = UNMutableNotificationContent() 128 | content.title = obj.title ?? "TitleMissing" 129 | content.subtitle = obj.msg! + "at lat = \(obj.latitude) long = \(obj.longitude)" 130 | content.body = "you are \(isEnter ? "enetr in" : "Exit from") \(obj.title ?? "TitleMissing")" 131 | content.sound = UNNotificationSound.default() 132 | 133 | let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 0.1, repeats: false) 134 | let request = UNNotificationRequest(identifier:obj.identifier! + "++ \(isEnter ? "1" : "0") \(NSDate.timeIntervalSinceReferenceDate)", content: content, trigger: trigger) 135 | 136 | UNUserNotificationCenter.current().delegate = self 137 | UNUserNotificationCenter.current().add(request){(error) in 138 | 139 | if (error != nil){ 140 | 141 | print(error?.localizedDescription ?? "error in notifications") 142 | } 143 | } 144 | } 145 | 5) For perfom any action on notification we need to implement UNUserNotificationCenterDelegate 146 | 147 | extension AppDelegate:UNUserNotificationCenterDelegate{ 148 | 149 | func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { 150 | 151 | print("Tapped in notification") 152 | } 153 | 154 | //This is key callback to present notification while the app is in foreground 155 | func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { 156 | 157 | print("Notification being triggered") 158 | let strDevides = notification.request.identifier.components(separatedBy: "++ ") 159 | 160 | let geoFance = (TblGeofence.findOrCreate(dictionary: ["identifier":strDevides[0]]) as? TblGeofence)! 161 | 162 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(Int(0))) { 163 | let alert = UIAlertController( 164 | title: "\(geoFance.title ?? "BlankTitleRecevied")", 165 | message: "\(notification.request.content.subtitle) \n \(notification.request.content.body)", 166 | preferredStyle: UIAlertControllerStyle.alert 167 | ) 168 | 169 | alert.addAction(UIAlertAction(title: "Okey", style: .cancel, handler: { (alert) -> Void in 170 | 171 | })) 172 | 173 | UIApplication.topViewController()?.present(alert, animated: true, completion: nil) 174 | 175 | } 176 | 177 | //You can either present alert ,sound or increase badge while the app is in foreground too with ios 10 178 | //to distinguish between notifications 179 | if notification.request.identifier == "123456"{ 180 | 181 | completionHandler( [.alert,.sound,.badge]) 182 | 183 | } 184 | } 185 | 186 | } 187 | 188 | 6) Now we have created one ViewController where we can add our geofence region. 189 | - Drag and Drop ViewControlle.swift file in your Projects. 190 | 191 | # LICENSE! 192 | 193 | Geofence is [MIT-licensed](https://github.com/mindinventory/Geofence/blob/master/LICENSE). 194 | 195 | ## Let us know! 196 | We’d be really happy if you send us links to your projects where you use our component. Just send an email to sales@mindinventory.com And do let us know if you have any questions or suggestion regarding our work. 197 | --------------------------------------------------------------------------------