2017-09-16 30 views
7

Swift'de ileti tabanlı mimari denemeler yapıyorum. Örneğin Elm Mimarisine benzer bir şey yapmaya çalışıyorum.İleti tabanlı mimaride genel iletileri

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 [] 
     } 
    } 
} 

Yani devlet State ile modellenmiş ve siz Message s göndererek değiştirebilirsiniz: Bu benim kod böyle görünüyor. Hesaplamak için herhangi bir yan etkisi varsa, SideEffect mesaj olarak iade edilir ve başka biri tarafından halledilir. Her bir SideEffect mesajında, yan etki bittiğinde göndermek için bir "geri arama" argümanı, bir Message alır. Bu harika çalışıyor.

Şimdi, genel bir yan etki iletisine sahip olmak istiyorsam ne olur? isteği yük ve tip ReturnType bir değer döndürmek için

struct Request<ReturnType> { … } 

Ve ilgili yan etkisi vardır: Böyle bir şey istiyorum

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

Ama bu (tabii ki) derleme değil case'un T üzerinden jenerik olması gerekir. T ile hiçbir ilgisi olmayan diğer yan etkileri olduğundan, SideEffect genelini T üzerinden tamamlayamıyorum.

Bir Request<T> numaralı SideEffect iletisini oluşturabilir miyim, daha sonra bir MessageT ile gönderebilir miyim? (Ben this feature discussed on swift-evolution gibi bir şey istiyorum.)

+0

? Daha sonra, bu protokole de uygun şekilde, 'String' gibi diğer türleri de uzatabiliriz. – sCha

+0

'T' silme yapmak isteyeceksiniz, genellikle bu kapaklarla yapılabilir (örneğin, isteği gerçekleştiren bir kapanma yaparsınız ve daha sonra sonucu iletinize gönderen bir işlev iletirsiniz. Dış dünyadan T '. Elm Mimarisine hiç aşina değilim ve bu yüzden 'Request' 'in uygulanmasını nasıl beklediğinizden emin değilim, ama [bunun gibi bir şey] (http://swift.sandbox.bluemix.net/#/ repl/59e15aad6cbea87f72c470cc) uygulanabilir mi? – Hamish

+0

Hamish yine kurtarmaya! Bence tam da ihtiyacım olan şey bu. Tip silme işleminin etrafında atıyordum, ama görünüşe göre konsepte alışkın olmadım, bu yüzden çözümle gelmedim. Çok teşekkür ederim! Yorumunuzu cevaba dönüştürmek için soruyu işaretledim. – zoul

cevap

1

Silme T yazmak isteyebilirsiniz - genellikle bu bağlamı açığa çıkarmadan, oluşturuldukları siteden içerik başvuruda bulunabileceğinden, kapanışlarla yapılabilir dış dünyaya. sahte Request<T> (o varsayarak bir zaman uyumsuz işlem) ile Mesela

:

struct Request<T> { 

    var mock: T 

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

Sonra belirli bir (Message) -> Void geri arama götüren bir kapatma tutan ve bir RequestSideEffect<Message> inşa edebilirsiniz yakalanmış bir istek yapar

struct RequestSideEffect<Message> { 

    private let _doRequest: (@escaping (Message) -> Void) -> Void 

    init<T>(request: Request<T>, nextMessage: @escaping (T) -> Message) { 
     self._doRequest = { callback in 
      request.doRequest { 
       callback(nextMessage($0)) 
      } 
     } 
    } 

    func doRequest(_ completion: @escaping (Message) -> Void) { 
     _doRequest(completion) 
    } 
} 
012: Request<T> örneği, bir (T) -> Message aracılığıyla sonuç iletirken bunun sonucu, daha sonra (bu yüzden kapağın içinde içerirken ' T değişken türü tutulması) geri arama geri geçirilebilir

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

Ve böyle State uygulayabilirsiniz:

Şimdi SideEffect<Message> şöyle olabilir biz bir protokol `Returnable` yapmak ve üretmesidir` ReturnType` Bu protokole uygun nasıl

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") 
    } 
} 
İlgili konular