2016-12-06 11 views
2

CoreLocationを使用してユーザーの現在の場所(文字列)を取得するクラスUserLocation(以下)を作成しました&緯度と経度APIに渡す)。続行する前にオブジェクトのインスタンス化を待つように強制します

私のコードは動作しますが、クラスを初期化するとき、残りのコードはinitが終了するのを待たずに移動しますので、私は位置関係の値を割り当てることができません検索しようとしています。

これは妥当なアプローチですか(そうでなければ、もう少し "MVC"組織について考える必要があります)、そうであれば、どのようにして初期化(場所が見つかる&逆ジオコーディング)移動する前に。クラスのinitで指定されている@エスケープクロージャのような初期化の下にコードを置く方法はありますか?あなたの親切なアドバイスに感謝しています。これは必ずしもスウィフト問題ではありません

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

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

そして、私のUserLocation.swiftクラス:ViewController.swiftのviewDidAppear()で

。問題は、returnResultsが、非同期機能であるgetUserLocationを呼び出すため、非同期の方法で変数設定を実行するという事実に起因しています。が非同期です(CoreLocationの動作方法です。コールバックで)。

returnResultsがコールバックを実行するのを待つ必要はありません。これは、CoreLocationが初期化してその場所を特定しようとしている間、メインスレッドをブロックすることを意味するためです。代わりに、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

関連する問題