用于编写CLLocationManagerDelegate的最佳Reactive-Cocoa方法,该方法很少获取位置

时间:2014-02-28 05:48:05

标签: ios objective-c reactive-cocoa

背景

我对ReactiveCocoa框架及其潜力感到非常兴奋,所以我决定我会咬紧牙关并使用它编写我的第一个应用程序。 在我的应用程序中,我已经编写了各种服务和代理,但我现在需要'Reactive-Cocoa-ise',以便我可以继续使用实际的GUI方面。

那就是说,为了让我更好地理解这一点,我正在编写一些简单的代码来尝试概念。 在这种情况下,为CLLocationManagerDelegate编写一个包装器。

在实际应用中,用例如下:

  • 1)当应用程序加载时(viewDidLoad)然后2)尝试获取
  • 的设备位置
  • 2.1)如果未启用位置服务,则
  • 2.1.1)检查授权状态,如果允许startMonitoringSignificantLocationChanges
  • 2.1.2)否则返回错误
  • 2.2)else(已启用位置服务)
  • 2.2.1)如果位置经理的最后位置是“最近”(6小时或更短),则返回
  • 2.2.2)否则startMonitoringSignificantLocationChanges
  • 3)返回位置时(直接或通过 locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations),然后我们stopMonitoringSignificantLocationChanges
  • 4)如果调用LocationManagerDelegate的代码收到并收到错误,请向代表询问固定的“默认”值
  • 5)然后下一段代码使用该位置(提取或默认)关闭并对第三方服务进行一堆调用(调用WebServices等)

您可以在https://github.com/ippoippo/reactive-core-location-test

看到测试工具

的关注

线束“起作用”,它起飞并取出位置。但是,我真的很担心我是以正确的方式做到这一点。

考虑这段代码

RACSignal *fetchLocationSignal = [lmDelegate latestLocationSignal];

RACSignal *locationSignal = [fetchLocationSignal catch:^RACSignal *(NSError *error) {
    NSLog(@"Unable to fetch location, going to grab default instead: %@", error);
    CLLocation *defaultLocation = [lmDelegate defaultLocation];
    NSLog(@"defaultLocation = [%@]", defaultLocation);
    // TODO OK, so now what. I just want to handle the error and 
    // pass back this default location as if nothing went wrong???
    return [RACSignal empty];
}];

NSLog(@"Created signal, about to bind on self.locationLabel text");

RAC(self.locationLabel, text) = [[locationSignal filter:^BOOL(CLLocation *newLocation) {
    NSLog(@"Doing filter first, newLocation = [%@]", newLocation);
    return newLocation != nil;
}] map:^(CLLocation *newLocation) {
    NSLog(@"Going to return the coordinates in map function");
    return [NSString stringWithFormat:@"%d, %d", newLocation.coordinate.longitude, newLocation.coordinate.latitude];
}];

问题

  • 1)如何处理错误?我可以使用catch然后让我有机会向代表询问默认位置。但是,我然后坚持如何将该默认位置作为信号传回?或者我只是更改defaultLocation方法以返回RACSignal而不是CLLocation ??
  • 2)当我返回该位置时,是应该以{{1​​}}还是sendNext完成?目前它被编码为sendCompleted,但似乎可以像sendNext那样完成。
  • 3)实际上,答案取决于我如何创建代表。每次加载视图时,此测试应用程序都会创建一个新的Delegate。这是我应该做的事情,我应该让代表成为单身人士。如果它是单身,那么sendNext似乎是正确的做法。但是,如果那意味着我将我在委托中的sendCompleted中的代码移动到init方法中呢?

为漫无边际的问题道歉,似乎只是在我的脑海里乱转。

3 个答案:

答案 0 :(得分:3)

  

如何处理错误?我可以使用catch,然后让我有机会向代表询问默认位置。

所以关闭。使用+[RACSignal return:]创建一个仅发送默认位置的信号:

RACSignal *locationSignal = [fetchLocationSignal catch:^RACSignal *(NSError *error) {
    NSLog(@"Unable to fetch location, going to grab default instead: %@", error);
    CLLocation *defaultLocation = [lmDelegate defaultLocation];
    NSLog(@"defaultLocation = [%@]", defaultLocation);
    return [RACSignal return:defaultLocation];
}];
  

当我返回该位置时,是应该以sendNext还是sendCompleted完成?目前它被编码为sendNext,但似乎可以像sendCompleted那样完成。

为了实际发送数据,您需要使用-sendNext:。如果这是信号发送的唯一内容,那么它之后也应该-sendCompleted。但是,如果它随着准确度的提高或位置的变化而继续发送位置,那么它就不应该完成。

更一般地说,信号可以发送任意数量的next s(0 *),但只能完成错误。一旦发送完成或错误,信号就完成了。它不会再发送任何值。

  

实际上,答案取决于我如何创建委托。每次加载视图时,此测试应用程序都会创建一个新的Delegate。这是我应该做的事情,我应该让代表成为单身人士。如果它是单身,那么sendNext似乎是正确的做法。但是,如果那意味着我将我在委托中的latestLocationSignal中的代码移动到init方法中呢?

我不确定我是否有足够的上下文来回答这个问题,除了说单身人士永远不是答案;)每次加载视图时创建一个新的委托似乎对我来说是合理的。

很难回答这样的广泛问题,因为我几乎不了解你的设计。希望这些答案有所帮助。如果您需要更多说明,具体示例确实很有帮助。

答案 1 :(得分:2)

在ReactiveCocoa 3.0和swift中似乎非常容易:

import ReactiveCocoa
import LlamaKit

class SignalCollector: NSObject, CLLocationManagerDelegate {

    let (signal,sink) = Signal<CLLocation, NoError>.pipe()

    let locationManager = CLLocationManager()

    func start(){
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.startUpdatingLocation()
    }

    func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        for item in locations {
            if let location = item as? CLLocation {
                sink.put(Event.Next(Box(location))
            }
        }
    }

}

无论您何时需要收听位置事件,请与观察者一起使用信号:

var locationSignals : [CLLocation]  = []
signal.observe(next: { value in locationSignals.append(value)})

请注意,由于此时语法可能会发生变化,因此它会预先发布。

答案 2 :(得分:2)

将上面的答案更新为ReactiveCocoa 4和Swift 2.1:

import Foundation
import ReactiveCocoa

class SignalCollector: NSObject, CLLocationManagerDelegate {

let (signal,sink) = Signal<CLLocation, NoError>.pipe()

let locationManager = CLLocationManager()

  func start(){
    locationManager.delegate = self
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.startUpdatingLocation()
  }

  func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

    for item in locations {

        guard let location = item as CLLocation! else { return }         
        sink.sendNext(location)
    }
}

倾听事件:

 var locationSignals : [CLLocation]  = []  
 signal.observeNext({ newLocation in
      locationSignals.append(newLocation)
    })