結合とCorelocation、パート1 -出版者と代表


リンゴからのAPIのほとんどは、Objective - Cと代表パターンの時代から来ます.これを念頭に、チャレンジSwiftuiに適応する方法を考え出すことです.具体的には、結合を使用してデリゲートから発行者を作成します.
例えば、my app Heartwitch is an Apple Watch app for live streamers . この場合、頻繁に委任パターンを移植するHealthKitを使用します.また、私は蒸気4、インディペンデント時計アプリ、および最も重要なSwiftuiなどの新しい技術を使用しています.
この一連の記事では、組み合わせのための古いAPIを適応するプロセスについて詳しく説明します.具体的に我々はcorelocationとあなたの緯度と経度を表示する基本的なSwiftuiアプリを構築する予定です.これには:
  • 代表から出版社をつくること
  • 値を変換する関数反応プログラミングの使用
  • フラットマップの理解と出版社
  • この部分については、我々はどのように作成するに入るだろうProtocol and Class これは、委任パターンとSwiftuiと結合の反応性の機能プログラミングの間の移動として機能します.

    2009年のような代表的な代表


    年以上の間、アップルは開発者にUIオブジェクトの代わりに対応して、更新して、行動する能力を与えるために、委任パターンをしばしば使いました.このパターンは、特に客観的なCの利点の過剰を持っていますが、迅速、特にSwiftuiでは、このパターンは不器用になります.
    これは、SwiftUIが更新を扱うことができるような方法でデリゲートを応答させる必要がある場合です.
    アップルの古いAPIでは、通常これを見ます.
    protocol NSDelegate : NSObjectProtocol {
      func manager(_ manager: NSManager, doneWith data: AnyObject)
      func manager(_ manager: NSManager, grantedPermission: Bool)
    }
    class NSManager : NSObject {
      weak var delegate : NSDelegate?
    
      func requestAuthorization() {}
      func doThing () {}
    }
    
    corelocationの場合は以下のようになります.
    protocol CLLocationManagerDelegate : NSObjectProtocol {
      func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation])
      func locationManager(_: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus)
    }
    class CLLocationManager : NSObject {
      weak var delegate : CLLocationManagerDelegate?
    
      func requestWhenInUseAuthorization() {}
      func startUpdatingLocation () {}
    }
    
    言い換えれば、我々は我々を結合する出版社をつくる必要があるでしょうObservableObject 聞くことができるか反応します.一度ObservableObject 適切に反応するView したがって、更新されます.最後に、アプリケーションでこれを見てください.

    我々の出版社をセットアップする前に、我々の足場をみましょうView and ObservableObject .

    Swiftui足場


    まず最初にSwiftuiビューを構築することから始めましょう.この場合、我々はAとともにSwiftuiビューを作成するでしょうObservableObject .
    struct LocationView: View {
      // CLLocationManager is basically a singleton so an EnvironmentObject ObservableObject makes sense
      @EnvironmentObject var locationObject: CoreLocationObject
      var body: some View {
        VStack {
          // use our extension method to display a description of the status
          Text("\(locationObject.authorizationStatus.description)")
            .onTapGesture {
              self.locationObject.authorize()
            }
          // use Optional.map to hide the Text if there's no location
          self.locationObject.location.map {
            Text($0.description)
          }
        }
      }
    }
    
    このLocationView 単に線を記述する線で場所の説明で1行を表示しますCLAuthorizationStatus この拡張モジュールの使用
    extension CLAuthorizationStatus: CustomStringConvertible {
      public var description: String {
        switch self {
        case .authorizedAlways:
          return "Always Authorized"
        case .authorizedWhenInUse:
          return "Authorized When In Use"
        case .denied:
          return "Denied"
        case .notDetermined:
          return "Not Determined"
        case .restricted:
          return "Restricted"
        @unknown default:
          return "🤷‍♂️"
        }
      }
    }
    
    さあ行きましょうObservableObject , 名前CoreLocationObject :
    import Combine
    import CoreLocation
    import SwiftUI
    class CoreLocationObject: ObservableObject {
      @Published var authorizationStatus = CLAuthorizationStatus.notDetermined
      @Published var location: CLLocation?
      init() { }
    }
    
    最後に、EnvironmentObject 使用するアプリケーションでは
    LocationView().environmentObject(CoreLocationObject())
    
    今、私たちは私たちの足場の設定を、プラグインのcorelocationを聞かせている.

    デリバティブを結合出版社に拡張する



    デリゲートパターンで、デリゲート(この場合)CoreLocationManagerDelegate ) 場所の更新を受け取る.したがって、それは我々のための出版社を作成する理想的なオブジェクトですObservableObject .
    我々のためにObservableObject corelocation変更に反応するために、デリゲートは私達のために出版者を作成しなければならない.これを念頭に、私は代表者に公開するために拡張している.つまり、デリゲートは出版社の工場でもあります.
    protocol CLLocationManagerCombineDelegate: CLLocationManagerDelegate {
      func authorizationPublisher() -> AnyPublisher<CLAuthorizationStatus, Never>
      func locationPublisher() -> AnyPublisher<[CLLocation], Never>
    }
    
    我々のアプリケーションの場合、我々は、緯度と経度だけでなく、コアの位置の認定ステータスを表示している.したがって、我々は出版社のために実装される2つの方法を必要とするだけです.
    新しいプロトコルの実装です.
    class CLLocationManagerPublicist: NSObject, CLLocationManagerCombineDelegate {
      let authorizationSubject = PassthroughSubject<CLAuthorizationStatus, Never>()
      let locationSubject = PassthroughSubject<[CLLocation], Never>()
      func authorizationPublisher() -> AnyPublisher<CLAuthorizationStatus, Never> {
        return Just(CLLocationManager.authorizationStatus())
          .merge(with:
            authorizationSubject.compactMap { $0 }
          ).eraseToAnyPublisher()
      }
      func locationPublisher() -> AnyPublisher<[CLLocation], Never> {
        return locationSubject.eraseToAnyPublisher()
      }
      func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        locationSubject.send(locations)
      }
      func locationManager(_: CLLocationManager, didFailWithError _: Error) {
        // Implement to avoid crashes
        // Extra Credit: Create a publisher for errors :/
      }
      func locationManager(_: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        authorizationSubject.send(status)
      }
    }
    
    このクラスがどのように機能するかを分解しましょう.

    タイプ消去出版社の権限


    私たちの広報担当者はどんな値にも保持する必要はありません.最後に、それは単にデータからの変換を目的としていますCoreLocationManagerObservableObject . この理由から、私たちはPassthroughSubject のためにCLLocation and CLAuthorizationStatus . 即ち、PassthroughSubject 値を受け取るのではなく、値を渡します.
    PassthroughSubject 場所のプロパティ、デリゲートメソッドから受け取った値を対象に委任できます.
    当社の最初の出版社を作成するCLLocation は単純です.
    class CLLocationManagerPublicist: NSObject, CLLocationManagerCombineDelegate {
    ...
      let locationSubject = PassthroughSubject<[CLLocation], Never>()
      func locationPublisher() -> AnyPublisher<[CLLocation], Never> {
        return locationSubject.eraseToAnyPublisher()
      }
      func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        locationSubject.send(locations)
      }
    ...
    }
    
    実現するために重要なことは、タイプ消去を実装する必要があることですeraseToAnyPublisher . Swiftuiの導入と組み合わせる迅速な改善に含まれます.これらの改善は、かなり複雑なジェネリック型をもたらす強力な変換を可能にします.例えば、我々authorizationPublisher 戻り型AnyPublisher :
      func authorizationPublisher() -> AnyPublisher<CLAuthorizationStatus, Never> {
        return Just(CLLocationManager.authorizationStatus())
          .merge(with:
            authorizationSubject.compactMap { $0 }
          ).eraseToAnyPublisher()
      }
    
    なしでeraseToAnyPublisher , 返り値は次のようになります.
    Publishers.Merge<Just<CLAuthorizationStatus>, Publishers.CompactMap<PassthroughSubject<CLAuthorizationStatus, Never>, CLAuthorizationStatus>>
    
    同様にlocationPublisher , 返り値は次のようになります.
    PassthroughSubject<[CLLocation], Never>
    
    最後に、これはかなり複雑なプロトコルと戻り値の型を作成することができます.までObservableObject 懸念しているのは、どのように発行者が変換されるかについては気にしません.
    したがって、我々のプロトコルはAnyPublisher 戻り値の型.最終的に、我々は両方を簡素化し、機能変換の方法を隠すことができますeraseToAnyPublisher . 同様に、実装コールeraseToAnyPublisher 戻り値の型を縮小し、プロトコルのメソッドシグネチャに一致します.
    今、我々はマッチング出版社の種類を作成する方法を考え出して、変換しましょうCLAuthorizationStatu ので、ビュー内で使用可能です.

    承認ステータスの発行元への変換


    我々の間locationSubject corelocationから値を反映するauthorizationSubject corelocationのステータスの現実から同期されます.この理由から、私たちは、どんな状態でも初期状態を含むコードを書く必要があるでしょうPassthroughSubject 受信する.
      func authorizationPublisher() -> AnyPublisher<CLAuthorizationStatus, Never> {
        return Just(CLLocationManager.authorizationStatus())
          .merge(with:
            authorizationSubject
          ).eraseToAnyPublisher()
      }
    
    CoreLocationManagerDelegate 更新を送信するauthorizationStatus , 我々は、経由で初期状態にアクセスする必要がありますCLLocationManager.authorizedStatus . 幸いにも、組み合わせは、単一の値を使用して内蔵の出版社が含まれてJust .Just しかし、我々は我々から我々の残りの公表値を含める必要がある初期値を与えるPassthroughSubject . この理由から、私たちはmerge 我々のものからの結果でinthe値に加わるためにauthorizationSubject :

    我々は今出版社の工場のセットアップを持っている!
    組み合わせについての詳細については、ドニーWalsと私のポッドキャストエピソードをチェックアウト:

    Donny Walsとの実用的結合


    EmpowerApps。ショーを見る





    ブラウザがオーディオ要素をサポートしていません.





    1 x
    初期化
    ×

    次は何ですか。


    このシリーズの次の部分では、この実装の使い方を学びますObservableObject CoreLocationObject . 具体的には、反応関数型プログラミングの中で関数プログラミングの力になります.楽しむ!