在Swift(iOS)中一个接一个地调用异步任务

时间:2015-11-30 20:19:31

标签: ios swift asynchronous

对于iOS天气应用程序,我在尝试使用swift中的dispatch async逐个运行多个异步任务时遇到问题。我想要'update()'函数:

  • 获取用户的位置(并将纬度和经度存储在类变量中)
  • 当位置服务完成后,根据新填充的lat& amp;来调用weather API。长变量
  • 当API调用(以及后续的XML解析)完成时,更新UI(iOS表视图)

(对我来说很容易,我是一名最近自学成才的编码员,所以我假设你们经验丰富,可以指出各种错误!任何帮助都会受到大力赞赏。)

    var latitude: String = ""
    var longitude: String = ""
    var locationManager: CLLocationManager!
    var forecastData: Weather = Weather() // the weather class has it's own asynchronous NSURLSession called retrieveForecast() 
                                          // which calls the Open Weather Map API and parses the XML

    func refresh() {
        // Here's where I don't know what I'm doing:
        let group = dispatch_group_create()
        dispatch_group_enter(group)
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
            self.getLocation()
            dispatch_group_leave(group)
        }
        dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
            self.getWeather()
        }
        self.updateUI() // ...and I know that this is in totally the wrong place!
    }


    // function(s) to get phone's location:
    func getLocation() {
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
        locationManager.requestWhenInUseAuthorization()
        locationManager.distanceFilter = 100.0
        locationManager.startUpdatingLocation()
    }
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        self.locationManager.stopUpdatingLocation()
        manager.stopUpdatingLocation()
        for item: AnyObject in locations {
            if let location = item as? CLLocation {
                if location.horizontalAccuracy < 1000 {
                    manager.stopUpdatingLocation()
                    self.latitude = String(location.coordinate.latitude)
                    self.longitude = String(location.coordinate.longitude)
                }
            }
        }
    }

    // function to download and parse the weather data within forecastData object
    func getWeather() {
            let apiCall = "http://api.openweathermap.org/data/2.5/forecast?lat=" + self.latitude
            + "&lon=" + self.longitude + "&mode=xml&appid=" + self.openWeatherMapAPIKey
        NSLog("getWeather called with api request: \(apiCall)")
        self.forecastData.retrieveForecast(apiCall)
    }

2 个答案:

答案 0 :(得分:3)

对于任何异步操作,最好有一个完成回调。 在您的情况下,如果您已为getLocationgetWeather实施了回调,那么您将永远不需要dispatch_groups,只需从getWeather&#拨打getLocation即可39; s回调,然后你只需从refreshUI的回调中拨打getWeather

    var latitude: String = ""
    var longitude: String = ""
    var locationManager: CLLocationManager!
    var forecastData: Weather = Weather() // the weather class has it's own asynchronous NSURLSession called retrieveForecast() 
                                          // which calls the Open Weather Map API and parses the XML

    func refresh() {
        self.getLocation()
    }


    // function(s) to get phone's location:
    func getLocation() {
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
        locationManager.requestWhenInUseAuthorization()
        locationManager.distanceFilter = 100.0
        locationManager.startUpdatingLocation()
    }
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        self.locationManager.stopUpdatingLocation()
        manager.stopUpdatingLocation()
        for item: AnyObject in locations {
            if let location = item as? CLLocation {
                if location.horizontalAccuracy < 1000 {
                    manager.stopUpdatingLocation()
                    self.latitude = String(location.coordinate.latitude)
                    self.longitude = String(location.coordinate.longitude)
                    self.getWeather()
                }
            }
        }
    }

    // function to download and parse the weather data within forecastData object
    func getWeather() {
            let apiCall = "http://api.openweathermap.org/data/2.5/forecast?lat=" + self.latitude
            + "&lon=" + self.longitude + "&mode=xml&appid=" + self.openWeatherMapAPIKey
        NSLog("getWeather called with api request: \(apiCall)")
        self.forecastData.retrieveForecast(apiCall)
        // assuming that self.forecastData.retrieveForecast(apiCall) is completely synchronous, we can call updateUI below
        self.updateUI()
    }

以下是代码,演示如何正确使用dispatch_group

func refetchOrdersAndChats(remoteNotificationData: [NSObject : AnyObject], completion: ((Bool)->())?) {
    var overallSuccess: Bool = true
    let refetchGroup = dispatch_group_create();

    dispatch_group_enter(refetchGroup);
    CPChatController.sharedInstance.updateChat(remoteNotificationData, completion: { success in
        overallSuccess = success && overallSuccess
        dispatch_group_leave(refetchGroup);
    })

    dispatch_group_enter(refetchGroup);
    CPChatController.sharedInstance.fetchNewOrdersWithNotification(remoteNotificationData, completion: { success in
        overallSuccess = success && overallSuccess
        dispatch_group_leave(refetchGroup);
    })

    dispatch_group_notify(refetchGroup,dispatch_get_main_queue(), {
        completion?(overallSuccess)
    })
}

答案 1 :(得分:1)

在开始学习关闭/阻塞之后,我终于找到了我的答案,以为我会分享。我需要做的是为我的Weather类中的参数添加一个闭包,它在XML解析完成时返回一个布尔值,并允许我在更新UI之前在我的View Controller中等待它。我希望这可以帮助其他人寻找类似的答案,如果有任何专业人士能够使这个代码更好,请添加评论!

...

// getWeather function in my View Controller (called from location manager)
  func getWeather() {
            let apiCall = "http://api.openweathermap.org/data/2.5/forecast?lat=" + self.latitude
            + "&lon=" + self.longitude + "&mode=xml&appid=" + self.openWeatherMapAPIKey
        NSLog("getWeather called with api request: \(apiCall)")
        self.forecastData.retrieveForecast(apiCall, completion: { success in
            if success {
                self.updateUI()
            } else {
                NSLog("Parse unsuccessful")
                // I'll handle the issue here
            }
        });
  }

...在Weather类功能中:

func retrieveForecast(url: String, completion: ((Bool)->())?) {
    self.reset()
    let apiCall = NSURL(string: url)
    let urlRequest: NSURLRequest = NSURLRequest(URL: apiCall!)
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithRequest(urlRequest) {
        (data, response, error) -> Void in
        self.xmlParser = NSXMLParser(data: data!)
        self.xmlParser.delegate = self
        let success: Bool = self.xmlParser.parse()
        if !success {
            NSLog("Did not parse. \(error?.localizedDescription)")
            completion?(false)
        } else {
            self.currentParsedElement = ""
            self.retrievingForecast = false
            completion?(true)
        }
    }
    task.resume()
}