2016-12-06 6 views
2

Я написал класс UserLocation (ниже), в котором используется CoreLocation, чтобы получить текущее место (строка) пользователя & широта и долгота (также строка «lat, long» для переход к API).Принудительный код для ожидания создания объекта перед продолжением

Мой код ниже работает, но когда я инициализирую класс, остальная часть моего кода не дожидается завершения init до перехода, поэтому я упускаю возможность назначить значения, связанные с местоположением, которые я пытаясь получить.

Является ли это разумным подходом (или я должен думать о какой-то другой организации «MVC»), и если да, то как я могу заставить мой код ждать инициализации (с обнаружением местоположения & обратного геокодирования), чтобы закончить прежде чем двигаться дальше. Есть ли способ поставить код ниже инициализации в какое-то закрытие @escaping, указанное в init класса? Я новичок в быстрой, спасибо за ваши добрые советы.

В viewDidAppear ViewController.swift (в):

let userLocation = UserLocation() // initializes properly but code below doesn't wait. 
locationsArray[0].name = userLocation.place 
locationsArray[0].coordinates = userLocation.coordinates 

И мой UserLocation.swift класс:

import Foundation 
import CoreLocation 

class UserLocation { 
    var place = "" 
    var coordinates = "" 

    let locationManager = CLLocationManager() 
    var currentLocation: CLLocation! 

    init() { 
     returnResults() 
    } 

    func returnResults() { 
     getUserLocation { placemark in 
      if placemark != nil { 
       self.place = (placemark?.name)! 
       self.coordinates = "\((placemark?.location?.coordinate.latitude)!),\((placemark?.location?.coordinate.longitude)!)" 
      } else { 
       print("Error retrieving placemark") 
      } 
     } 
    } 

    func getUserLocation(completion: @escaping (CLPlacemark?) ->()) { 
     var placemark: CLPlacemark? 

     locationManager.requestWhenInUseAuthorization() 

     if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse || 
      CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways) { 
      currentLocation = locationManager.location 

      let geoCoder = CLGeocoder() 
      geoCoder.reverseGeocodeLocation(currentLocation) { (placemarks, error) -> Void in 

       if error != nil { 
        print("Error getting location: \(error)") 
        placemark = nil 
       } else { 
        placemark = placemarks?.first 
       } 
       completion(placemark) 
      } 
     } 
    } 
} 

extension CLPlacemark { 
    var cityState: String { 
     var result = "" 
     switch (self.locality, self.administrativeArea, self.country) { 
     case (.some, .some, .some("United States")): 
      result = "\(locality!), \(administrativeArea!)" 
     case (.some, _ , .some): 
      result = "\(locality!), \(country!)" 
     default: 
      result = name ?? "Location Unknown" 
     } 
     return result 
    } 
} 

ответ

2

Это не обязательно Swift вопрос. Ваша проблема вызвана тем фактом, что returnResults выполняет настройку переменных в асинхронном режиме, так как вызывает функцию async - getUserLocation, которая является асинхронной, так как reverseGeocodeLocation является асинхронным (так работает CoreLocation - вы не получаете местоположение синхронно, но в обратном вызове).

Вы не хотите ждать returnResults для выполнения обратного вызова, так как это означает блокирование основного потока, в то время как CoreLocation инициализирует и пытается определить местоположение. Вместо этого вы должны следовать шаблону async, используя блок завершения, который returnResults может использовать для оповещения о завершении поиска местоположения.

Примером выше будет:

class UserLocation { 
    var place = "" 
    var coordinates = "" 

    let locationManager = CLLocationManager() 
    var currentLocation: CLLocation! 

    init() { 
     // don't call anymore from here, let the clients ask for the locations 
    } 

    // This was renamed from returnResults to a more meaningful name 
    // Using the Bool in the completion to signal the success/failure 
    // of the location retrieval 
    func updateLocations(withCompletion completion: @escaping (Bool) -> Void) { 
     getUserLocation { placemark in 
      if placemark != nil { 
       self.place = (placemark?.name)! 
       self.coordinates = "\((placemark?.location?.coordinate.latitude)!),\((placemark?.location?.coordinate.longitude)!)" 
       completion(true) 
      } else { 
       print("Error retrieving placemark") 
       completion(false) 
      } 
     } 
    } 
... 

Вы можете изменить вызываемый код что-то вроде этого:

let userLocation = UserLocation() 
userLocation.updateLocations { success in 
    guard success else { return } 
    locationsArray[0].name = userLocation.place 
    locationsArray[0].coordinates = userLocation.coordinates 
} 

Вы не блокировать основной поток, и выполнить соответствующий код, когда это место доступно.

+0

Огромное спасибо - именно то, что мне было нужно. Очень четкое объяснение синтаксиса. Очень признателен! – Gallaugher

+0

@ Gallaugher рад, что я могу помочь – Cristik