Ios 编写CLLocationManagerDelegate的最佳方法,它将不经常获取位置 背景

Ios 编写CLLocationManagerDelegate的最佳方法,它将不经常获取位置 背景,ios,objective-c,reactive-cocoa,Ios,Objective C,Reactive Cocoa,我对ReactiveCocoa框架及其潜力感到非常兴奋,因此我决定咬紧牙关,用它编写我的第一个应用程序。 在我的应用程序中,我已经编写了各种服务和委托,但我现在需要“激活”它们,以便我能够继续处理实际的GUI方面的事情 也就是说,为了更好地理解这一点,我正在编写一段简单的代码来尝试一些概念。 在本例中,为CLLocationManagerDelegate编写包装器 在实际应用程序中,用例如下所示: 1) 当应用程序加载(viewDidLoad)后,2)尝试获取 设备的位置由 2.1)如果未启用

我对ReactiveCocoa框架及其潜力感到非常兴奋,因此我决定咬紧牙关,用它编写我的第一个应用程序。 在我的应用程序中,我已经编写了各种服务和委托,但我现在需要“激活”它们,以便我能够继续处理实际的GUI方面的事情

也就是说,为了更好地理解这一点,我正在编写一段简单的代码来尝试一些概念。 在本例中,为CLLocationManagerDelegate编写包装器

在实际应用程序中,用例如下所示:

  • 1) 当应用程序加载(viewDidLoad)后,2)尝试获取 设备的位置由
  • 2.1)如果未启用位置服务,则
  • 2.1.1)检查授权状态,如果允许
    开始监控重要位置变化
  • 2.1.2)否则返回错误
  • 2.2)其他(位置服务已启用)
  • 2.2.1)如果地点经理上一个地点是“最近的”(6小时或更短),则返回
  • 2.2.2)其他开始监控重要位置变化
  • 3) 返回位置时(直接返回或通过
    locationManager:(CLLocationManager*)管理器
    didUpdateLocations:(NSArray*)位置
    ),然后我们也停止监视重要位置更改
  • 4) 如果调用LocationManagerDelegate的代码收到并出错,则向代理请求一个固定的“默认”值
  • 5) 然后,下一段代码使用该位置(获取或默认)启动并对第三方服务进行一系列调用(对Web服务的调用等)
您可以在以下位置看到测试线束:

担心 线束“工作”的原因是它断开并获取位置。然而,我真的很担心我这样做是正确的

考虑一下这个代码

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) 当我返回位置时,是否应按
    sendNext
    sendCompleted
    执行?目前它被编码为
    sendNext
    ,但它看起来像是将被编码为
    sendCompleted
  • 3) 实际上,答案是否取决于我如何创建委托。每次加载视图时,此测试应用程序都会创建一个新的委托。这是我应该做的事情吗?我应该让代表成为一个单独的人。如果是单身,那么sendNext似乎是正确的选择。但是如果这样做,是否意味着我将委托中的
    latestLocationSignal
    中的代码移到init方法中
为这些漫无边际的问题道歉,我的脑子里似乎在转圈圈

我如何处理错误?我可以使用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,但它似乎是在sendNext完成时完成的

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

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

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

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


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

在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))
            }
        }
    }

}

请注意,由于目前是alpha之前的版本,语法可能会发生变化。

将上述答案更新为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)
    }
}

谢谢你<代码>+[RACSignal return::方法似乎不错。我尝试的另一种方法是在委托中定义defaultLocation方法以返回信号。你的方法似乎更简单,也达到了同样的效果。而且,是的,请欣赏这是一个广泛的问题,因此更难“回答”。但这给了我一些思考和尝试的机会。我不会把这个问题标记为完整的,以防其他人有什么补充。(我不确定Github上的代码是否有助于我尝试通过委托实现什么。)
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)
    })