├── .gitignore
├── .swift-version
├── Cartfile
├── Cartfile.private
├── Cartfile.resolved
├── LICENSE.md
├── README.md
├── RxLocationManager.podspec
├── RxLocationManager.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── xcshareddata
│ └── xcschemes
│ ├── RxLocationManager iOS.xcscheme
│ ├── RxLocationManager macOS.xcscheme
│ ├── RxLocationManager tvOS.xcscheme
│ └── RxLocationManager watchOS.xcscheme
├── RxLocationManager.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── RxLocationManagerDemo
├── AppDelegate.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── HeadingUpdateServiceViewController.swift
├── Info.plist
├── MonitoredCircleRegionTableViewCell.swift
├── RegionMonitoringServiceViewController.swift
├── RootViewController.swift
├── SignificantLocationUpdateViewController.swift
├── StandardLocationServiceViewController.swift
└── VisitMonitoringViewController.swift
├── RxLocationManagerTests
├── Fixtures.swift
├── HeadingUpdateServiceTest.swift
├── Info.plist
├── LocationManagerStub.swift
├── MonitoringVisitsServiceTest.swift
├── RegionMonitoringServiceTest.swift
├── SignificantLocationUpdateServiceTest.swift
└── StandardLocationServiceTest.swift
└── Sources
├── Bridge.swift
├── HeadingUpdateService.swift
├── Info.plist
├── MonitoringVisitsService.swift
├── RegionMonitoringService.swift
├── RxLocationManager.swift
├── SignificantLocationUpdateService.swift
├── StandardLocationService.swift
└── nextId.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | ## OS X Finder
2 | .DS_Store
3 |
4 | ## Build generated
5 | build/
6 | DerivedData
7 |
8 | ## Various settings
9 | *.pbxuser
10 | !default.pbxuser
11 | *.mode1v3
12 | !default.mode1v3
13 | *.mode2v3
14 | !default.mode2v3
15 | *.perspectivev3
16 | !default.perspectivev3
17 | xcuserdata
18 |
19 | ## Other
20 | *.xccheckout
21 | *.moved-aside
22 | *.xcuserstate
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 |
29 | # Swift Package Manager
30 | .build/
31 |
32 | # Carthage
33 | Carthage/Build
34 | Carthage/Checkouts
35 |
36 | .gitmodules
37 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
2 |
--------------------------------------------------------------------------------
/Cartfile:
--------------------------------------------------------------------------------
1 | github "ReactiveX/RxSwift" ~> 5.0
2 |
--------------------------------------------------------------------------------
/Cartfile.private:
--------------------------------------------------------------------------------
1 | github "Quick/Nimble"
2 |
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "Quick/Nimble" "v8.0.2"
2 | github "ReactiveX/RxSwift" "5.0.1"
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | **The MIT License**
2 | **Copyright © 2016 Yonny Hao**
3 | **All rights reserved.**
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A Reactive LocationManager in Swift
2 |
3 | [](https://cocoapods.org)
4 | [](https://github.com/Carthage/Carthage)
5 | [](https://github.com/popduke/RxLocationManager)
6 |
7 | ## Introduction
8 | You may find CLLocationManager awkward to use if you adopt [RP](http://reactivex.io/)([RxSwift](https://github.com/ReactiveX/RxSwift)) paradigm to develop apps. RxLocationManager is an attempt to create a "reactive" skin around CLLocationManager, so that you don't need to worry about things like conform your view controller to CLLocationManagerDelegate which sometimes feels unnatural, where to put CLLocationManager instance(e.g. AppDelegate) for easily referencing, etc. Everything is behind RxLocationManager class and its static methods and variables. Internally RxLocationManager has multiple sharing CLLocationManager+Delegate instances, and manage them efficiently in terms of memory usage and battery life. Instead of providing an "all-in-one" class like CLLocationManager does, RxLocationManager divides properties/methods into several groups based on their relativity, for example, location related APIs go into *StandardLocationService* class, heading update related APIs go into *HeadingUpdateService* class, region monitoring related APIs go into *RegionMonitoringService* class which also includes ranging beacons capability, and visits monitoring related APIs go into *MonitoringVisitsService*, so it's more clear to use.
9 |
10 | ## Requirement
11 | * iOS 8.0+ | macOS 10.10+ | tvOS 9.0+ | watchOS 2.0+
12 | * Xcode 8.1+
13 | * Swift 3.0+ (for Swift 2 support, see branch [Swift-2.X](https://github.com/popduke/RxLocationManager/tree/Swift-2.x))
14 | * RxSwift 3.0+
15 |
16 | ## Installation
17 | ### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html)
18 | Add RxLocationManager dependency to your Podfile
19 | ```
20 | # Podfile
21 | use_frameworks!
22 |
23 | # replace YOUR_TARGET_NAME with yours
24 | target 'YOUR_TARGET_NAME' do
25 | pod 'RxLocationManager', '~> 2.0'
26 | end
27 | ```
28 | and run
29 | ```
30 | $ pod install
31 | ```
32 |
33 | ### [Carthage](https://github.com/Carthage/Carthage)
34 | Add following line to `Cartfile`
35 |
36 | ```
37 | github "popduke/RxLocationManager" ~> 2.0
38 | ```
39 | and run
40 |
41 | ```
42 | $ carthage update
43 | ```
44 | ### [Git submodules](https://git-scm.com/docs/git-submodule)
45 | * Run following line to add RxLocationManager as a submodule
46 |
47 | ```
48 | $ git submodule add git@github.com:popduke/RxLocationManager.git
49 | ```
50 |
51 | * Drag `RxLocationManager.xcodeproj` into Project Navigator
52 | * Go to `Project > Targets > Build Phases > Link Binary With Libraries`, click `+` and select `RxLocationManager [Platform]` targets
53 |
54 | ## Usage
55 | **:pushpin: Always consult official document of [*CLLocationManager*](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/index.html#//apple_ref/occ/cl/CLLocationManager) to learn how to configure it to work in different modes :pushpin:**
56 |
57 | Add below line to import RxLocationManager module
58 | ```
59 | import RxLocationManager
60 | ```
61 |
62 | ### Observe Location Service's enable/disable status change
63 |
64 | ```
65 | RxLocationManager.enable
66 | .map{
67 | //$0 is Boolean
68 | return $0 ? "enabled" : "disabled"
69 | }
70 | .subscribe(onNext:{
71 | print("Location Service is \($0)")
72 | })
73 | .addDisposableTo(disposeBag)
74 | ```
75 |
76 | ### Observe app's authorization status change
77 |
78 | ```
79 | RxLocationManager.authorizationStatus
80 | .subscribe(onNext:{
81 | //$0 is CLAuthorizationStatus
82 | print("Current authorization status is \($0)")
83 | })
84 | .addDisposableTo(disposeBag)
85 | ```
86 |
87 | ### Request authorization to your app
88 | ```
89 | //ask for WhenInUse authorization
90 | RxLocationManager.requestWhenInUseAuthorization()
91 | //ask for Always authorization
92 | RxLocationManager.requestAlwaysAuthorization()
93 | ```
94 |
95 | ### Determine service availability
96 | ```
97 | #if os(iOS) || os(OSX)
98 | RxLocationManager.significantLocationChangeMonitoringAvailable
99 | RxLocationManager.isMonitoringAvailableForClass(regionClass: AnyClass) -> Bool
100 | #endif
101 |
102 | #if os(iOS)
103 | RxLocationManager.deferredLocationUpdatesAvailable
104 | RxLocationManager.headingAvailable
105 | RxLocationManager.isRangingAvailable
106 | #endif
107 | ```
108 |
109 | ### Standard Location Service
110 | *StandardLocationService* contains two main *Observable*s: *Located* and *Locating*, *Located* reports only one *CLLocation* object per subscription and complete, representing the current determined location of device; *Locating* reports series of *CLLocation* objects upon observing, representing the changing location of device. Multiple subscriptions share a single underlying CLLocationManager object, RxLocationManager starts location updating when first subscription is made and stops it after last subscription is disposed.
111 | #### Determine current location of device
112 |
113 | ```
114 | // RxLocationManager.Standard is a shared standard location service instance
115 | #if os(iOS) || os(watchOS) || os(tvOS)
116 | RxLocationManager.Standard.located.subscribe(
117 | onNext:{
118 | // the event will only be triggered once to report current determined location of device
119 | print("Current Location is \($0)")
120 | },
121 | onError:{
122 | // in case some error occurred during determining device location, e.g. LocationUnknown
123 | },
124 | onCompleted:{
125 | // completed event will get triggered after location is reported successfully
126 | print("Subscription is Completed")
127 | })
128 | .addDisposableTo(disposeBag)
129 | #endif
130 | ```
131 |
132 | #### Monitoring location update of device
133 | ```
134 | #if os(iOS) || os(OSX) || os(watchOS)
135 | //available in watchOS 3.0
136 | RxLocationManager.Standard.locating.subscribe(
137 | onNext:{
138 | // series of events will be delivered during subscription
139 | print("Current Location is \($0)")
140 | },
141 | onError:{
142 | // LocationUnknown error will be ignored, and other errors reported
143 | },
144 | onCompleted:{
145 | // no complete event
146 | })
147 | .addDisposableTo(disposeBag)
148 | #endif
149 | ```
150 | #### Configuration
151 | Before start subscribing to *located* or *locating*, you can also configure the standard location service instance through below chaining style APIs
152 | ```
153 | RxLocationManager.Standard.distanceFilter(distance: CLLocationDistance) -> StandardLocationService
154 | RxLocationManager.Standard.desiredAccuracy(desiredAccuracy: CLLocationAccuracy) -> StandardLocationService
155 |
156 | #if os(iOS)
157 | RxLocationManager.Standard.allowsBackgroundLocationUpdates(allow : Bool) -> StandardLocationService
158 | RxLocationManager.Standard.activityType(type: CLActivityType) -> StandardLocationService
159 | #endif
160 | ```
161 |
162 | #### Enable auto-paused mode for location delivery, and observe the notification of pausing state change
163 | ```
164 | #if os(iOS)
165 | RxLocationManager.Standard.pausesLocationUpdatesAutomatically(true)
166 |
167 | RxLocationManager.Standard.isPaused
168 | .map{
169 | //$0 is Boolean
170 | return $0 ? "Paused" : "Resumed"
171 | }
172 | .subscribe(
173 | onNext:{
174 | print("Location Updating is \($0)")
175 | }
176 | )
177 | .addDisposableTo(disposeBag)
178 | #endif
179 | ```
180 |
181 | #### Setup/remove deferred location update condition and observe when condition is satisfied or finished with error
182 | ```
183 | #if os(iOS)
184 | //Setup deferred location update condition
185 | RxLocationManager.Standard.allowDeferredLocationUpdates(untilTraveled:100, timeout: 120)
186 |
187 | //Remove current deferred update condition
188 | RxLocationManager.Standard.disallowDeferredLocationUpdates()
189 |
190 | //Observe the event when condition is satisfied or finished with error
191 | RxLocationManager.Standard.deferredUpdateFinished
192 | .map{
193 | //$0 is NSError?
194 | return $0 == nil ? "Finished" : "Finished with error code \($0.code) in \($0.domain)"
195 | }
196 | .subscribe(
197 | onNext:{
198 | error in
199 | print("Location Updating is \($0)")
200 | }
201 | )
202 | .addDisposableTo(disposeBag)
203 | #endif
204 | ```
205 |
206 | #### Multiple standard location services
207 | In some cases you need more than one standard location service in your app, which configured differently, you can create a new one by cloning from the shared
208 | ```
209 | var anotherStandardLocationService = RxLocationManager.Standard.clone()
210 | anotherStandardLocationService.distanceFilter(100).desiredAccuracy(50)
211 | ```
212 |
213 | ### Significant Location Update Service
214 |
215 | *SignificantLocationUpdateService* contains only one *Observable*: *locating*, which reports series of *CLLocation* objects upon observing, representing the significant location change of device. Multiple subscriptions share a single underlying CLLocationManager object, RxLocationManager starts monitoring significant location change when first subscription is made and stops it after last subscription is disposed.
216 | ```
217 | #if os(iOS) || os(OSX)
218 | // RxLocationManager.SignificantLocation is the shared significant location update service instance
219 | RxLocationManager.SignificantLocation.locating.subscribe(
220 | onNext:{
221 | print("Current Location is \($0)")
222 | },
223 | onError:{
224 | // in case errors
225 | },
226 | onCompleted:{
227 | // no complete event
228 | }
229 | )
230 | .addDisposableTo(disposeBag)
231 | #endif
232 | ```
233 |
234 | ### Heading Update Service
235 |
236 | *HeadingUpdateService* contains only one *Observable*: *heading*, which reports series of *CLHeading* objects upon observing, representing heading change of device. Multiple subscriptions share a single underlying CLLocationManager object, RxLocationManager starts monitoring device heading change when first subscription is made and stops it after last subscription is disposed.
237 |
238 | #### Observe heading change of device
239 | ```
240 | #if os(iOS)
241 | // RxLocationManager.HeadingUpdate is the shared heading update service instance
242 | RxLocationManager.HeadingUpdate.heading.subscribe(
243 | onNext:{
244 | // series of events will be delivered during subscription
245 | print("Current heading is \($0)")
246 | },
247 | onCompleted:{
248 | // no complete event
249 | },
250 | onError:{
251 | // in case errors
252 | }
253 | )
254 | .addDisposableTo(disposeBag)
255 | #endif
256 | ```
257 |
258 | #### Configuration
259 | Before start subscribing to *heading*, you can also configure the heading update service instance through below chaining style APIs
260 | ```
261 | #if os(iOS)
262 | RxLocationManager.HeadingUpdate.headingFilter(degrees:CLLocationDegrees) -> HeadingUpdateService
263 | RxLocationManager.HeadingUpdate.headingOrientation(degrees:CLDeviceOrientation) -> HeadingUpdateService
264 | RxLocationManager.HeadingUpdate.displayHeadingCalibration(should:Bool) -> HeadingUpdateService
265 |
266 | //Use following to methods to start/stop location updating, so that true heading value will be reported
267 | RxLocationManager.HeadingUpdate.startTrueHeading(withParams:(distanceFilter:CLLocationDistance, desiredAccuracy:CLLocationAccuracy))
268 | RxLocationManager.HeadingUpdate.stopTrueHeading()
269 | #endif
270 | ```
271 |
272 | #### Dismiss heading calibration display if any
273 | ```
274 | #if os(iOS)
275 | RxLocationManager.HeadingUpdate.dismissHeadingCalibrationDisplay()
276 | #endif
277 | ```
278 |
279 | #### Multiple heading update services
280 | In some cases you need more than one heading update service in your app, which configured differently, you can create a new one by cloning from the shared
281 | ```
282 | var anotherHeadingUpdateService = RxLocationManager.HeadingUpdate.clone()
283 | anotherHeadingUpdateService.distanceFilter(100).desiredAccuracy(50)
284 | ```
285 | ### Region Monitoring Service
286 |
287 | #### Observe the changes to the collection of current monitored regions
288 | ```
289 | #if os(iOS) || os(OSX)
290 | // methods to start|stop monitoring regions
291 | RxLocationManager.RegionMonitoring.startMonitoringForRegions(regions: [CLRegion]) -> RegionMonitoringService
292 | RxLocationManager.RegionMonitoring.stopMonitoringForRegions(regions: [CLRegion]) -> RegionMonitoringService
293 | RxLocationManager.RegionMonitoring.stopMonitoringForAllRegions() -> RegionMonitoringService
294 |
295 | RxLocationManager.RegionMonitoring.monitoredRegions.subscribe(
296 | onNext:{
297 | //happens no matter when new region is added or existing one gets removed from the monitored regions set
298 | regions in
299 | print("Current monitoring \(regions.count) regions")
300 | }
301 | )
302 | .addDisposableTo(disposeBag)
303 | #endif
304 | ```
305 |
306 | #### Observe region enter/exit event
307 | ```
308 | #if os(iOS) || os(OSX)
309 | RxLocationManager.RegionMonitoring.entering.subscribe(
310 | onNext:{
311 | region in
312 | print("Device is entering the region: \(region.identifier)")
313 | }
314 | )
315 | .addDisposableTo(disposeBag)
316 |
317 | RxLocationManager.RegionMonitoring.exiting.subscribe(
318 | onNext:{
319 | region in
320 | print("Device is leaving the region: \(region.identifier)")
321 | }
322 | )
323 | .addDisposableTo(disposeBag)
324 | #endif
325 | ```
326 |
327 | #### Ask for the current state of monitored regions
328 | ```
329 | RxLocationManager.RegionMonitoring.requestRegionsState(regions:[CLRegion]) -> RegionMonitoringService
330 | RxLocationManager.RegionMonitoring.determinedRegionState.subscribe(
331 | onNext:{
332 | region, state in
333 | print("the region: \(region.identifier) is in state: \(state.rawValue)")
334 | }
335 | )
336 | .addDisposableTo(disposeBag)
337 | ```
338 |
339 | #### Start/stop ranging beacons in range
340 | ```
341 | #if os(iOS)
342 | RxLocationManager.RegionMonitoring.startRangingBeaconsInRegion(region: CLBeaconRegion)
343 | RxLocationManager.RegionMonitoring.stopRangingBeaconsInRegion(region: CLBeaconRegion)
344 | #endif
345 | ```
346 |
347 | #### Observe ranged beacons
348 | ```
349 | #if os(iOS)
350 | RxLocationManager.RegionMonitoring.ranging.subscribe(
351 | onNext:{
352 | beacons, inRegion in
353 | print("\(beacons.count) beacons ranged in range:\(inRange.identifier)")
354 | }
355 | )
356 | .addDisposableTo(disposeBag)
357 | #endif
358 | ```
359 |
360 | ### Monitoring Visits Service
361 |
362 | #### Start/stop monitoring visits
363 | ```
364 | #if os(iOS)
365 | RxLocationManager.VisitMonitoring.startMonitoringVisits()
366 | RxLocationManager.VisitMonitoring.stopMonitoringVisits()
367 | #endif
368 | ```
369 |
370 | #### Observe visit events
371 | ```
372 | #if os(iOS)
373 | RxLocationManager.VisitMonitoring.visiting.subscribe(
374 | onNext:{
375 | visit in
376 | print("coordinate: \(visit.coordinate.longitude),\(visit.coordinate.latitude)")
377 | }
378 | )
379 | .addDisposableTo(disposeBag)
380 | #endif
381 | ```
382 |
383 | ## MIT License
384 |
--------------------------------------------------------------------------------
/RxLocationManager.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = "RxLocationManager"
3 | spec.version = "3.0.1"
4 | spec.summary = "Reactive Style Location Manager for iOS, macOS, watchOS, tvOS"
5 | spec.description = "If you programs in functional reactive style in iOS, RxLocationManager makes location management a lot easier comparing to CLLocationManager"
6 | spec.homepage = "https://github.com/popduke/RxLocationManager"
7 | spec.license = { type: 'MIT', file: 'LICENSE.md' }
8 | spec.authors = { "Yonny Hao" => 'popduke@gmail.com' }
9 |
10 | spec.ios.deployment_target = '8.0'
11 | spec.osx.deployment_target = '10.10'
12 | spec.watchos.deployment_target = '2.0'
13 | spec.tvos.deployment_target = '9.0'
14 |
15 | spec.frameworks = "Foundation", "CoreLocation"
16 | spec.requires_arc = true
17 | spec.source = { git: "https://github.com/popduke/RxLocationManager.git", tag: spec.version.to_s }
18 | spec.source_files = 'sources/*.{h,swift}'
19 |
20 | spec.dependency "RxSwift", "~> 5.0"
21 | end
22 |
--------------------------------------------------------------------------------
/RxLocationManager.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RxLocationManager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/RxLocationManager.xcodeproj/xcshareddata/xcschemes/RxLocationManager iOS.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 |
75 |
81 |
82 |
83 |
84 |
85 |
86 |
92 |
93 |
99 |
100 |
101 |
102 |
104 |
105 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/RxLocationManager.xcodeproj/xcshareddata/xcschemes/RxLocationManager macOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/RxLocationManager.xcodeproj/xcshareddata/xcschemes/RxLocationManager tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/RxLocationManager.xcodeproj/xcshareddata/xcschemes/RxLocationManager watchOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/RxLocationManager.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/RxLocationManager.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/RxLocationManagerDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // RxLocationManagerDemo
4 | //
5 | // Created by Yonny Hao on 16/7/10.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreLocation
11 |
12 | extension NSError{
13 | open override var description: String{
14 | get{
15 | switch domain {
16 | case "kCLErrorDomain":
17 | switch CLError(_nsError:self).code {
18 | case .locationUnknown:
19 | return "Location Unknown"
20 | case .denied:
21 | return "Denied"
22 | case .network:
23 | return "Network"
24 | case .headingFailure:
25 | return "Heading Failure"
26 | case .regionMonitoringDenied:
27 | return "Region Monitoring Denied"
28 | case .regionMonitoringFailure:
29 | return "Region Monitoring Failure"
30 | case .regionMonitoringSetupDelayed:
31 | return "Region Monitoring Setup Delayed"
32 | case .regionMonitoringResponseDelayed:
33 | return "Region Monitoring Response Delayed"
34 | case .geocodeFoundNoResult:
35 | return "Geocode Found No Result"
36 | case .geocodeFoundPartialResult:
37 | return "Geocode Found Partial Result"
38 | case .geocodeCanceled:
39 | return "Geocode Canceled"
40 | case .deferredFailed:
41 | return "Deferred Failed"
42 | case .deferredNotUpdatingLocation:
43 | return "Deferred Not Updating Location"
44 | case .deferredAccuracyTooLow:
45 | return "Deferred Accuracy Too Low"
46 | case .deferredDistanceFiltered:
47 | return "Deferred Distance Filtered"
48 | case .deferredCanceled:
49 | return "Deferred Canceled"
50 | case .rangingUnavailable:
51 | return "Ranging Unavailable"
52 | case .rangingFailure:
53 | return "Ranging Failure"
54 | }
55 | default:
56 | return self.description
57 | }
58 | }
59 | }
60 | }
61 |
62 | @UIApplicationMain
63 | class AppDelegate: UIResponder, UIApplicationDelegate {
64 |
65 | var window: UIWindow?
66 |
67 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
68 | // Override point for customization after application launch.
69 | return true
70 | }
71 |
72 | func applicationWillResignActive(_ application: UIApplication) {
73 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
74 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
75 | }
76 |
77 | func applicationDidEnterBackground(_ application: UIApplication) {
78 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
79 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
80 | }
81 |
82 | func applicationWillEnterForeground(_ application: UIApplication) {
83 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
84 | }
85 |
86 | func applicationDidBecomeActive(_ application: UIApplication) {
87 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
88 | }
89 |
90 | func applicationWillTerminate(_ application: UIApplication) {
91 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
92 | }
93 |
94 |
95 | }
96 |
97 |
--------------------------------------------------------------------------------
/RxLocationManagerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/RxLocationManagerDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/RxLocationManagerDemo/HeadingUpdateServiceViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HeadingUpdateServiceViewController.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/13.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreLocation
11 | import RxSwift
12 | import RxLocationManager
13 |
14 | class HeadingUpdateServiceViewController: UIViewController {
15 |
16 | @IBOutlet weak var magneticHeadingValueLbl: UILabel!
17 |
18 | @IBOutlet weak var trueHeadingValueLbl: UILabel!
19 |
20 | @IBOutlet weak var headingAccuracyValueLbl: UILabel!
21 |
22 | @IBOutlet weak var timestampValueLbl: UILabel!
23 |
24 | @IBOutlet weak var toggleHeadingUpdateBtn: UIButton!
25 |
26 | @IBOutlet weak var trueHeadingSwitch: UISwitch!
27 |
28 | private var disposeBag: DisposeBag!
29 |
30 | private var headingSubscription: Disposable?
31 |
32 | override func viewDidLoad() {
33 | super.viewDidLoad()
34 | }
35 |
36 | override func viewWillAppear(_ animated: Bool) {
37 | disposeBag = DisposeBag()
38 | trueHeadingSwitch.rx.value
39 | .subscribe(onNext:{
40 | if $0{
41 | RxLocationManager.HeadingUpdate.startTrueHeading(nil)
42 | }else{
43 | RxLocationManager.HeadingUpdate.stopTrueHeading()
44 | }
45 | })
46 | .addDisposableTo(disposeBag)
47 |
48 | toggleHeadingUpdateBtn.rx.tap
49 | .subscribe{
50 | [unowned self]
51 | _ in
52 | if self.headingSubscription == nil {
53 | self.toggleHeadingUpdateBtn.setTitle("Stop", for: .normal)
54 | self.headingSubscription = RxLocationManager.HeadingUpdate.heading
55 | .subscribe(onNext:{
56 | [unowned self]
57 | heading in
58 | self.magneticHeadingValueLbl.text = heading.magneticHeading.description
59 | self.trueHeadingValueLbl.text = heading.trueHeading.description
60 | self.headingAccuracyValueLbl.text = heading.headingAccuracy.description
61 | self.timestampValueLbl.text = heading.timestamp.description
62 | })
63 | }else{
64 | self.headingSubscription?.dispose()
65 | self.toggleHeadingUpdateBtn.setTitle("Start", for: .normal)
66 | self.magneticHeadingValueLbl.text = ""
67 | self.trueHeadingValueLbl.text = ""
68 | self.headingAccuracyValueLbl.text = ""
69 | self.timestampValueLbl.text = ""
70 | self.headingSubscription!.dispose()
71 | self.headingSubscription = nil
72 | }
73 | }
74 | .addDisposableTo(disposeBag)
75 | }
76 |
77 | override func viewDidDisappear(_ animated: Bool) {
78 | disposeBag = nil
79 | headingSubscription?.dispose()
80 | headingSubscription = nil
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/RxLocationManagerDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSLocationAlwaysUsageDescription
26 | RxLocationManagerDemo app needs authorization all the time
27 | NSLocationWhenInUseUsageDescription
28 | RxLocationManagerDemo app requests authorization when in use
29 | UIBackgroundModes
30 |
31 | location
32 |
33 | UILaunchStoryboardName
34 | LaunchScreen
35 | UIMainStoryboardFile
36 | Main
37 | UIRequiredDeviceCapabilities
38 |
39 | armv7
40 |
41 | UISupportedInterfaceOrientations
42 |
43 | UIInterfaceOrientationPortrait
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/RxLocationManagerDemo/MonitoredCircleRegionTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MonitoredCircleRegionTableViewCell.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/14.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreLocation
11 |
12 | class MonitoredCircleRegionTableViewCell: UITableViewCell {
13 | @IBOutlet weak var centerCoordLbl: UILabel!
14 |
15 | @IBOutlet weak var inoutStatusLbl: UILabel!
16 |
17 | var monitoredRegion: CLCircularRegion?{
18 | didSet{
19 | if let region = monitoredRegion{
20 | centerCoordLbl.text = "\(region.center.latitude),\(region.center.longitude)"
21 | }
22 | }
23 | }
24 |
25 | override func awakeFromNib() {
26 | super.awakeFromNib()
27 | // Initialization code
28 | }
29 |
30 | override func setSelected(_ selected: Bool, animated: Bool) {
31 | super.setSelected(selected, animated: animated)
32 |
33 | // Configure the view for the selected state
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/RxLocationManagerDemo/RegionMonitoringServiceViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RegionMonitoringServiceViewController.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/14.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreLocation
11 | import RxLocationManager
12 | import RxSwift
13 |
14 | extension CLRegionState: CustomStringConvertible{
15 | public var description:String{
16 | get{
17 | switch self{
18 | case .unknown:
19 | return "Unknown"
20 | case .inside:
21 | return "IN"
22 | case .outside:
23 | return "OUT"
24 | }
25 | }
26 | }
27 | }
28 |
29 | class RegionMonitoringServiceViewController: UIViewController {
30 |
31 | @IBOutlet weak var addRegionBtn: UIButton!
32 |
33 | @IBOutlet weak var errorLbl: UILabel!
34 |
35 | @IBOutlet weak var monitoredRangesTableView: UITableView!
36 |
37 | private var disposeBag:DisposeBag!
38 |
39 | override func viewWillAppear(_ animated: Bool) {
40 | disposeBag = DisposeBag()
41 | addRegionBtn.rx.tap
42 | .subscribe{
43 | [unowned self]
44 | _ in
45 | self.errorLbl.text = ""
46 | RxLocationManager.Standard.located
47 | .do(onError:{
48 | self.errorLbl.text = ($0 as NSError).description
49 | })
50 | .subscribe(onNext:{
51 | location in
52 | _ = RxLocationManager.RegionMonitoring.startMonitoringForRegions([CLCircularRegion(center: location.coordinate, radius: 20, identifier: location.timestamp.description)])
53 | })
54 | .addDisposableTo(self.disposeBag)
55 | }
56 | .addDisposableTo(disposeBag)
57 |
58 | RxLocationManager.RegionMonitoring.error
59 | .subscribe(onNext:{
60 | [unowned self]
61 | region, error in
62 | self.errorLbl.text = error.description
63 | })
64 | .addDisposableTo(disposeBag)
65 |
66 | RxLocationManager.RegionMonitoring.monitoredRegions
67 | .bindTo(monitoredRangesTableView.rx.items) { (tableView, row, monitoredRegion) in
68 | let cell = tableView.dequeueReusableCell(withIdentifier: "MonitoredRegionTableViewCell")! as! MonitoredCircleRegionTableViewCell
69 | cell.monitoredRegion = monitoredRegion as? CLCircularRegion
70 | return cell
71 | }
72 | .addDisposableTo(disposeBag)
73 |
74 | monitoredRangesTableView.rx.itemDeleted
75 | .subscribe(onNext:{
76 | [unowned self]
77 | indexOfRemovedRegion in
78 | let removedRegionCell = self.monitoredRangesTableView.cellForRow(at: indexOfRemovedRegion) as! MonitoredCircleRegionTableViewCell
79 | _ = RxLocationManager.RegionMonitoring.stopMonitoringForRegions([removedRegionCell.monitoredRegion!])
80 | })
81 | .addDisposableTo(disposeBag)
82 |
83 | RxLocationManager.RegionMonitoring.entering
84 | .subscribe(onNext:{
85 | [unowned self]
86 | enteredRegion in
87 | for i in 0 ..< self.monitoredRangesTableView.numberOfSections {
88 | for j in 0 ..< self.monitoredRangesTableView.numberOfRows(inSection: i){
89 | if let cell = self.monitoredRangesTableView.cellForRow(at:IndexPath(row: j, section: i)){
90 | let monitoredCell = cell as! MonitoredCircleRegionTableViewCell
91 | if monitoredCell.monitoredRegion!.identifier == enteredRegion.identifier{
92 | monitoredCell.inoutStatusLbl!.text = "IN"
93 | }
94 | }
95 | }
96 | }
97 | })
98 | .addDisposableTo(disposeBag)
99 |
100 | RxLocationManager.RegionMonitoring.exiting
101 | .subscribe(onNext:{
102 | [unowned self]
103 | exitedRegion in
104 | for i in 0 ..< self.monitoredRangesTableView.numberOfSections {
105 | for j in 0 ..< self.monitoredRangesTableView.numberOfRows(inSection: i){
106 | if let cell = self.monitoredRangesTableView.cellForRow(at:IndexPath(row: j, section: i)){
107 | let monitoredCell = cell as! MonitoredCircleRegionTableViewCell
108 | if monitoredCell.monitoredRegion!.identifier == exitedRegion.identifier{
109 | monitoredCell.inoutStatusLbl!.text = "OUT"
110 | }
111 | }
112 | }
113 | }
114 | })
115 | .addDisposableTo(disposeBag)
116 |
117 | monitoredRangesTableView.rx.itemSelected
118 | .subscribe(onNext:{
119 | [unowned self]
120 | indexPath in
121 | let monitoredCell = self.monitoredRangesTableView.cellForRow(at:indexPath) as! MonitoredCircleRegionTableViewCell
122 | _ = RxLocationManager.RegionMonitoring.requestRegionsState([monitoredCell.monitoredRegion!])
123 | })
124 | .addDisposableTo(disposeBag)
125 |
126 | RxLocationManager.RegionMonitoring.determinedRegionState
127 | .subscribe(onNext:{
128 | [unowned self]
129 | region, state in
130 | for i in 0 ..< self.monitoredRangesTableView.numberOfSections {
131 | for j in 0 ..< self.monitoredRangesTableView.numberOfRows(inSection: i){
132 | if let cell = self.monitoredRangesTableView.cellForRow(at: IndexPath(row: j, section: i)){
133 | let monitoredCell = cell as! MonitoredCircleRegionTableViewCell
134 | if monitoredCell.monitoredRegion!.identifier == region.identifier{
135 | monitoredCell.inoutStatusLbl!.text = state.description
136 | }
137 | }
138 | }
139 | }
140 | })
141 | .addDisposableTo(disposeBag)
142 | }
143 |
144 | override func viewDidDisappear(_ animated: Bool) {
145 | disposeBag = nil
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/RxLocationManagerDemo/RootViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // RxLocationManagerDemo
4 | //
5 | // Created by Yonny Hao on 16/7/10.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreLocation
11 | import RxSwift
12 | import RxCocoa
13 | import RxLocationManager
14 |
15 | class RootViewController: UIViewController {
16 | var disposeBag = DisposeBag()
17 |
18 | @IBOutlet weak var requestWhenInUseBtn: UIButton!
19 |
20 | @IBOutlet weak var requestAlwaysBtn: UIButton!
21 |
22 | @IBOutlet weak var standardLocationServiceBtn: UIButton!
23 |
24 | @IBOutlet weak var locationServiceStatusLbl: UILabel!
25 |
26 | @IBOutlet weak var significantLocationUpdateBtn: UIButton!
27 |
28 | @IBOutlet weak var headingUpdateServiceBtn: UIButton!
29 |
30 | @IBOutlet weak var regionMonitoringServiceBtn: UIButton!
31 |
32 | @IBOutlet weak var authStatusLbl: UILabel!
33 |
34 | @IBOutlet weak var visitMonitoringServiceBtn: UIButton!
35 |
36 | override func viewDidLoad() {
37 | super.viewDidLoad()
38 | let isAuthorized = RxLocationManager.authorizationStatus.map{return $0 == .authorizedAlways || $0 == .authorizedWhenInUse}
39 |
40 | isAuthorized.subscribe(standardLocationServiceBtn.rx.isEnabled).addDisposableTo(disposeBag)
41 | isAuthorized.subscribe(visitMonitoringServiceBtn.rx.isEnabled).addDisposableTo(disposeBag)
42 |
43 | isAuthorized.map{
44 | $0 && RxLocationManager.significantLocationChangeMonitoringAvailable
45 | }
46 | .bindTo(significantLocationUpdateBtn.rx.isEnabled)
47 | .addDisposableTo(disposeBag)
48 |
49 | isAuthorized.map{
50 | $0 && RxLocationManager.headingAvailable
51 | }
52 | .bindTo(headingUpdateServiceBtn.rx.isEnabled)
53 | .addDisposableTo(disposeBag)
54 |
55 | isAuthorized.map{
56 | $0 && RxLocationManager.isMonitoringAvailableForClass(regionClass: CLCircularRegion.self)
57 | }
58 | .bindTo(regionMonitoringServiceBtn.rx.isEnabled)
59 | .addDisposableTo(disposeBag)
60 |
61 | requestWhenInUseBtn.rx.tap
62 | .subscribe(
63 | onNext:{
64 | _ in
65 | RxLocationManager.requestWhenInUseAuthorization()
66 | })
67 | .addDisposableTo(disposeBag)
68 |
69 | requestAlwaysBtn.rx.tap
70 | .subscribe(
71 | onNext:{
72 | _ in
73 | RxLocationManager.requestAlwaysAuthorization()
74 | })
75 | .addDisposableTo(disposeBag)
76 |
77 | RxLocationManager.enabled
78 | .map{return "Location Service is \($0 ? "ON":"OFF")"}
79 | .bindTo(locationServiceStatusLbl.rx.text)
80 | .addDisposableTo(disposeBag)
81 |
82 | RxLocationManager.authorizationStatus
83 | .map {
84 | switch($0){
85 | case .notDetermined:
86 | return "NotDetermined"
87 | case .restricted:
88 | return "Restricted"
89 | case .denied:
90 | return "Denied"
91 | case .authorizedAlways:
92 | return "AuthorizedAlways"
93 | case .authorizedWhenInUse:
94 | return "AuthorizedWhenInUse"
95 | }
96 | }
97 | .map {
98 | return "Authorization Status is " + $0
99 | }
100 | .bindTo(authStatusLbl.rx.text)
101 | .addDisposableTo(disposeBag)
102 |
103 | }
104 |
105 | override func didReceiveMemoryWarning() {
106 | super.didReceiveMemoryWarning()
107 | // Dispose of any resources that can be recreated.
108 | }
109 |
110 |
111 | }
112 |
113 |
--------------------------------------------------------------------------------
/RxLocationManagerDemo/SignificantLocationUpdateViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SignificantLocationUpdateViewController.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/12.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import RxLocationManager
11 | import RxSwift
12 | import RxCocoa
13 |
14 | class SignificantLocationUpdateViewController: UIViewController {
15 |
16 | @IBOutlet weak var currentLocationLbl: UILabel!
17 |
18 | @IBOutlet weak var toggleSignificantLocationUpdateBtn: UIButton!
19 |
20 | private var disposeBag:DisposeBag!
21 |
22 | private var locatingSubscription: Disposable?
23 | override func viewWillAppear(_ animated: Bool) {
24 | disposeBag = DisposeBag()
25 | toggleSignificantLocationUpdateBtn.rx.tap
26 | .subscribe{
27 | [unowned self]
28 | _ in
29 | if self.locatingSubscription == nil {
30 | self.toggleSignificantLocationUpdateBtn.setTitle("Stop", for: .normal)
31 | self.locatingSubscription = RxLocationManager.SignificantLocation.locating
32 | .map{
33 | let coord = $0.last!;
34 | return "\(coord.coordinate.latitude),\(coord.coordinate.longitude)"
35 | }
36 | .catchErrorJustReturn("")
37 | .subscribe(self.currentLocationLbl.rx.text)
38 | }else{
39 | self.toggleSignificantLocationUpdateBtn.setTitle("Start", for: .normal)
40 | self.currentLocationLbl.text = ""
41 | self.locatingSubscription!.dispose()
42 | self.locatingSubscription = nil
43 | }
44 | }
45 | .addDisposableTo(disposeBag)
46 | }
47 | override func viewDidDisappear(_ animated: Bool) {
48 | disposeBag = nil
49 | locatingSubscription?.dispose()
50 | locatingSubscription = nil
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/RxLocationManagerDemo/StandardLocationServiceViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StandardLocationServiceViewController.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/10.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import RxLocationManager
11 | import RxSwift
12 | import RxCocoa
13 |
14 | class StandardLocationServiceViewController: UIViewController {
15 |
16 | @IBOutlet weak var currentLocationLbl: UILabel!
17 |
18 | @IBOutlet weak var errorLbl: UILabel!
19 |
20 | @IBOutlet weak var getCurrentLocationBtn: UIButton!
21 |
22 | @IBOutlet weak var toggleLocatingBtn: UIButton!
23 |
24 | @IBOutlet weak var modeSwitcher: UISegmentedControl!
25 |
26 | private var disposeBag:DisposeBag!
27 |
28 | private var locatedSubscription: Disposable?
29 | private var locatingSubscription: Disposable?
30 |
31 | override func viewWillAppear(_ animated: Bool) {
32 | disposeBag = DisposeBag()
33 | modeSwitcher.rx.value
34 | .map{
35 | return $0 != 0
36 | }
37 | .subscribe(getCurrentLocationBtn.rx.isHidden)
38 | .addDisposableTo(disposeBag)
39 |
40 | modeSwitcher.rx.value
41 | .map{
42 | return $0 == 0
43 | }
44 | .subscribe(toggleLocatingBtn.rx.isHidden)
45 | .addDisposableTo(disposeBag)
46 |
47 | getCurrentLocationBtn.rx.tap
48 | .subscribe{
49 | [unowned self]
50 | _ in
51 | if self.locatedSubscription != nil {
52 | self.currentLocationLbl.text = ""
53 | self.locatedSubscription!.dispose()
54 | }
55 | self.locatedSubscription = RxLocationManager.Standard.located
56 | .map{
57 | return "\($0.coordinate.latitude),\($0.coordinate.longitude)"
58 | }
59 | .do(
60 | onNext:{
61 | _ in
62 | self.errorLbl.text = ""
63 | },
64 | onError:{
65 | self.currentLocationLbl.text = ""
66 | self.errorLbl.text = ($0 as NSError).description
67 | }
68 | )
69 | .catchErrorJustReturn("")
70 | .bindTo(self.currentLocationLbl.rx.text)
71 | }
72 | .addDisposableTo(disposeBag)
73 |
74 | toggleLocatingBtn.rx.tap
75 | .subscribe{
76 | [unowned self]
77 | _ in
78 | if self.locatingSubscription == nil {
79 | self.toggleLocatingBtn.setTitle("Stop", for: .normal)
80 | self.locatingSubscription = RxLocationManager.Standard.locating
81 | .map{
82 | let coord = $0.last!;
83 | return "\(coord.coordinate.latitude),\(coord.coordinate.longitude)"
84 | }
85 | .do(
86 | onNext:{
87 | _ in
88 | self.errorLbl.text = ""
89 | },
90 | onError:{
91 | self.currentLocationLbl.text = ""
92 | self.errorLbl.text = ($0 as NSError).description
93 | }
94 | )
95 | .catchErrorJustReturn("")
96 | .bindTo(self.currentLocationLbl.rx.text)
97 | }else{
98 | self.toggleLocatingBtn.setTitle("Start", for: .normal)
99 | self.currentLocationLbl.text = ""
100 | self.locatingSubscription!.dispose()
101 | self.locatingSubscription = nil
102 | }
103 | }
104 | .addDisposableTo(disposeBag)
105 | }
106 |
107 | override func viewDidDisappear(_ animated: Bool) {
108 | disposeBag = nil
109 | locatedSubscription?.dispose()
110 | locatedSubscription = nil
111 | locatingSubscription?.dispose()
112 | locatingSubscription = nil
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/RxLocationManagerDemo/VisitMonitoringViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VisitMonitoringViewController.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/15.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreLocation
11 | import RxLocationManager
12 | import RxSwift
13 |
14 | class VisitMonitoringViewController: UIViewController {
15 |
16 | @IBOutlet weak var coordValueLbl: UILabel!
17 | @IBOutlet weak var horizontalAccuracyLbl: UILabel!
18 | @IBOutlet weak var arriveDateLbl: UILabel!
19 | @IBOutlet weak var departureDateLbl: UILabel!
20 | @IBOutlet weak var toggleBtn: UIButton!
21 |
22 | private var disposeBag:DisposeBag!
23 | private var subscription: Disposable?
24 | override func viewWillAppear(_ animated: Bool) {
25 | disposeBag = DisposeBag()
26 | toggleBtn.rx.tap
27 | .subscribe(
28 | onNext:{
29 | [unowned self]
30 | _ in
31 | if self.subscription != nil{
32 | self.subscription!.dispose()
33 | self.toggleBtn.setTitle("Start", for: .normal)
34 | self.subscription = nil
35 | }else{
36 | self.subscription = RxLocationManager.VisitMonitoring.visiting
37 | .subscribe(onNext:{
38 | visit in
39 | self.coordValueLbl.text = "\(visit.coordinate.latitude),\(visit.coordinate.longitude)"
40 | self.horizontalAccuracyLbl.text = visit.horizontalAccuracy.description
41 | self.arriveDateLbl.text = visit.arrivalDate.description
42 | self.departureDateLbl.text = visit.departureDate.description
43 | })
44 | self.toggleBtn.setTitle("Stop", for: .normal)
45 | }
46 | })
47 | .addDisposableTo(disposeBag)
48 | }
49 |
50 | override func viewDidDisappear(_ animated: Bool) {
51 | disposeBag = nil
52 | subscription?.dispose()
53 | subscription = nil
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/RxLocationManagerTests/Fixtures.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Fixtures.swift
3 | // RxLocationManager
4 | //
5 | // Created by Hao Yu on 16/7/24.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 |
12 | let dummyLocationManager = CLLocationManager()
13 |
14 | struct Locations{
15 | static let London = CLLocation(latitude: 51.50, longitude: -0.13)
16 | static let Johnannesburg = CLLocation(latitude: -26.20, longitude: 28.05)
17 | static let Moscow = CLLocation(latitude: 55.75, longitude: 37.62)
18 | static let Mumbai = CLLocation(latitude: 19.02, longitude: 72.86)
19 | static let Tokyo = CLLocation(latitude: 35.70, longitude: 139.78)
20 | static let Sydney = CLLocation(latitude: -33.86, longitude: 151.21)
21 | }
22 |
23 | struct GeoRegions{
24 | static let London = CLCircularRegion(center: Locations.London.coordinate, radius: 100, identifier: "London")
25 | static let Johnannesburg = CLCircularRegion(center: Locations.Johnannesburg.coordinate, radius: 100, identifier: "Johnannesburg")
26 | static let Moscow = CLCircularRegion(center: Locations.Moscow.coordinate, radius: 100, identifier: "Moscow")
27 | static let Mumbai = CLCircularRegion(center: Locations.Mumbai.coordinate, radius: 100, identifier: "Mumbai")
28 | static let Tokyo = CLCircularRegion(center: Locations.Tokyo.coordinate, radius: 100, identifier: "Tokyo")
29 | static let Sydney = CLCircularRegion(center: Locations.Sydney.coordinate, radius: 100, identifier: "Sydney")
30 | }
31 |
32 | #if os(iOS)
33 | struct BeaconRegions{
34 | static let one = CLBeaconRegion(proximityUUID: UUID(uuidString: "436F7E14-D361-4D9E-8A0B-9C5B780788C0")!, identifier: "one")
35 | static let two = CLBeaconRegion(proximityUUID: UUID(uuidString: "A36C2C84-CFC8-4E2F-BEE8-9036A7CBD26D")!, identifier: "two")
36 | static let three = CLBeaconRegion(proximityUUID: UUID(uuidString: "6CE0D127-42AC-45B9-839C-0B6AD53EBE11")!, identifier: "three")
37 | }
38 | #endif
39 |
40 | #if os(iOS)
41 | class CLHeadingForTest: CLHeading{
42 | override init(){super.init()}
43 | required init?(coder aDecoder: NSCoder) {
44 | super.init(coder: aDecoder)
45 | }
46 | }
47 |
48 | struct Headings{
49 | static let north = CLHeadingForTest()
50 | static let south = CLHeadingForTest()
51 | static let east = CLHeadingForTest()
52 | static let west = CLHeadingForTest()
53 | }
54 | #endif
55 |
56 | #if os(iOS)
57 | struct Visits{
58 | static let one = CLVisitForTest()
59 | static let two = CLVisitForTest()
60 | }
61 | class CLVisitForTest: CLVisit{
62 | override init(){super.init()}
63 |
64 | required init?(coder aDecoder: NSCoder) {
65 | super.init(coder: aDecoder)
66 | }
67 | }
68 | #endif
69 |
70 |
71 |
72 | extension CLError.Code{
73 | func toNSError() -> NSError{
74 | return NSError(domain: kCLErrorDomain, code: rawValue, userInfo: nil)
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/RxLocationManagerTests/HeadingUpdateServiceTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HeadingUpdateServiceTest.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/25.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import RxSwift
11 | import CoreLocation
12 | import Nimble
13 | @testable
14 | import RxLocationManager
15 | #if os(iOS)
16 | class HeadingUpdateServiceTest: XCTestCase {
17 | var headingUpdateService: DefaultHeadingUpdateService!
18 | var bridge: LocationManagerStub!
19 | var disposeBag: DisposeBag!
20 | override func setUp() {
21 | headingUpdateService = DefaultHeadingUpdateService(bridgeClass: LocationManagerStub.self)
22 | bridge = headingUpdateService.locMgr as! LocationManagerStub
23 | disposeBag = DisposeBag()
24 | }
25 |
26 | override func tearDown() {
27 | disposeBag = nil
28 | }
29 |
30 | func testGetSetHeadingFilter() {
31 | _ = headingUpdateService.headingFilter(20.0)
32 | expect(self.headingUpdateService.headingFilter).to(equal(bridge.headingFilter))
33 | expect(self.headingUpdateService.headingFilter).to(equal(20.0))
34 | }
35 | func testGetSetHeadingOrientation() {
36 | _ = headingUpdateService.headingOrientation(CLDeviceOrientation.faceDown)
37 | expect(self.headingUpdateService.headingOrientation).to(equal(bridge.headingOrientation))
38 | expect(self.headingUpdateService.headingOrientation).to(equal(CLDeviceOrientation.faceDown))
39 | }
40 | func testGetSetDisplayHeadingCalibration() {
41 | _ = headingUpdateService.displayHeadingCalibration(false)
42 | expect(self.headingUpdateService.displayHeadingCalibration).to(equal(bridge.displayHeadingCalibration))
43 | expect(self.headingUpdateService.displayHeadingCalibration).to(beFalse())
44 | }
45 | func testGetSetTrueHeading() {
46 | _ = headingUpdateService.startTrueHeading((100, kCLLocationAccuracyKilometer))
47 | expect(self.bridge.currentDistanceFilter).to(equal(100))
48 | expect(self.bridge.currentDesiredAccuracy).to(equal(kCLLocationAccuracyKilometer))
49 | expect(self.bridge.updatingLocation).to(beTrue())
50 | headingUpdateService.stopTrueHeading()
51 | expect(self.bridge.updatingLocation).to(beFalse())
52 | }
53 | func testHeadingObservable() {
54 | let xcTestExpectation = self.expectation(description: "Get one heading update")
55 | headingUpdateService.heading
56 | .subscribe(onNext: {
57 | heading in
58 | expect(heading == Headings.north).to(beTrue())
59 | xcTestExpectation.fulfill()
60 | })
61 | .addDisposableTo(disposeBag)
62 | self.bridge.didUpdateHeading!(dummyLocationManager, Headings.north)
63 | self.waitForExpectations(timeout: 50, handler: nil)
64 | }
65 | }
66 | #endif
67 |
--------------------------------------------------------------------------------
/RxLocationManagerTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
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 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/RxLocationManagerTests/LocationManagerStub.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LocationManagerStub.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/24.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 | @testable
12 | import RxLocationManager
13 |
14 | class LocationManagerStub: CLLocationManagerBridge{
15 | var whenInUseAuthorizationRequested = false
16 | var alwaysAuthorizationRequested = false
17 |
18 | var currentLocation:CLLocation?
19 | var currentDistanceFilter: CLLocationDistance = 0.0
20 | var currentDesiredAccuracy: CLLocationAccuracy = 0.0
21 | var currentPausesLocationUpdatesAutomatically = false
22 | var currentAllowsBackgroundLocationUpdates = false
23 | var currentActivityType = CLActivityType.other
24 |
25 | var currentlyDeferedSetting:(CLLocationDistance, TimeInterval)?
26 |
27 | var currentHeadingFilter = 0.0
28 | var currentHeadingOrientation = CLDeviceOrientation.portrait
29 |
30 | var currentMonitoredRegions = Set()
31 | var currentRegionStateRequests = Set()
32 |
33 | #if os(iOS)
34 | var currangRangedBeaconRegions = Set()
35 | #endif
36 |
37 | var updatingLocation = false
38 | var locationRequested = false
39 | var updatingHeading = false
40 | var monitoringSignificantLocationChange = false
41 |
42 | //instance methods on CLLocationManager instance
43 | #if os(iOS) || os(watchOS) || os(tvOS)
44 | override func requestWhenInUseAuthorization(){
45 | whenInUseAuthorizationRequested = true
46 | }
47 | #endif
48 | #if os(iOS) || os(watchOS)
49 | override func requestAlwaysAuthorization(){
50 | alwaysAuthorizationRequested = true
51 | }
52 | #endif
53 |
54 | #if os(iOS) || os(watchOS) || os(tvOS)
55 | override var location: CLLocation? {
56 | get{
57 | return currentLocation
58 | }
59 | }
60 | #endif
61 |
62 | #if os(iOS) || os(OSX)
63 | override func startUpdatingLocation(){
64 | updatingLocation = true
65 | }
66 | #endif
67 |
68 | override func stopUpdatingLocation(){
69 | updatingLocation = false
70 | }
71 |
72 | #if os(iOS) || os(watchOS) || os(tvOS)
73 | @available(iOS 9.0, *)
74 | override func requestLocation(){
75 | locationRequested = true
76 | }
77 | #endif
78 |
79 | override var distanceFilter: CLLocationDistance {
80 | get{
81 | return currentDistanceFilter
82 | }
83 | set{
84 | currentDistanceFilter = newValue
85 | }
86 | }
87 | override var desiredAccuracy: CLLocationAccuracy {
88 | get{
89 | return currentDesiredAccuracy
90 | }
91 | set{
92 | currentDesiredAccuracy = newValue
93 | }
94 | }
95 | #if os(iOS)
96 | override var pausesLocationUpdatesAutomatically: Bool {
97 | get{
98 | return currentPausesLocationUpdatesAutomatically
99 | }
100 | set{
101 | currentPausesLocationUpdatesAutomatically = newValue
102 | }
103 | }
104 | @available(iOS 9.0, *)
105 | override var allowsBackgroundLocationUpdates: Bool {
106 | get{
107 | return currentAllowsBackgroundLocationUpdates
108 | }
109 | set{
110 | return currentAllowsBackgroundLocationUpdates = newValue
111 | }
112 | }
113 | override func allowDeferredLocationUpdates(untilTraveled distance: CLLocationDistance, timeout: TimeInterval){
114 | currentlyDeferedSetting = (distance, timeout)
115 | }
116 | override func disallowDeferredLocationUpdates(){
117 | currentlyDeferedSetting = nil
118 | }
119 | override var activityType: CLActivityType {
120 | get{
121 | return currentActivityType
122 | }
123 | set{
124 | currentActivityType = newValue
125 | }
126 | }
127 | #endif
128 |
129 | #if os(iOS) || os(OSX)
130 | override func startMonitoringSignificantLocationChanges(){
131 | monitoringSignificantLocationChange = true
132 | }
133 | override func stopMonitoringSignificantLocationChanges(){
134 | monitoringSignificantLocationChange = false
135 | }
136 | #endif
137 |
138 | #if os(iOS)
139 | override func startUpdatingHeading(){
140 | updatingHeading = true
141 | }
142 | override func stopUpdatingHeading(){
143 | updatingHeading = false
144 | }
145 | override func dismissHeadingCalibrationDisplay(){
146 |
147 | }
148 | override var headingFilter: CLLocationDegrees {
149 | get{
150 | return currentHeadingFilter
151 | }
152 | set{
153 | currentHeadingFilter = newValue
154 | }
155 | }
156 | override var headingOrientation: CLDeviceOrientation {
157 | get{
158 | return currentHeadingOrientation
159 | }
160 | set{
161 | currentHeadingOrientation = newValue
162 | }
163 | }
164 | #endif
165 |
166 | #if os(iOS) || os(OSX)
167 | override func startMonitoring(for region: CLRegion){
168 | currentMonitoredRegions.insert(region)
169 | }
170 | override func stopMonitoring(for region: CLRegion){
171 | currentMonitoredRegions.remove(region)
172 | }
173 | override var monitoredRegions: Set {
174 | get{
175 | return currentMonitoredRegions
176 | }
177 | }
178 | override var maximumRegionMonitoringDistance: CLLocationDistance {
179 | get{
180 | return 200
181 | }
182 | }
183 | override func requestState(for region: CLRegion){
184 | currentRegionStateRequests.insert(region)
185 | }
186 | #endif
187 |
188 | #if os(iOS)
189 | override var rangedRegions: Set {
190 | get{
191 | return currangRangedBeaconRegions
192 | }
193 | }
194 | override func startRangingBeacons(in region: CLBeaconRegion){
195 | currangRangedBeaconRegions.insert(region)
196 | }
197 | override func stopRangingBeacons(in region: CLBeaconRegion){
198 | currangRangedBeaconRegions.remove(region)
199 | }
200 | #endif
201 |
202 | #if os(iOS)
203 | override func startMonitoringVisits(){
204 |
205 | }
206 | override func stopMonitoringVisits(){
207 |
208 | }
209 | #endif
210 |
211 | required init(){
212 | super.init()
213 | }
214 |
215 | }
216 |
217 |
--------------------------------------------------------------------------------
/RxLocationManagerTests/MonitoringVisitsServiceTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MonitoringVisitsServiceTest.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/27.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import RxSwift
11 | import CoreLocation
12 | import Nimble
13 | @testable
14 | import RxLocationManager
15 | #if os(iOS)
16 | class MonitoringVisitsServiceTest: XCTestCase {
17 | var monitoringVisitsService: DefaultMonitoringVisitsService!
18 | var bridge: LocationManagerStub!
19 | var disposeBag: DisposeBag!
20 | override func setUp() {
21 | monitoringVisitsService = DefaultMonitoringVisitsService(bridgeClass: LocationManagerStub.self)
22 | bridge = monitoringVisitsService.locMgr as! LocationManagerStub
23 | disposeBag = DisposeBag()
24 | }
25 |
26 | override func tearDown() {
27 | disposeBag = nil
28 | }
29 |
30 | func testVisitsObservable() {
31 | let xcTestExpectation = self.expectation(description: "Get one visited place")
32 | monitoringVisitsService.visiting
33 | .subscribe(onNext: {
34 | visit in
35 | expect(visit == Visits.one).to(beTrue())
36 | xcTestExpectation.fulfill()
37 | })
38 | .addDisposableTo(disposeBag)
39 | self.bridge.didVisit!(dummyLocationManager, Visits.one)
40 | self.waitForExpectations(timeout: 50, handler: nil)
41 |
42 | }
43 | }
44 | #endif
45 |
--------------------------------------------------------------------------------
/RxLocationManagerTests/RegionMonitoringServiceTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RegionMonitoringServiceTest.swift
3 | // RxLocationManager
4 | //
5 | // Created by HaoYu on 16/7/26.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import RxSwift
11 | import CoreLocation
12 | import Nimble
13 | @testable
14 | import RxLocationManager
15 | #if os(iOS) || os(OSX)
16 | class RegionMonitoringServiceTest: XCTestCase {
17 | var regionMonitoringService: DefaultRegionMonitoringService!
18 | var bridge: LocationManagerStub!
19 | var disposeBag: DisposeBag!
20 | override func setUp() {
21 | regionMonitoringService = DefaultRegionMonitoringService(bridgeClass: LocationManagerStub.self)
22 | bridge = regionMonitoringService.locMgr as! LocationManagerStub
23 | disposeBag = DisposeBag()
24 | }
25 |
26 | override func tearDown() {
27 | disposeBag = nil
28 | }
29 |
30 | func testStartMonitoringForRegions() {
31 | _ = regionMonitoringService.startMonitoringForRegions([GeoRegions.London, GeoRegions.Johnannesburg])
32 | expect(self.bridge.currentMonitoredRegions).to(equal([GeoRegions.London, GeoRegions.Johnannesburg]))
33 | }
34 |
35 | func testStopMonitoringForRegions() {
36 | _ = regionMonitoringService.startMonitoringForRegions([GeoRegions.London, GeoRegions.Johnannesburg])
37 | _ = regionMonitoringService.stopMonitoringForRegions([GeoRegions.London])
38 | expect(self.bridge.currentMonitoredRegions).to(equal([GeoRegions.Johnannesburg]))
39 | }
40 |
41 | func testStopAllMonitoringForRegions() {
42 | _ = regionMonitoringService.startMonitoringForRegions([GeoRegions.London, GeoRegions.Johnannesburg])
43 | _ = regionMonitoringService.stopMonitoringForAllRegions()
44 | expect(self.bridge.currentMonitoredRegions.count).to(equal(0))
45 | }
46 |
47 | func testRequestStateForRegion() {
48 | _ = regionMonitoringService.requestRegionsState([GeoRegions.London, GeoRegions.Johnannesburg])
49 | expect(self.bridge.currentRegionStateRequests).to(equal([GeoRegions.London, GeoRegions.Johnannesburg]))
50 | }
51 |
52 | #if os(iOS)
53 | func testStartRangingBeaconsInRegion(){
54 | _ = regionMonitoringService.startRangingBeaconsInRegion(BeaconRegions.one)
55 | _ = regionMonitoringService.startRangingBeaconsInRegion(BeaconRegions.two)
56 | expect(self.bridge.currangRangedBeaconRegions).to(equal([BeaconRegions.one, BeaconRegions.two]))
57 | expect(self.bridge.currangRangedBeaconRegions).to(equal(regionMonitoringService.rangedRegions))
58 | }
59 |
60 | func testStopRangingBeaconsInRegion(){
61 | _ = regionMonitoringService.startRangingBeaconsInRegion(BeaconRegions.one)
62 | _ = regionMonitoringService.startRangingBeaconsInRegion(BeaconRegions.two)
63 | _ = regionMonitoringService.startRangingBeaconsInRegion(BeaconRegions.three)
64 | _ = regionMonitoringService.stopRangingBeaconsInRegion(BeaconRegions.three)
65 | expect(self.bridge.currangRangedBeaconRegions).to(equal([BeaconRegions.one, BeaconRegions.two]))
66 | }
67 | #endif
68 |
69 | func testGetMaximumRegionMonitoringDistance(){
70 | expect(self.regionMonitoringService.maximumRegionMonitoringDistance).to(equal(200))
71 | }
72 |
73 | func testMonitoredRegionsObservable(){
74 | self.bridge.currentMonitoredRegions.insert(GeoRegions.London)
75 | let xcTestExpectation = self.expectation(description: "Get one monitored region")
76 | var n = 1
77 | regionMonitoringService.monitoredRegions
78 | .subscribe(onNext: {
79 | regions in
80 | if n == 1{
81 | expect(regions).to(equal([GeoRegions.London]))
82 | n += 1
83 | }else{
84 | expect(regions).to(equal([GeoRegions.London, GeoRegions.Johnannesburg]))
85 | xcTestExpectation.fulfill()
86 | }
87 | })
88 | .addDisposableTo(disposeBag)
89 |
90 | self.bridge.currentMonitoredRegions.insert(GeoRegions.Johnannesburg)
91 | self.bridge.didStartMonitoringForRegion!(dummyLocationManager, GeoRegions.Johnannesburg)
92 | self.waitForExpectations(timeout: 50, handler: nil)
93 | }
94 |
95 |
96 | func testRegionEnteringEventObservable(){
97 | let xcTestExpectation = self.expectation(description: "Get one monitored region enter event")
98 | regionMonitoringService.entering
99 | .subscribe(onNext: {
100 | region in
101 | expect(region).to(equal(GeoRegions.London))
102 | xcTestExpectation.fulfill()
103 | })
104 | .addDisposableTo(disposeBag)
105 | self.bridge.didEnterRegion!(dummyLocationManager, GeoRegions.London)
106 | self.waitForExpectations(timeout: 50, handler: nil)
107 | }
108 |
109 | func testRegionExitingEventObservable(){
110 | let xcTestExpectation = self.expectation(description: "Get one monitored region exit event")
111 | regionMonitoringService.exiting
112 | .subscribe(onNext: {
113 | region in
114 | expect(region).to(equal(GeoRegions.London))
115 | xcTestExpectation.fulfill()
116 | })
117 | .addDisposableTo(disposeBag)
118 | self.bridge.didExitRegion!(dummyLocationManager, GeoRegions.London)
119 | self.waitForExpectations(timeout: 50, handler: nil)
120 | }
121 |
122 | func testDeterminedRegionStateObservable(){
123 | let xcTestExpectation = self.expectation(description: "Determined state for one monitored region")
124 | regionMonitoringService.determinedRegionState
125 | .subscribe(onNext: {
126 | region, state in
127 | expect(region).to(equal(GeoRegions.London))
128 | expect(state).to(equal(CLRegionState.inside))
129 | xcTestExpectation.fulfill()
130 | })
131 | .addDisposableTo(disposeBag)
132 | self.bridge.didDetermineState!(dummyLocationManager, CLRegionState.inside,GeoRegions.London)
133 | self.waitForExpectations(timeout: 50, handler: nil)
134 | }
135 |
136 | func testErrorObservableWithMonitoringError(){
137 | let xcTestExpectation = self.expectation(description: "Get error during monitoring region")
138 | regionMonitoringService.error
139 | .subscribe(onNext: {
140 | region, error in
141 | expect(region!).to(equal(GeoRegions.London))
142 | expect(error).to(equal(CLError.regionMonitoringFailure.toNSError()))
143 | xcTestExpectation.fulfill()
144 | })
145 | .addDisposableTo(disposeBag)
146 | self.bridge.monitoringDidFailForRegion!(dummyLocationManager, GeoRegions.London, CLError.regionMonitoringFailure.toNSError())
147 | self.waitForExpectations(timeout: 50, handler: nil)
148 | }
149 |
150 | #if os(iOS)
151 | func testErrorObservableWithRangingError(){
152 | let xcTestExpectation = self.expectation(description: "Get error during ranging beacons")
153 | regionMonitoringService.error.subscribe(onNext: {
154 | region, error in
155 | expect(region!).to(equal(BeaconRegions.one))
156 | expect(error).to(equal(CLError.Code.rangingFailure.toNSError()))
157 | xcTestExpectation.fulfill()
158 | })
159 | .addDisposableTo(disposeBag)
160 | self.bridge.rangingBeaconsDidFailForRegion!(dummyLocationManager, BeaconRegions.one, CLError.Code.rangingFailure.toNSError())
161 | self.waitForExpectations(timeout: 50, handler: nil)
162 | }
163 |
164 | func testRangingObservable(){
165 | let xcTestExpectation = self.expectation(description: "Get ranged beacons")
166 | regionMonitoringService.ranging
167 | .subscribe(onNext: {
168 | beacons, beaconRegion in
169 | expect(beacons.count).to(equal(0))
170 | expect(beaconRegion).to(equal(BeaconRegions.one))
171 | xcTestExpectation.fulfill()
172 | })
173 | .addDisposableTo(disposeBag)
174 | self.bridge.didRangeBeaconsInRegion!(dummyLocationManager, [], BeaconRegions.one)
175 | self.waitForExpectations(timeout: 50, handler: nil)
176 | }
177 | #endif
178 | }
179 | #endif
180 |
--------------------------------------------------------------------------------
/RxLocationManagerTests/SignificantLocationUpdateServiceTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SignificantLocationServiceTest.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/25.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 | import XCTest
9 | import Nimble
10 | import CoreLocation
11 | import RxSwift
12 | @testable
13 | import RxLocationManager
14 | #if os(iOS) || os(OSX)
15 | class SignificantLocationUpdateServiceTest: XCTestCase {
16 | var significantLocationUpdateService:DefaultSignificantLocationUpdateService!
17 | var bridge:LocationManagerStub!
18 | var disposeBag: DisposeBag!
19 | override func setUp() {
20 | significantLocationUpdateService = DefaultSignificantLocationUpdateService(bridgeClass:LocationManagerStub.self)
21 | bridge = significantLocationUpdateService.locMgr as! LocationManagerStub
22 | disposeBag = DisposeBag()
23 | }
24 |
25 | override func tearDown() {
26 | disposeBag = nil
27 | }
28 |
29 | func testLocatingObservableWithoutError() {
30 | let xcTestExpectation = self.expectation(description: "GotSeriesOfLocations")
31 | var n = 1
32 | significantLocationUpdateService.locating
33 | .subscribe{
34 | event in
35 | switch event{
36 | case .next(let location):
37 | switch n{
38 | case 1:
39 | expect(location.last!).to(equal(Locations.London))
40 | n += 1
41 | case 2:
42 | expect(location.last!).to(equal(Locations.Johnannesburg))
43 | n += 1
44 | case 3:
45 | expect(location.last!).to(equal(Locations.Moscow))
46 | xcTestExpectation.fulfill()
47 | default:
48 | expect(true).to(beFalse(), description: "You should not be here")
49 | }
50 | case .completed:
51 | expect(true).to(beFalse(), description: "Completed should not get called when observing location updating")
52 | case .error:
53 | expect(true).to(beFalse(), description: "Error should not get called when location is reported")
54 | }
55 | }
56 | .addDisposableTo(disposeBag)
57 | expect(self.bridge.monitoringSignificantLocationChange).to(beTrue())
58 | bridge.didUpdateLocations!(dummyLocationManager, [Locations.London])
59 | bridge.didUpdateLocations!(dummyLocationManager, [Locations.Johnannesburg])
60 | bridge.didUpdateLocations!(dummyLocationManager, [Locations.Moscow])
61 | self.waitForExpectations(timeout: 100, handler:nil)
62 | }
63 |
64 | func testLocatingObservableWithError() {
65 | let xcTextExpectation = self.expectation(description: "GotSeriesOfLocationsAndError")
66 | var n = 1
67 | significantLocationUpdateService.locating
68 | .subscribe{
69 | event in
70 | switch event{
71 | case .next(let location):
72 | switch n{
73 | case 1:
74 | expect(location.last!).to(equal(Locations.London))
75 | n += 1
76 | case 2:
77 | expect(location.last!).to(equal(Locations.Johnannesburg))
78 | n += 1
79 | case 3:
80 | expect(true).to(beFalse(), description: "You should not be here")
81 | default:
82 | expect(true).to(beFalse(), description: "You should not be here")
83 | }
84 | case .completed:
85 | expect(true).to(beFalse(), description: "Completed should not get called when observing location updating")
86 | case .error(let error as NSError):
87 | expect(error.domain == CLError.network.toNSError().domain).to(beTrue())
88 | expect(error.code == CLError.network.toNSError().code).to(beTrue())
89 | xcTextExpectation.fulfill()
90 | default:
91 | expect(true).to(beFalse(), description: "You should not be here")
92 | }
93 | }
94 | .addDisposableTo(disposeBag)
95 | bridge.didUpdateLocations!(dummyLocationManager, [Locations.London])
96 | bridge.didUpdateLocations!(dummyLocationManager, [Locations.Johnannesburg])
97 | bridge.didFailWithError!(dummyLocationManager, CLError.network.toNSError())
98 | bridge.didUpdateLocations!(dummyLocationManager, [Locations.Moscow])
99 | self.waitForExpectations(timeout: 100, handler:nil)
100 | }
101 | }
102 | #endif
103 |
--------------------------------------------------------------------------------
/RxLocationManagerTests/StandardLocationServiceTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StandardLocationServiceTest.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/24.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import Nimble
11 | import CoreLocation
12 | import RxSwift
13 | @testable
14 | import RxLocationManager
15 |
16 | class StandardLocationServiceTest: XCTestCase{
17 | var standardLocationService:DefaultStandardLocationService!
18 | var disposeBag: DisposeBag!
19 | var bridgeForLocation:LocationManagerStub!
20 | var bridgeForLocating:LocationManagerStub!
21 | override func setUp() {
22 | standardLocationService = DefaultStandardLocationService(bridgeClass:LocationManagerStub.self)
23 | #if os(iOS) || os(watchOS) || os(tvOS)
24 | bridgeForLocation = standardLocationService.locMgrForLocation as! LocationManagerStub
25 | #endif
26 | #if os(iOS) || os(OSX)
27 | bridgeForLocating = standardLocationService.locMgrForLocating as! LocationManagerStub
28 | #endif
29 | disposeBag = DisposeBag()
30 | }
31 |
32 | override func tearDown() {
33 | disposeBag = nil
34 | }
35 |
36 | func testGetSetDistanceFilter(){
37 | _ = standardLocationService.distanceFilter(10.0)
38 | #if os(iOS) || os(watchOS) || os(tvOS)
39 | expect(self.standardLocationService.locMgrForLocation.distanceFilter).to(equal(10.0))
40 | #endif
41 |
42 | #if os(iOS) || os(OSX)
43 | expect(self.standardLocationService.locMgrForLocating.distanceFilter).to(equal(10.0))
44 | #endif
45 | }
46 |
47 | func testGetSetDesiredAccuracy(){
48 | _ = standardLocationService.desiredAccuracy(100.0)
49 | expect(self.standardLocationService.desiredAccuracy).to(equal(100.0))
50 | }
51 | #if os(iOS)
52 | func testGetSetPausesLocationUpdatesAutomatically(){
53 | _ = standardLocationService.pausesLocationUpdatesAutomatically(true)
54 | expect(self.standardLocationService.locMgrForLocating.pausesLocationUpdatesAutomatically).to(equal(true))
55 |
56 | }
57 |
58 | func testEnableDeferredLocationUpdates(){
59 | _ = standardLocationService.allowDeferredLocationUpdates(untilTraveled: 100, timeout: 60)
60 | expect((self.standardLocationService.locMgrForLocating as! LocationManagerStub).currentlyDeferedSetting! == (100,60)).to(beTrue())
61 | }
62 |
63 | func testDisableDeferredLocationUpdates(){
64 | _ = standardLocationService.allowDeferredLocationUpdates(untilTraveled: 100, timeout: 60)
65 | _ = standardLocationService.disallowDeferredLocationUpdates()
66 | expect((self.standardLocationService.locMgrForLocating as! LocationManagerStub).currentlyDeferedSetting == nil).to(beTrue())
67 | }
68 |
69 | func testGetSetAllowsBgLocationUpdates(){
70 | _ = standardLocationService.allowsBackgroundLocationUpdates(true)
71 | expect(self.standardLocationService.locMgrForLocating.allowsBackgroundLocationUpdates).to(equal(true))
72 |
73 | }
74 |
75 | func testGetSetActivityType(){
76 | _ = standardLocationService.activityType(CLActivityType.automotiveNavigation)
77 | expect(self.standardLocationService.locMgrForLocating.activityType).to(equal(CLActivityType.automotiveNavigation))
78 | }
79 | #endif
80 |
81 | #if os(iOS) || os(watchOS) || os(tvOS)
82 | func testCurrentLocationObservable(){
83 | let xcTextExpectation1 = self.expectation(description: "GotLocationAndComplete")
84 | standardLocationService.located
85 | .subscribe{
86 | event in
87 | switch event{
88 | case .next(let location):
89 | expect(location).to(equal(Locations.London))
90 | case .completed:
91 | xcTextExpectation1.fulfill()
92 | case .error:
93 | expect(true).to(beFalse(), description: "Error should not get called when location is reported")
94 | }
95 | }
96 | .addDisposableTo(disposeBag)
97 | bridgeForLocation.didUpdateLocations!(dummyLocationManager, [Locations.London])
98 | self.waitForExpectations(timeout: 5, handler:nil)
99 |
100 | let xcTextExpectation2 = self.expectation(description: "GotError")
101 | standardLocationService.located
102 | .subscribe{
103 | event in
104 | switch event{
105 | case .next:
106 | expect(true).to(beFalse(), description: "Next should not get called when error is reported")
107 | case .completed:
108 | expect(true).to(beFalse(), description: "Completed should not get called when error is reported")
109 | case .error(let error as NSError):
110 | expect(error.domain == CLError.locationUnknown.toNSError().domain).to(beTrue())
111 | expect(error.code == CLError.locationUnknown.toNSError().code).to(beTrue())
112 | xcTextExpectation2.fulfill()
113 | default:
114 | expect(true).to(beFalse(), description: "You should not be here")
115 | }
116 | }
117 | .addDisposableTo(disposeBag)
118 | bridgeForLocation.didFailWithError!(dummyLocationManager, CLError.locationUnknown.toNSError())
119 | self.waitForExpectations(timeout: 5, handler:nil)
120 | }
121 | #endif
122 | #if os(iOS) || os(OSX)
123 | func testLocatingObservable(){
124 | let xcTextExpectation = self.expectation(description: "GotSeriesOfLocations")
125 | var n = 1
126 | standardLocationService.locating
127 | .subscribe{
128 | event in
129 | switch event{
130 | case .next(let location):
131 | switch n{
132 | case 1:
133 | expect(location.last!).to(equal(Locations.London))
134 | n += 1
135 | case 2:
136 | expect(location.last!).to(equal(Locations.Johnannesburg))
137 | n += 1
138 | case 3:
139 | expect(location.last!).to(equal(Locations.Moscow))
140 | xcTextExpectation.fulfill()
141 | default:
142 | expect(true).to(beFalse(), description: "You should not be here")
143 | }
144 | case .completed:
145 | expect(true).to(beFalse(), description: "Completed should not get called when observing location updating")
146 | case .error:
147 | expect(true).to(beFalse(), description: "Error should not get called when location is reported")
148 | }
149 | }
150 | .addDisposableTo(disposeBag)
151 | expect(self.bridgeForLocating.updatingLocation).to(beTrue())
152 | bridgeForLocating.didUpdateLocations!(dummyLocationManager, [Locations.London])
153 | bridgeForLocating.didUpdateLocations!(dummyLocationManager, [Locations.Johnannesburg])
154 | bridgeForLocating.didUpdateLocations!(dummyLocationManager, [Locations.Moscow])
155 | self.waitForExpectations(timeout: 100, handler:nil)
156 | }
157 |
158 | func testLocatingObservableWithIgnorableError(){
159 | let xcTextExpectation = self.expectation(description: "GotSeriesOfLocationsAndIgnoreLocationUnknownError")
160 | var n = 1
161 | standardLocationService.locating
162 | .subscribe{
163 | event in
164 | switch event{
165 | case .next(let location):
166 | switch n{
167 | case 1:
168 | expect(location.last!).to(equal(Locations.London))
169 | n += 1
170 | case 2:
171 | expect(location.last!).to(equal(Locations.Johnannesburg))
172 | n += 1
173 | case 3:
174 | expect(location.last!).to(equal(Locations.Moscow))
175 | xcTextExpectation.fulfill()
176 | default:
177 | expect(true).to(beFalse(), description: "You should not be here")
178 | }
179 | case .completed:
180 | expect(true).to(beFalse(), description: "Completed should not get called when observing location updating")
181 | case .error:
182 | expect(true).to(beFalse(), description: "Error should not get called when location is reported")
183 | }
184 | }
185 | .addDisposableTo(disposeBag)
186 | bridgeForLocating.didUpdateLocations!(dummyLocationManager, [Locations.London])
187 | bridgeForLocating.didFailWithError!(dummyLocationManager, CLError.locationUnknown.toNSError())
188 | bridgeForLocating.didUpdateLocations!(dummyLocationManager, [Locations.Johnannesburg])
189 | bridgeForLocating.didFailWithError!(dummyLocationManager, CLError.locationUnknown.toNSError())
190 | bridgeForLocating.didUpdateLocations!(dummyLocationManager, [Locations.Moscow])
191 | bridgeForLocating.didFailWithError!(dummyLocationManager, CLError.locationUnknown.toNSError())
192 | self.waitForExpectations(timeout: 5, handler:nil)
193 | }
194 |
195 | func testLocatingObservableWithError(){
196 | let xcTextExpectation = self.expectation(description: "GotSeriesOfLocationsAndNonIgnorableError")
197 | var n = 1
198 | standardLocationService.locating
199 | .subscribe{
200 | event in
201 | switch event{
202 | case .next(let location):
203 | switch n{
204 | case 1:
205 | expect(location.last!).to(equal(Locations.London))
206 | n += 1
207 | case 2:
208 | expect(location.last!).to(equal(Locations.Johnannesburg))
209 | n += 1
210 | case 3:
211 | expect(true).to(beFalse(), description: "You should not be here")
212 | default:
213 | expect(true).to(beFalse(), description: "You should not be here")
214 | }
215 | case .completed:
216 | expect(true).to(beFalse(), description: "Completed should not get called when observing location updating")
217 | case .error:
218 | xcTextExpectation.fulfill()
219 | }
220 | }
221 | .addDisposableTo(disposeBag)
222 | bridgeForLocating.didUpdateLocations!(dummyLocationManager, [Locations.London])
223 | bridgeForLocating.didFailWithError!(dummyLocationManager, CLError.locationUnknown.toNSError())
224 | bridgeForLocating.didUpdateLocations!(dummyLocationManager, [Locations.Johnannesburg])
225 | bridgeForLocating.didFailWithError!(dummyLocationManager, CLError.denied.toNSError())
226 | bridgeForLocating.didUpdateLocations!(dummyLocationManager, [Locations.Moscow])
227 | self.waitForExpectations(timeout: 5, handler:nil)
228 | }
229 | #endif
230 |
231 | #if os(iOS)
232 | func testPausedObservable(){
233 | let xcTextExpectation = self.expectation(description: "ObservableOfIsPaused")
234 | var n = 1
235 | standardLocationService.isPaused
236 | .subscribe{
237 | event in
238 | switch event{
239 | case .next(let isPaused):
240 | switch n{
241 | case 1:
242 | expect(isPaused).to(beTrue())
243 | n += 1
244 | case 2:
245 | expect(isPaused).to(beFalse())
246 | xcTextExpectation.fulfill()
247 | n += 1
248 | default:
249 | expect(true).to(beFalse(), description: "You should not be here")
250 | }
251 | case .completed:
252 | expect(true).to(beFalse(), description: "Completed should not get called for this observable")
253 | case .error:
254 | expect(true).to(beFalse(), description: "Error should not get called for this observable")
255 | }
256 | }
257 | .addDisposableTo(disposeBag)
258 | bridgeForLocating.didPausedUpdate!(dummyLocationManager)
259 | bridgeForLocating.didResumeUpdate!(dummyLocationManager)
260 | self.waitForExpectations(timeout: 5, handler:nil)
261 | }
262 |
263 | func testDeferredUpdateErrorObservable(){
264 | let xcTextExpectation = self.expectation(description: "ObservableOfIsPaused")
265 | standardLocationService.deferredUpdateFinished
266 | .subscribe{
267 | event in
268 | switch event{
269 | case .next(let error):
270 | expect(error!.code == CLError.deferredAccuracyTooLow.toNSError().code).to(beTrue())
271 | xcTextExpectation.fulfill()
272 | case .completed:
273 | expect(true).to(beFalse(), description: "Completed should not get called for this observable")
274 | case .error:
275 | expect(true).to(beFalse(), description: "Error should not get called for this observable")
276 | }
277 | }
278 | .addDisposableTo(disposeBag)
279 | bridgeForLocating.didFinishDeferredUpdatesWithError!(dummyLocationManager, CLError.deferredAccuracyTooLow.toNSError())
280 | self.waitForExpectations(timeout: 5, handler:nil)
281 | }
282 | #endif
283 | }
284 |
--------------------------------------------------------------------------------
/Sources/Bridge.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Bridge.swift
3 | // RxLocationManager
4 | //
5 | // Created by Hao Yu on 16/7/6.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 |
12 | class CLLocationManagerBridge: CLLocationManager, CLLocationManagerDelegate{
13 | var didFailWithError: ((CLLocationManager, NSError) -> Void)?
14 | var didChangeAuthorizationStatus: ((CLLocationManager, CLAuthorizationStatus)->Void)?
15 | var didUpdateLocations: ((CLLocationManager, [CLLocation]) -> Void)?
16 |
17 | #if os(iOS) || os(OSX)
18 | var didFinishDeferredUpdatesWithError: ((CLLocationManager, NSError?) -> Void)?
19 | var didEnterRegion: ((CLLocationManager, CLRegion) -> Void)?
20 | var didExitRegion: ((CLLocationManager, CLRegion) -> Void)?
21 | var monitoringDidFailForRegion: ((CLLocationManager, CLRegion?, NSError) -> Void)?
22 | var didDetermineState:((CLLocationManager, CLRegionState, CLRegion) -> Void)?
23 | var didStartMonitoringForRegion:((CLLocationManager, CLRegion) -> Void)?
24 | #endif
25 |
26 | #if os(iOS)
27 | var didPausedUpdate:((CLLocationManager) -> Void)?
28 | var didResumeUpdate:((CLLocationManager) -> Void)?
29 | var displayHeadingCalibration:Bool = false
30 | var didUpdateHeading: ((CLLocationManager, CLHeading) -> Void)?
31 | var didRangeBeaconsInRegion:((CLLocationManager, [CLBeacon], CLBeaconRegion) -> Void)?
32 | var rangingBeaconsDidFailForRegion:((CLLocationManager, CLBeaconRegion, NSError) -> Void)?
33 | var didVisit:((CLLocationManager, CLVisit) -> Void)?
34 | #endif
35 |
36 | required override init() {
37 | super.init()
38 | self.delegate = self
39 | }
40 |
41 | func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
42 | didFailWithError?(manager, error as NSError)
43 | }
44 |
45 | func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
46 | didChangeAuthorizationStatus?(manager, status)
47 | }
48 |
49 | func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
50 | didUpdateLocations?(manager, locations)
51 | }
52 | }
53 |
54 | #if os(iOS) || os(OSX)
55 | extension CLLocationManagerBridge{
56 |
57 | func locationManager(manager: CLLocationManager, didDetermineState state: CLRegionState, forRegion region: CLRegion) {
58 | didDetermineState?(manager, state, region)
59 | }
60 |
61 | func locationManager(manager: CLLocationManager, didEnterRegion region: CLRegion) {
62 | didEnterRegion?(manager, region)
63 | }
64 |
65 | func locationManager(manager: CLLocationManager, didExitRegion region: CLRegion) {
66 | didExitRegion?(manager, region)
67 | }
68 |
69 | func locationManager(manager: CLLocationManager, monitoringDidFailForRegion region: CLRegion?, withError error: NSError) {
70 | monitoringDidFailForRegion?(manager, region, error)
71 | }
72 |
73 | func locationManager(manager: CLLocationManager, didStartMonitoringForRegion region: CLRegion) {
74 | didStartMonitoringForRegion?(manager, region)
75 | }
76 | }
77 | #endif
78 |
79 | #if os(iOS)
80 | extension CLLocationManagerBridge{
81 |
82 | func locationManager(manager: CLLocationManager, didFinishDeferredUpdatesWithError error: NSError?) {
83 | didFinishDeferredUpdatesWithError?(manager, error)
84 | }
85 |
86 | func locationManagerDidPauseLocationUpdates(manager: CLLocationManager) {
87 | didPausedUpdate?(manager)
88 | }
89 |
90 | func locationManagerDidResumeLocationUpdates(manager: CLLocationManager) {
91 | didResumeUpdate?(manager)
92 | }
93 |
94 | func locationManagerShouldDisplayHeadingCalibration(manager: CLLocationManager) -> Bool {
95 | return displayHeadingCalibration
96 | }
97 |
98 | func locationManager(manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
99 | didUpdateHeading?(manager, newHeading)
100 | }
101 |
102 | func locationManager(manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
103 | didRangeBeaconsInRegion?(manager, beacons, region)
104 | }
105 |
106 | func locationManager(manager: CLLocationManager, rangingBeaconsDidFailForRegion region: CLBeaconRegion, withError error: NSError){
107 | rangingBeaconsDidFailForRegion?(manager, region, error)
108 | }
109 |
110 | func locationManager(manager: CLLocationManager, didVisit visit: CLVisit) {
111 | didVisit?(manager, visit)
112 | }
113 | }
114 | #endif
115 |
--------------------------------------------------------------------------------
/Sources/HeadingUpdateService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HeadingUpdateService.swift
3 | // RxLocationManager
4 | //
5 | // Created by Hao Yu on 16/7/6.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | import Foundation
12 | import CoreLocation
13 | import RxSwift
14 |
15 | //MARK: HeadingUpdateServiceConfigurable
16 | public protocol HeadingUpdateServiceConfigurable{
17 | /**
18 | Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instp/CLLocationManager/headingFilter)
19 |
20 | - parameter degrees: to filter
21 |
22 | - returns: self for chaining call
23 | */
24 | func headingFilter(_ degrees:CLLocationDegrees) -> HeadingUpdateService
25 | /**
26 | Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instp/CLLocationManager/headingOrientation)
27 |
28 | - parameter degrees
29 |
30 | - returns: self for chaining call
31 | */
32 | func headingOrientation(_ degrees:CLDeviceOrientation) -> HeadingUpdateService
33 | /**
34 | Should display heading calibration during monitoring heading update?
35 |
36 | - parameter should: display heading calibration
37 |
38 | - returns: self for chaining call
39 | */
40 | func displayHeadingCalibration(_ should:Bool) -> HeadingUpdateService
41 | /// Current heading filter value
42 | var headingFilter: CLLocationDegrees{get}
43 | /// Current heading orientation value
44 | var headingOrientation: CLDeviceOrientation{get}
45 | /// Current value of displayHeadingCalibration
46 | var displayHeadingCalibration: Bool{get}
47 | }
48 | //MARK: HeadingUpdateService
49 | public protocol HeadingUpdateService: HeadingUpdateServiceConfigurable{
50 | /// Observable of current heading update
51 | var heading: Observable{get}
52 | /**
53 | Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instm/CLLocationManager/dismissHeadingCalibrationDisplay)
54 | */
55 | func dismissHeadingCalibrationDisplay() -> Void
56 |
57 | /**
58 | Start generating true heading with the specified parameters to start location updating
59 |
60 | - parameter withParams: setting distance filter and desired accuracy for the location update
61 | */
62 | func startTrueHeading(_ withParams: (distanceFilter:CLLocationDistance, desiredAccuracy:CLLocationAccuracy)?)
63 | /**
64 | Stop generating true heading
65 | */
66 | func stopTrueHeading()
67 | /**
68 | Return a cloned instance of current heading update service
69 |
70 | - returns: cloned instance
71 | */
72 | func clone() -> HeadingUpdateService
73 | }
74 |
75 | //MARK: DefaultHeadingUpdateService
76 | class DefaultHeadingUpdateService: HeadingUpdateService {
77 | private let bridgeClass: CLLocationManagerBridge.Type
78 | var locMgr: CLLocationManagerBridge
79 | private var trueHeadingParams: (distanceFilter:CLLocationDistance, desiredAccuracy:CLLocationAccuracy)?
80 |
81 | var headingFilter: CLLocationDegrees{
82 | get{
83 | return locMgr.headingFilter
84 | }
85 | }
86 | var headingOrientation: CLDeviceOrientation{
87 | get{
88 | return locMgr.headingOrientation
89 | }
90 | }
91 | var displayHeadingCalibration: Bool{
92 | get{
93 | return locMgr.displayHeadingCalibration
94 | }
95 | }
96 |
97 | var observers = [(id: Int, observer: AnyObserver)]()
98 | var heading : Observable{
99 | get{
100 | return Observable.create {
101 | observer in
102 | var ownerService: DefaultHeadingUpdateService! = self
103 | let id = nextId()
104 | ownerService.observers.append((id, observer))
105 | if ownerService.trueHeadingParams != nil{
106 | ownerService.locMgr.distanceFilter = ownerService.trueHeadingParams!.distanceFilter
107 | ownerService.locMgr.desiredAccuracy = ownerService.trueHeadingParams!.desiredAccuracy
108 | ownerService.locMgr.startUpdatingLocation()
109 | }
110 | ownerService.locMgr.startUpdatingHeading()
111 | return Disposables.create {
112 | ownerService.observers.remove(at: ownerService.observers.index(where: {$0.id == id})!)
113 | if(ownerService.observers.count == 0){
114 | ownerService.locMgr.stopUpdatingLocation()
115 | ownerService.locMgr.stopUpdatingHeading()
116 | }
117 | ownerService = nil
118 | }
119 | }
120 | }
121 | }
122 |
123 | init(bridgeClass: CLLocationManagerBridge.Type){
124 | self.bridgeClass = bridgeClass
125 | locMgr = bridgeClass.init()
126 | locMgr.didUpdateHeading = {
127 | [weak self]
128 | mgr, heading in
129 | if let copyOfObservers = self?.observers{
130 | for (_, observer) in copyOfObservers{
131 | observer.onNext(heading)
132 | }
133 | }
134 | }
135 | locMgr.didFailWithError = {
136 | [weak self]
137 | mgr, err in
138 | if let copyOfObservers = self?.observers{
139 | for (_, observer) in copyOfObservers{
140 | observer.onError(err)
141 | }
142 | }
143 | }
144 | }
145 |
146 | func headingFilter(_ degrees: CLLocationDegrees) -> HeadingUpdateService {
147 | locMgr.headingFilter = degrees
148 | return self
149 | }
150 |
151 | func headingOrientation(_ degrees: CLDeviceOrientation) -> HeadingUpdateService {
152 | locMgr.headingOrientation = degrees
153 | return self
154 | }
155 |
156 | func displayHeadingCalibration(_ should: Bool) -> HeadingUpdateService {
157 | locMgr.displayHeadingCalibration = should
158 | return self
159 | }
160 |
161 | func startTrueHeading(_ withParams: (distanceFilter: CLLocationDistance, desiredAccuracy: CLLocationAccuracy)?) {
162 | if withParams == nil{
163 | trueHeadingParams = (1000, kCLLocationAccuracyKilometer)
164 | }else{
165 | trueHeadingParams = withParams
166 | }
167 | locMgr.distanceFilter = trueHeadingParams!.distanceFilter
168 | locMgr.desiredAccuracy = trueHeadingParams!.desiredAccuracy
169 | locMgr.startUpdatingLocation()
170 |
171 | }
172 |
173 | func stopTrueHeading() {
174 | locMgr.stopUpdatingLocation()
175 | }
176 |
177 | func dismissHeadingCalibrationDisplay() {
178 | locMgr.dismissHeadingCalibrationDisplay()
179 | }
180 |
181 | func clone() -> HeadingUpdateService {
182 | let clone = DefaultHeadingUpdateService(bridgeClass:bridgeClass)
183 | _ = clone.headingFilter(self.headingFilter)
184 | _ = clone.headingOrientation(self.headingOrientation)
185 | _ = clone.displayHeadingCalibration(self.displayHeadingCalibration)
186 | return clone
187 | }
188 | }
189 |
190 | #endif
191 |
--------------------------------------------------------------------------------
/Sources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Sources/MonitoringVisitsService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MonitoringVisitsService.swift
3 | // RxLocationManager
4 | //
5 | // Created by Hao Yu on 16/7/6.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 | #if os(iOS)
9 | import Foundation
10 | import CoreLocation
11 | import RxSwift
12 |
13 |
14 | //MARK: MonitoringVisitsService
15 | public protocol MonitoringVisitsService{
16 | /// Observable of visit event
17 | var visiting: Observable{get}
18 | }
19 |
20 | //MARK: DefaultMonitoringVisitsService
21 | class DefaultMonitoringVisitsService: MonitoringVisitsService{
22 | let locMgr: CLLocationManagerBridge
23 | private var observers = [(id:Int, observer: AnyObserver)]()
24 |
25 | var visiting: Observable{
26 | get{
27 | return Observable.create{
28 | observer in
29 | var ownerService:DefaultMonitoringVisitsService! = self
30 | let id = nextId()
31 | ownerService.observers.append((id, observer))
32 | ownerService.locMgr.startMonitoringVisits()
33 | return Disposables.create {
34 | ownerService.observers.remove(at: ownerService.observers.index(where: {$0.id == id})!)
35 | if ownerService.observers.count == 0{
36 | ownerService.locMgr.stopMonitoringVisits()
37 | }
38 | ownerService = nil
39 | }
40 | }
41 | }
42 | }
43 |
44 | init(bridgeClass: CLLocationManagerBridge.Type){
45 | locMgr = bridgeClass.init()
46 | locMgr.didVisit = {
47 | [weak self]
48 | mgr, visit in
49 | if let copyOfObservers = self?.observers{
50 | for (_, observer) in copyOfObservers{
51 | observer.onNext(visit)
52 | }
53 | }
54 | }
55 | }
56 | }
57 | #endif
58 |
--------------------------------------------------------------------------------
/Sources/RegionMonitoringService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RegionMonitoringService.swift
3 | // RxLocationManager
4 | //
5 | // Created by Yonny Hao on 16/7/6.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 | #if os(iOS) || os(OSX)
9 | import Foundation
10 | import CoreLocation
11 | import RxSwift
12 |
13 | //MARK: RegionMonitoringServiceConfigurable
14 | public protocol RegionMonitoringServiceConfigurable{
15 | /**
16 | Unlike the official [version](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instm/CLLocationManager/startMonitoringForRegion:), this method allows you to start monitoring multiple regions at once
17 |
18 | - parameter regions: to start monitoring
19 |
20 | - returns: self for chaining call
21 | */
22 | func startMonitoringForRegions(_ regions: [CLRegion]) -> RegionMonitoringService
23 | /**
24 | Unlike the official [version](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instm/CLLocationManager/stopMonitoringForRegion:), this method allows you to stop monitoring multiple regions at once
25 |
26 | - parameter regions: to stop monitoring
27 |
28 | - returns: self for chaining call
29 | */
30 | func stopMonitoringForRegions(_ regions: [CLRegion]) -> RegionMonitoringService
31 | /**
32 | convenient method to stop all monitored regions at once
33 |
34 | - returns: self for chaining call
35 | */
36 | func stopMonitoringForAllRegions() -> RegionMonitoringService
37 | /**
38 | Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instm/CLLocationManager/requestStateForRegion:)
39 |
40 | - parameter regions: to request
41 |
42 | - returns: self for chaining call
43 | */
44 | func requestRegionsState(_ regions:[CLRegion]) -> RegionMonitoringService
45 |
46 | #if os(iOS)
47 | /**
48 | Refer to official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instm/CLLocationManager/startRangingBeaconsInRegion:), this method allows you to start regioning multiple beacons at once
49 |
50 | - parameter region: to start regioning
51 |
52 | - returns: self for chaining call
53 | */
54 | func startRangingBeaconsInRegion(_ region: CLBeaconRegion) -> RegionMonitoringService
55 | /**
56 | Refer to official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instm/CLLocationManager/stopRangingBeaconsInRegion:)
57 |
58 | - parameter region: to stop regioning
59 |
60 | - returns: self for chaining call
61 | */
62 | func stopRangingBeaconsInRegion(_ region: CLBeaconRegion) -> RegionMonitoringService
63 | #endif
64 | }
65 | //MARK: RegionMonitoringService
66 | public protocol RegionMonitoringService: RegionMonitoringServiceConfigurable{
67 | /// Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instp/CLLocationManager/maximumRegionMonitoringDistance)
68 | var maximumRegionMonitoringDistance: CLLocationDistance { get }
69 | /// Observable of current monitored regions
70 | var monitoredRegions: Observable> { get }
71 | /// Observable of region entering event
72 | var entering: Observable{get}
73 | /// Observable of region exiting event
74 | var exiting: Observable{get}
75 | /// Observable of determined state of requested region
76 | var determinedRegionState: Observable<(CLRegion, CLRegionState)> {get}
77 | /// Observable of possible errors during monitoring or ranging, errors won't trigger onError on each Observable, so caller have to manage subscription lifecycle explicitly
78 | var error: Observable<(CLRegion?, NSError)>{get}
79 |
80 |
81 | #if os(iOS)
82 | /// Observable of current ranged beacons
83 | var ranging: Observable<([CLBeacon], CLBeaconRegion)>{get}
84 | /// Set of currently ranged beacon regions
85 | var rangedRegions: Set {get}
86 | #endif
87 | }
88 |
89 | //MARK: DefaultRegionMonitoringService
90 | class DefaultRegionMonitoringService: RegionMonitoringService{
91 | let locMgr: CLLocationManagerBridge
92 |
93 | private var enteringObservers = [(id:Int, observer: AnyObserver)]()
94 | private var exitingObservers = [(id:Int, observer: AnyObserver)]()
95 | private var determinedRegionStateObservers = [(id:Int, observer: AnyObserver<(CLRegion, CLRegionState)>)]()
96 | private var errorObservers = [(id:Int, observer: AnyObserver<(CLRegion?, NSError)>)]()
97 | private var monitoredRegionsObservers = [(id:Int, observer: AnyObserver>)]()
98 |
99 | #if os(iOS)
100 | private var rangingObservers = [(id:Int, observer: AnyObserver<([CLBeacon], CLBeaconRegion)>)]()
101 | #endif
102 |
103 | var maximumRegionMonitoringDistance: CLLocationDistance{
104 | get{
105 | return locMgr.maximumRegionMonitoringDistance
106 | }
107 | }
108 |
109 | var entering: Observable{
110 | get{
111 | return Observable.create{
112 | observer in
113 | var ownerService:DefaultRegionMonitoringService! = self
114 | let id = nextId()
115 | ownerService.enteringObservers.append((id, observer))
116 | return Disposables.create {
117 | ownerService.enteringObservers.remove(at: ownerService.enteringObservers.index(where: {$0.id == id})!)
118 | ownerService = nil
119 | }
120 | }
121 | }
122 | }
123 |
124 | var exiting: Observable{
125 | get{
126 | return Observable.create{
127 | observer in
128 | var ownerService:DefaultRegionMonitoringService! = self
129 | let id = nextId()
130 | ownerService.exitingObservers.append((id, observer))
131 | return Disposables.create {
132 | ownerService.exitingObservers.remove(at: ownerService.exitingObservers.index(where: {$0.id == id})!)
133 | ownerService = nil
134 | }
135 | }
136 | }
137 | }
138 |
139 | var determinedRegionState: Observable<(CLRegion, CLRegionState)>{
140 | get{
141 | return Observable.create{
142 | observer in
143 | var ownerService:DefaultRegionMonitoringService! = self
144 | let id = nextId()
145 | ownerService.determinedRegionStateObservers.append((id, observer))
146 | return Disposables.create {
147 | ownerService.determinedRegionStateObservers.remove(at: ownerService.determinedRegionStateObservers.index(where: {$0.id == id})!)
148 | ownerService = nil
149 | }
150 | }
151 | }
152 | }
153 |
154 | var error: Observable<(CLRegion?, NSError)>{
155 | get{
156 | return Observable.create{
157 | observer in
158 | var ownerService:DefaultRegionMonitoringService! = self
159 | let id = nextId()
160 | ownerService.errorObservers.append((id, observer))
161 | return Disposables.create {
162 | ownerService.errorObservers.remove(at: ownerService.errorObservers.index(where: {$0.id == id})!)
163 | ownerService = nil
164 | }
165 | }
166 | }
167 | }
168 |
169 | var monitoredRegions: Observable>{
170 | get{
171 | return Observable.create{
172 | observer in
173 | var ownerService:DefaultRegionMonitoringService! = self
174 | let id = nextId()
175 | ownerService.monitoredRegionsObservers.append((id, observer))
176 | if !ownerService.locMgr.monitoredRegions.isEmpty{
177 | observer.onNext(ownerService.locMgr.monitoredRegions)
178 | }
179 | return Disposables.create {
180 | ownerService.monitoredRegionsObservers.remove(at: ownerService.monitoredRegionsObservers.index(where:{$0.id == id})!)
181 | ownerService = nil
182 | }
183 | }
184 | }
185 | }
186 |
187 | #if os(iOS)
188 | var rangedRegions: Set {
189 | get{
190 | return locMgr.rangedRegions as! Set
191 | }
192 | }
193 | var ranging: Observable<([CLBeacon], CLBeaconRegion)>{
194 | get{
195 | return Observable.create{
196 | observer in
197 | var ownerService:DefaultRegionMonitoringService! = self
198 | let id = nextId()
199 | ownerService.rangingObservers.append((id, observer))
200 | return Disposables.create {
201 | ownerService.rangingObservers.remove(at: ownerService.rangingObservers.index(where: {$0.id == id})!)
202 | ownerService = nil
203 | }
204 | }
205 | }
206 | }
207 | #endif
208 |
209 | init(bridgeClass: CLLocationManagerBridge.Type){
210 | locMgr = bridgeClass.init()
211 | locMgr.didEnterRegion = {
212 | [weak self]
213 | mgr, region in
214 | if let copyOfEnteringObservers = self?.enteringObservers{
215 | for (_, observer) in copyOfEnteringObservers{
216 | observer.onNext(region)
217 | }
218 | }
219 | }
220 | locMgr.didExitRegion = {
221 | [weak self]
222 | mgr, region in
223 | if let copyOfExitingObservers = self?.exitingObservers{
224 | for (_, observer) in copyOfExitingObservers{
225 | observer.onNext(region)
226 | }
227 | }
228 | }
229 | locMgr.monitoringDidFailForRegion = {
230 | [weak self]
231 | mgr, region, error in
232 | if let copyOfErrorObservers = self?.errorObservers{
233 | for (_, observer) in copyOfErrorObservers{
234 | observer.onNext((region, error))
235 | }
236 | }
237 | }
238 | locMgr.didStartMonitoringForRegion = {
239 | [weak self]
240 | mgr, region in
241 | if let copyOfMonitoredRegionsObservers = self?.monitoredRegionsObservers{
242 | for (_, observer) in copyOfMonitoredRegionsObservers{
243 | observer.onNext(self!.locMgr.monitoredRegions)
244 | }
245 | }
246 | }
247 | locMgr.didDetermineState = {
248 | [weak self]
249 | mgr, state, region in
250 | if let copyOfDeterminedRegionStateObservers = self?.determinedRegionStateObservers{
251 | for(_, observer) in copyOfDeterminedRegionStateObservers{
252 | observer.onNext((region, state))
253 | }
254 | }
255 | }
256 | #if os(iOS)
257 | locMgr.didRangeBeaconsInRegion = {
258 | [weak self]
259 | mgr, beacons, region in
260 | if let copyOfRangingObservers = self?.rangingObservers{
261 | for (_, observer) in copyOfRangingObservers{
262 | observer.onNext((beacons, region))
263 | }
264 | }
265 | }
266 |
267 | locMgr.rangingBeaconsDidFailForRegion = {
268 | [weak self]
269 | mgr, region, error in
270 | if let copyOfErrorObservers = self?.errorObservers{
271 | for (_, observer) in copyOfErrorObservers{
272 | observer.onNext((region, error))
273 | }
274 | }
275 | }
276 | #endif
277 | }
278 |
279 | func requestRegionsState(_ regions: [CLRegion]) -> RegionMonitoringService {
280 | for region in regions{
281 | locMgr.requestState(for: region)
282 | }
283 | return self
284 | }
285 |
286 | func startMonitoringForRegions(_ regions: [CLRegion]) -> RegionMonitoringService {
287 | for region in regions{
288 | locMgr.startMonitoring(for: region)
289 | }
290 | return self
291 | }
292 |
293 | func stopMonitoringForRegions(_ regions: [CLRegion]) -> RegionMonitoringService {
294 | for region in regions{
295 | locMgr.stopMonitoring(for: region)
296 | }
297 |
298 | //Workaround for lacking knowledge about the time when regions actually stop monitored
299 | let currentMonitoredRegions = locMgr.monitoredRegions.subtracting(regions)
300 | for (_, observer) in monitoredRegionsObservers{
301 | observer.onNext(currentMonitoredRegions)
302 | }
303 | return self
304 | }
305 |
306 | func stopMonitoringForAllRegions() -> RegionMonitoringService {
307 | for region in locMgr.monitoredRegions{
308 | locMgr.stopMonitoring(for: region)
309 | }
310 | return self
311 | }
312 |
313 | #if os(iOS)
314 | func startRangingBeaconsInRegion(_ region: CLBeaconRegion) -> RegionMonitoringService {
315 | locMgr.startRangingBeacons(in: region)
316 | return self
317 | }
318 |
319 | func stopRangingBeaconsInRegion(_ region: CLBeaconRegion) -> RegionMonitoringService {
320 | locMgr.stopRangingBeacons(in: region)
321 | return self
322 | }
323 | #endif
324 | }
325 | #endif
326 |
--------------------------------------------------------------------------------
/Sources/RxLocationManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RxLocationManager.swift
3 | // PaperChat
4 | //
5 | // Created by Yonny Hao on 16/6/14.
6 | // Copyright © 2016年 HaoYu. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 | import CoreLocation
12 |
13 | //MARK: RxLocationManager
14 | open class RxLocationManager{
15 | private static var defaultLocationMgr: CLLocationManagerBridge = {
16 | let locMgr = CLLocationManagerBridge()
17 | locMgr.didChangeAuthorizationStatus = {
18 | clLocMgr, status in
19 | authorizationStatusSink.onNext(status)
20 | enabledSink.onNext(CLLocationManagerBridge.locationServicesEnabled())
21 | }
22 | return locMgr
23 | }()
24 |
25 | private static var enabledSink:ReplaySubject = {
26 | let replaySubject:ReplaySubject = ReplaySubject.create(bufferSize: 1)
27 | replaySubject.onNext(CLLocationManagerBridge.locationServicesEnabled())
28 | //Force initialize defaultLocationMgr, since it's always lazy
29 | defaultLocationMgr = defaultLocationMgr
30 | return replaySubject
31 | }()
32 |
33 | /// Observable of location service enabled status change, start with current authorization status
34 | open static var enabled:Observable{
35 | get{
36 | return enabledSink.distinctUntilChanged()
37 | }
38 | }
39 |
40 | private static var authorizationStatusSink:ReplaySubject = {
41 | let replaySubject:ReplaySubject = ReplaySubject.create(bufferSize: 1)
42 | replaySubject.onNext(CLLocationManagerBridge.authorizationStatus())
43 | //Force initialize defaultLocationMgr, since it's always lazy
44 | defaultLocationMgr = defaultLocationMgr
45 | return replaySubject
46 | }()
47 |
48 | /// Observable of the app's authorization status change, start with current authorization status
49 | open static var authorizationStatus: Observable{
50 | get{
51 | return authorizationStatusSink.distinctUntilChanged()
52 | }
53 | }
54 |
55 |
56 | #if os(iOS) || os(watchOS) || os(tvOS)
57 | /**
58 | Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instm/CLLocationManager/requestWhenInUseAuthorization)
59 | */
60 | open static func requestWhenInUseAuthorization(){
61 | defaultLocationMgr.requestWhenInUseAuthorization()
62 | }
63 | #endif
64 |
65 | #if os(iOS) || os(watchOS)
66 | /**
67 | Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instm/CLLocationManager/requestAlwaysAuthorization)
68 | */
69 | open static func requestAlwaysAuthorization(){
70 | defaultLocationMgr.requestAlwaysAuthorization()
71 | }
72 | #endif
73 |
74 | #if os(iOS) || os(OSX)
75 | /// Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/clm/CLLocationManager/significantLocationChangeMonitoringAvailable)
76 | open static var significantLocationChangeMonitoringAvailable:Bool {
77 | get{
78 | return CLLocationManagerBridge.significantLocationChangeMonitoringAvailable()
79 | }
80 | }
81 |
82 | /// Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/clm/CLLocationManager/headingAvailable)
83 | open static var headingAvailable:Bool{
84 | return CLLocationManagerBridge.headingAvailable()
85 | }
86 |
87 | /**
88 | Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/clm/CLLocationManager/isMonitoringAvailableForClass:)
89 |
90 | - parameter regionClass: to test
91 |
92 | - returns: self for chaining call
93 | */
94 | open static func isMonitoringAvailableForClass(regionClass: AnyClass) -> Bool{
95 | return CLLocationManagerBridge.isMonitoringAvailable(for: regionClass)
96 | }
97 | #endif
98 |
99 | #if os(iOS)
100 | /// Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/clm/CLLocationManager/deferredLocationUpdatesAvailable)
101 | open static var deferredLocationUpdatesAvailable:Bool{
102 | get{
103 | return CLLocationManagerBridge.deferredLocationUpdatesAvailable()
104 | }
105 | }
106 |
107 | /// Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/clm/CLLocationManager/isRangingAvailable)
108 | open static var isRangingAvailable:Bool{
109 | get{
110 | return CLLocationManagerBridge.isRangingAvailable()
111 | }
112 | }
113 | #endif
114 |
115 | /// Shared standard location service
116 | open static let Standard: StandardLocationService = DefaultStandardLocationService(bridgeClass: CLLocationManagerBridge.self)
117 |
118 | #if os(iOS) || os(OSX)
119 | /// Shared significant location update service
120 | open static let SignificantLocation: SignificantLocationUpdateService = DefaultSignificantLocationUpdateService(bridgeClass: CLLocationManagerBridge.self)
121 |
122 | /// Shared region monitoring service
123 | open static let RegionMonitoring: RegionMonitoringService = DefaultRegionMonitoringService(bridgeClass: CLLocationManagerBridge.self)
124 | #endif
125 |
126 | #if os(iOS)
127 | /// Shared visit monitoring service
128 | open static let VisitMonitoring: MonitoringVisitsService = DefaultMonitoringVisitsService(bridgeClass: CLLocationManagerBridge.self)
129 | /// Shared heading update service
130 | open static let HeadingUpdate: HeadingUpdateService = DefaultHeadingUpdateService(bridgeClass: CLLocationManagerBridge.self)
131 | #endif
132 | }
133 |
134 |
--------------------------------------------------------------------------------
/Sources/SignificantLocationUpdateService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SignificantLocationUpdateService.swift
3 | // RxLocationManager
4 | //
5 | // Created by Hao Yu on 16/7/6.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 | #if os(iOS) || os(OSX)
9 | import Foundation
10 | import CoreLocation
11 | import RxSwift
12 |
13 | //MARK: SignificantLocationUpdateService
14 | public protocol SignificantLocationUpdateService{
15 | /// Observable of current significant location change
16 | var locating: Observable<[CLLocation]> {get}
17 | }
18 |
19 | //MARK: DefaultSignificantLocationUpdateService
20 | class DefaultSignificantLocationUpdateService: SignificantLocationUpdateService{
21 | let locMgr:CLLocationManagerBridge
22 | private var observers = [(id: Int, AnyObserver<[CLLocation]>)]()
23 | var locating: Observable<[CLLocation]>{
24 | get{
25 | return Observable.create {
26 | observer in
27 | var ownerService: DefaultSignificantLocationUpdateService! = self
28 | let id = nextId()
29 | ownerService.observers.append((id, observer))
30 | ownerService.locMgr.startMonitoringSignificantLocationChanges()
31 | return Disposables.create {
32 | ownerService.observers.remove(at: ownerService.observers.index(where: {$0.id == id})!)
33 | if(ownerService.observers.count == 0){
34 | ownerService.locMgr.stopMonitoringSignificantLocationChanges()
35 | }
36 | ownerService = nil
37 | }
38 | }
39 | }
40 | }
41 |
42 | init(bridgeClass: CLLocationManagerBridge.Type){
43 | locMgr = bridgeClass.init()
44 | locMgr.didUpdateLocations = {
45 | [weak self]
46 | mgr, locations in
47 | if let copyOfObservers = self?.observers{
48 | for (_,observer) in copyOfObservers{
49 | observer.onNext(locations)
50 | }
51 | }
52 | }
53 | locMgr.didFailWithError = {
54 | [weak self]
55 | mgr, err in
56 | if let copyOfObservers = self?.observers{
57 | for (_,observer) in copyOfObservers{
58 | observer.onError(err)
59 | }
60 | }
61 | }
62 | }
63 | }
64 | #endif
65 |
--------------------------------------------------------------------------------
/Sources/StandardLocationService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StandardLocationService.swift
3 | // RxLocationManager
4 | //
5 | // Created by Hao Yu on 16/7/6.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 | import Foundation
9 | import CoreLocation
10 | import RxSwift
11 |
12 |
13 | //MARK: StandardLocationServiceConfigurable
14 | public protocol StandardLocationServiceConfigurable{
15 | /**
16 | Set distance filter
17 |
18 | - parameter distance
19 |
20 | - returns: self for chaining call
21 | */
22 | func distanceFilter(_ distance: CLLocationDistance) -> StandardLocationService
23 | /**
24 | Set desired accuracy
25 |
26 | - parameter desiredAccuracy
27 |
28 | - returns: self for chaining call
29 | */
30 | func desiredAccuracy(_ desiredAccuracy: CLLocationAccuracy) -> StandardLocationService
31 |
32 | #if os(iOS)
33 | /**
34 | Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instm/CLLocationManager/allowDeferredLocationUpdatesUntilTraveled:timeout:)
35 | - parameter distance
36 | - parameter timeout
37 |
38 | - returns: self for chaining call
39 | */
40 | func allowDeferredLocationUpdates(untilTraveled distance: CLLocationDistance, timeout: TimeInterval) -> StandardLocationService
41 | /**
42 | Refer description in official [document](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instm/CLLocationManager/disallowDeferredLocationUpdates)
43 |
44 | - returns: self for chaining call
45 | */
46 | func disallowDeferredLocationUpdates() -> StandardLocationService
47 | /**
48 | Set Boolean value to [pausesLocationUpdatesAutomatically](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instp/CLLocationManager/pausesLocationUpdatesAutomatically)
49 |
50 | - parameter pause: Boolean value
51 |
52 | - returns: self for chaining call
53 | */
54 | func pausesLocationUpdatesAutomatically(_ pause : Bool) -> StandardLocationService
55 |
56 |
57 | /**
58 | Set Boolean value to [allowsBackgroundLocationUpdates](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instp/CLLocationManager/allowsBackgroundLocationUpdates)
59 |
60 | - parameter allow: Boolean value
61 |
62 | - returns: self for chaining call
63 | */
64 | @available(iOS 9.0, *)
65 | func allowsBackgroundLocationUpdates(_ allow : Bool) -> StandardLocationService
66 | /**
67 | Set value to [activityType](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/occ/instp/CLLocationManager/activityType)
68 |
69 | - parameter type
70 |
71 | - returns: self for chaining call
72 | */
73 | func activityType(_ type: CLActivityType) -> StandardLocationService
74 | #endif
75 |
76 | /// Current distance filter value
77 | var distanceFilter: CLLocationDistance {get}
78 | /// Current desired accuracy value
79 | var desiredAccuracy: CLLocationAccuracy {get}
80 |
81 | #if os(iOS)
82 | /// Current pausesLocationUpdatesAutomatically value
83 | var pausesLocationUpdatesAutomatically: Bool {get}
84 | @available(iOS 9.0, *)
85 | /// Current allowsBackgroundLocationUpdates
86 | var allowsBackgroundLocationUpdates: Bool {get}
87 | /// Current activityType
88 | var activityType: CLActivityType {get}
89 | #endif
90 | }
91 |
92 |
93 | //MARK: StandardLocationService
94 | public protocol StandardLocationService: StandardLocationServiceConfigurable{
95 | #if os(iOS) || os(OSX) || os(watchOS)
96 | /// Observable of current changing location, series of CLLocation objects will be reported, intermittent LocationUnknown error will be ignored and not stop subscriptions on this observable, other errors are reported as usual
97 | @available(watchOS 3.0, *)
98 | var locating: Observable<[CLLocation]>{get}
99 | #endif
100 |
101 | #if os(iOS) || os(watchOS) || os(tvOS)
102 | /// Observable of current location, only report one CLLocation object and complete, or error if underlying CLLocationManager reports error
103 | var located: Observable{get}
104 | #endif
105 |
106 | #if os(iOS)
107 | /// Observable of possible error when calling allowDeferredLocationUpdates method
108 | var deferredUpdateFinished: Observable{get}
109 | /// Observable of pause status
110 | var isPaused: Observable{get}
111 | #endif
112 |
113 | /**
114 | Return cloned instance
115 |
116 | - returns: cloned standard location service
117 | */
118 | func clone() -> StandardLocationService
119 | }
120 |
121 | //MARK: DefaultStandardLocationService
122 | class DefaultStandardLocationService: StandardLocationService{
123 | private let bridgeClass: CLLocationManagerBridge.Type
124 |
125 | #if os(iOS) || os(watchOS) || os(tvOS)
126 | var locMgrForLocation:CLLocationManagerBridge!
127 | private var locatedObservers = [(id: Int, observer: AnyObserver)]()
128 | #endif
129 |
130 | #if os(iOS) || os(OSX) || os(watchOS)
131 | var locMgrForLocating:CLLocationManagerBridge!
132 | private var locatingObservers = [(id: Int, observer: AnyObserver<[CLLocation]>)]()
133 | #endif
134 |
135 | #if os(iOS)
136 | private var deferredUpdateErrorObservers = [(id: Int, observer: AnyObserver)]()
137 | private var isPausedObservers = [(id: Int, observer: AnyObserver)]()
138 | #endif
139 |
140 | var distanceFilter:CLLocationDistance{
141 | get{
142 | #if os(OSX)
143 | return locMgrForLocating.distanceFilter
144 | #else
145 | return locMgrForLocation.distanceFilter
146 | #endif
147 | }
148 | }
149 | var desiredAccuracy: CLLocationAccuracy{
150 | get{
151 | #if os(OSX)
152 | return locMgrForLocating.desiredAccuracy
153 | #else
154 | return locMgrForLocation.desiredAccuracy
155 | #endif
156 | }
157 | }
158 |
159 | #if os(iOS)
160 | var pausesLocationUpdatesAutomatically: Bool{
161 | get{
162 | return locMgrForLocating.pausesLocationUpdatesAutomatically
163 | }
164 | }
165 |
166 | @available(iOS 9.0, *)
167 | var allowsBackgroundLocationUpdates: Bool{
168 | get{
169 | return locMgrForLocating.allowsBackgroundLocationUpdates
170 | }
171 | }
172 | var activityType: CLActivityType{
173 | get{
174 | return locMgrForLocating.activityType
175 | }
176 | }
177 | #endif
178 |
179 | #if os(iOS) || os(watchOS) || os(tvOS)
180 | var located:Observable {
181 | get{
182 | if self.locMgrForLocation.location != nil{
183 | return Observable.just(self.locMgrForLocation.location!)
184 | }else{
185 | return Observable.create{
186 | observer in
187 | var ownerService: DefaultStandardLocationService! = self
188 | let id = nextId()
189 | ownerService.locatedObservers.append((id, observer))
190 | if #available(iOS 9.0, *) {
191 | ownerService.locMgrForLocation.requestLocation()
192 | } else {
193 | #if os(iOS)
194 | ownerService.locMgrForLocation.startUpdatingLocation()
195 | #endif
196 | }
197 | return Disposables.create {
198 | ownerService.locatedObservers.remove(at: ownerService.locatedObservers.index(where: {$0.id == id})!)
199 | if(ownerService.locatedObservers.count == 0){
200 | ownerService.locMgrForLocation.stopUpdatingLocation()
201 | }
202 | ownerService = nil
203 | }
204 | }
205 | }
206 | }
207 | }
208 | #endif
209 |
210 | #if os(iOS)
211 | var isPaused:Observable{
212 | get{
213 | return Observable.create {
214 | observer in
215 | var ownerService: DefaultStandardLocationService! = self
216 | let id = nextId()
217 | ownerService.isPausedObservers.append((id, observer))
218 | return Disposables.create {
219 | ownerService.isPausedObservers.remove(at: ownerService.isPausedObservers.index(where: {$0.id == id})!)
220 | ownerService = nil
221 | }
222 | }
223 | }
224 | }
225 |
226 | var deferredUpdateFinished:Observable{
227 | get{
228 | return Observable.create {
229 | observer in
230 | var ownerService: DefaultStandardLocationService! = self
231 | let id = nextId()
232 | ownerService.deferredUpdateErrorObservers.append((id, observer))
233 | return Disposables.create {
234 | ownerService.deferredUpdateErrorObservers.remove(at: ownerService.deferredUpdateErrorObservers.index(where: {$0.id == id})!)
235 | ownerService = nil
236 | }
237 | }
238 | }
239 | }
240 | #endif
241 |
242 | #if os(iOS) || os(OSX) || os(watchOS)
243 | @available(watchOS 3.0, *)
244 | var locating:Observable<[CLLocation]> {
245 | get{
246 | return Observable.create {
247 | observer in
248 | var ownerService: DefaultStandardLocationService! = self
249 | let id = nextId()
250 | ownerService.locatingObservers.append((id, observer))
251 | //calling this method to start updating location anyway, it's no harm according to the doc
252 | ownerService.locMgrForLocating.startUpdatingLocation()
253 | return Disposables.create {
254 | ownerService.locatingObservers.remove(at: ownerService.locatingObservers.index(where: {$0.id == id})!)
255 | if(ownerService.locatingObservers.count == 0){
256 | ownerService.locMgrForLocating.stopUpdatingLocation()
257 | }
258 | ownerService = nil
259 | }
260 | }
261 | }
262 | }
263 | #endif
264 |
265 |
266 | init(bridgeClass: CLLocationManagerBridge.Type){
267 | self.bridgeClass = bridgeClass
268 | #if os(iOS) || os(watchOS) || os(tvOS)
269 | locMgrForLocation = bridgeClass.init()
270 | #endif
271 |
272 | #if os(iOS) || os(OSX) || os(watchOS)
273 | if #available(watchOS 3.0, *) {
274 | locMgrForLocating = bridgeClass.init()
275 | }
276 | #endif
277 |
278 | #if os(iOS) || os(watchOS) || os(tvOS)
279 | locMgrForLocation.didUpdateLocations = {
280 | [weak self]
281 | mgr, locations in
282 | if let copyOfLocatedObservers = self?.locatedObservers{
283 | for (_, observer) in copyOfLocatedObservers{
284 | observer.onNext(locations.last!)
285 | observer.onCompleted()
286 | }
287 | guard #available(iOS 9.0, *) else {
288 | self?.locMgrForLocation.stopUpdatingLocation()
289 | return
290 | }
291 |
292 | }
293 | }
294 | locMgrForLocation.didFailWithError = {
295 | [weak self]
296 | mgr, err in
297 | if let copyOfLocatedObservers = self?.locatedObservers{
298 | for (_, observer) in copyOfLocatedObservers{
299 | observer.onError(err)
300 | }
301 | }
302 | }
303 | #endif
304 |
305 | #if os(iOS) || os(OSX) || os(watchOS)
306 | if #available(watchOS 3.0, *){
307 | locMgrForLocating.didUpdateLocations = {
308 | [weak self]
309 | mgr, locations in
310 | if let copyOfLocatingObservers = self?.locatingObservers{
311 | for (_, observer) in copyOfLocatingObservers{
312 | observer.onNext(locations)
313 | }
314 | }
315 | }
316 |
317 | locMgrForLocating.didFailWithError = {
318 | [weak self]
319 | mgr, err in
320 | if err.domain == "kCLErrorDomain" && CLError.locationUnknown.rawValue == err.code{
321 | //ignore location update error, since new update event may come
322 | return
323 | }
324 | if let copyOfLocatingObservers = self?.locatingObservers{
325 | for (_, observer) in copyOfLocatingObservers{
326 | observer.onError(err)
327 | }
328 | }
329 | }
330 | }
331 | #endif
332 |
333 |
334 | #if os(iOS)
335 | locMgrForLocating.didFinishDeferredUpdatesWithError = {
336 | [weak self]
337 | mgr, error in
338 | if let copyOfdeferredUpdateErrorObservers = self?.deferredUpdateErrorObservers{
339 | for (_, observer) in copyOfdeferredUpdateErrorObservers{
340 | observer.onNext(error)
341 | }
342 | }
343 | }
344 |
345 | locMgrForLocating.didPausedUpdate = {
346 | [weak self]
347 | mgr in
348 | if let copyOfIsPausedObservers = self?.isPausedObservers{
349 | for(_, observer) in copyOfIsPausedObservers{
350 | observer.onNext(true)
351 | }
352 | }
353 | }
354 | locMgrForLocating.didResumeUpdate = {
355 | [weak self]
356 | mgr in
357 | if let copyOfIsPausedObservers = self?.isPausedObservers{
358 | for(_, observer) in copyOfIsPausedObservers{
359 | observer.onNext(false)
360 | }
361 | }
362 | }
363 | #endif
364 | }
365 |
366 | #if os(iOS)
367 | func allowDeferredLocationUpdates(untilTraveled distance: CLLocationDistance, timeout: TimeInterval) -> StandardLocationService{
368 | locMgrForLocating.allowDeferredLocationUpdates(untilTraveled: distance, timeout: timeout)
369 | return self
370 | }
371 |
372 | func disallowDeferredLocationUpdates() -> StandardLocationService{
373 | locMgrForLocating.disallowDeferredLocationUpdates()
374 | return self
375 | }
376 |
377 | func pausesLocationUpdatesAutomatically(_ pause: Bool) -> StandardLocationService {
378 | locMgrForLocating.pausesLocationUpdatesAutomatically = pause
379 | return self
380 | }
381 |
382 | @available(iOS 9.0, *)
383 | func allowsBackgroundLocationUpdates(_ allow: Bool) -> StandardLocationService {
384 | locMgrForLocating.allowsBackgroundLocationUpdates = allow
385 | return self
386 | }
387 |
388 | func activityType(_ type: CLActivityType) -> StandardLocationService {
389 | locMgrForLocating.activityType = type
390 | return self
391 | }
392 | #endif
393 |
394 | func distanceFilter(_ distance: CLLocationDistance) -> StandardLocationService {
395 | #if os(iOS) || os(watchOS) || os(tvOS)
396 | locMgrForLocation.distanceFilter = distance
397 | #endif
398 |
399 | #if os(iOS) || os(OSX)
400 | locMgrForLocating.distanceFilter = distance
401 | #endif
402 | return self
403 | }
404 |
405 | func desiredAccuracy(_ desiredAccuracy: CLLocationAccuracy) -> StandardLocationService {
406 | #if os(iOS) || os(watchOS) || os(tvOS)
407 | locMgrForLocation.desiredAccuracy = desiredAccuracy
408 | #endif
409 |
410 | #if os(iOS) || os(OSX)
411 | locMgrForLocating.desiredAccuracy = desiredAccuracy
412 | #endif
413 | return self
414 | }
415 |
416 | func clone() -> StandardLocationService {
417 | let cloned = DefaultStandardLocationService(bridgeClass: bridgeClass)
418 | #if os(iOS)
419 | _ = cloned.activityType(self.activityType)
420 | if #available(iOS 9.0, *) {
421 | _ = cloned.allowsBackgroundLocationUpdates(self.allowsBackgroundLocationUpdates)
422 | }
423 | _ = cloned.pausesLocationUpdatesAutomatically(self.pausesLocationUpdatesAutomatically)
424 | #endif
425 |
426 | _ = cloned.desiredAccuracy(self.desiredAccuracy)
427 | _ = cloned.distanceFilter(self.distanceFilter)
428 |
429 | return cloned
430 | }
431 | }
432 |
--------------------------------------------------------------------------------
/Sources/nextId.swift:
--------------------------------------------------------------------------------
1 | //
2 | // nextId.swift
3 | // RxLocationManager
4 | //
5 | // Created by Hao Yu on 16/7/6.
6 | // Copyright © 2016年 GFWGTH. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | private var id = -1
12 | func nextId() -> Int{
13 | id += 1
14 | return id
15 | }
--------------------------------------------------------------------------------