2017-09-16 8 views
7

私はSwiftのメッセージベースのアーキテクチャを試しています。私は、例えばElm Architectureに似た何かをしようとしています。これは私のコードがどのように見えるかです:メッセージベースのアーキテクチャの一般的なメッセージ

enum SideEffect<Message> { 

    case sendRequest((String) -> Message) 
} 

protocol Component { 

    associatedtype Message 

    mutating func send(msg: Message) -> [SideEffect<Message>] 
} 

struct State: Component { 

    var something: String? 

    enum Message { 

     case downloadSomething 
     case receiveResponse(String) 
    } 

    mutating func send(msg: Message) -> [SideEffect<Message>] { 
     switch msg { 
      case .downloadSomething: 
       return [.sendRequest(Message.receiveResponse)] 
      case .receiveResponse(let response): 
       something = response 
       return [] 
     } 
    } 
} 

ので状態はStateによってモデル化されていて、Message Sを送信することによって、それを変更することができます。計算する副作用がある場合は、SideEffectメッセージとして返され、他の誰かが世話をします。各SideEffectメッセージは、コールバック引数を受け取り、副作用が終了すると、Messageが送信されます。これは素晴らしいです。

一般的な副作用メッセージを表示するにはどうすればよいですか。

struct Request<ReturnType> { … } 

、要求をロードして、タイプReturnTypeの値を返すために、関連する副作用があります:私はこのような何かがしたい

enum SideEffect<Message> { 
    case sendRequest(Request<T>, (T) -> Message) 
} 

をしかし、これは(明らかに)コンパイルされません。 caseTより一般的でなければならないからです。 Tとは関係のないその他の副作用があるので、私は全体をT以上にすることはできません。

Request<T>SideEffectメッセージを作成し、後でMessageTとディスパッチすることはできますか。 (私はthis feature discussed on swift-evolutionのようなものが欲しいと思います)

+0

我々はプロトコル 'Returnable'を作ると' ReturnType'は、このプロトコルに適合させる方法について?次に、 'String'のような他の型もこのプロトコルに準拠するように拡張することができます。 – sCha

+0

あなたは '' T 'を消去したいと思うでしょう。これは通常、クロージャで行うことができます(例えば、リクエストを実行するクロージャを作成し、その結果を関数に渡してメッセージを生成し、 '' T "を外界から受け入れる)。私はエルムアーキテクチャに精通していないので、あなたは 'Request'がどのように実装されるのかは不明ですが、[これと似たようなものです](http://swift.sandbox.bluemix.net/#/ repl/59e15aad6cbea87f72c470cc)は実行可能ですか? – Hamish

+0

もう一度救助にハミッシュ!私はそれが私が必要としていたものだと思う。私はタイプ消去の周りに拍手していたが、明らかに私はそのコンセプトに慣れていないので、私は解決策を思い付かなかった。どうもありがとうございました!私はあなたのコメントを回答に変換させる質問にフラグを立てました。 – zoul

答えて

1

あなたは消去を入力したいと思うでしょうT - これは、彼らが作成されたサイトからのコンテキストを参照することができるので、外の世界に。例えば

、モックRequest<T>と(それが非同期オペレーションの仮定):

struct Request<T> { 

    var mock: T 

    func doRequest(_ completion: @escaping (T) -> Void) { 
     // ... 
     completion(mock) 
    } 
} 

我々は、所与(Message) -> Voidコールバックをとるクロージャを保持RequestSideEffect<Message>を構築し、次いで捕捉に要求を行うことができRequest<T>例えば、(T) -> Messageを通して結果を転送し、その後、バックコールバックに渡すことができ、その結果(従ってT変数型を維持するクロージャに「含まれる」):

今すぐあなたのSideEffect<Message>は次のようになります。

enum SideEffect<Message> { 
    case sendRequest(RequestSideEffect<Message>) 
} 

そして、あなたはこのようなStateを実装することができます

protocol Component { 
    associatedtype Message 
    mutating func send(msg: Message) -> [SideEffect<Message>] 
} 

struct State: Component { 

    var something: String 

    enum Message { 
     case downloadSomething 
     case receiveResponse(String) 
    } 

    mutating func send(msg: Message) -> [SideEffect<Message>] { 
     switch msg { 
     case .downloadSomething: 
      let sideEffect = RequestSideEffect(
       request: Request(mock: "foo"), nextMessage: Message.receiveResponse 
      ) 
      return [.sendRequest(sideEffect)] 
     case .receiveResponse(let response): 
      something = response 
      return [] 
     } 
    } 
} 

var s = State(something: "hello") 
let sideEffects = s.send(msg: .downloadSomething) 

for case .sendRequest(let sideEffect) in sideEffects { 
    sideEffect.doRequest { 
     _ = s.send(msg: $0) // no side effects expected 
     print(s) // State(something: "foo") 
    } 
} 
関連する問題