我正在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
来更改状态。如果要计算任何副作用,它们将作为SideEffect
消息返回,并由其他人处理。每个SideEffect
消息都带有一个“回调”参数,一个Message
在副作用结束时发送。这很有效。
现在,如果我想要一个通用的副作用消息怎么办?我想有这样的事情:
struct Request<ReturnType> { … }
并且有相关的副作用来加载请求并返回类型为ReturnType
的值:
enum SideEffect<Message> {
case sendRequest(Request<T>, (T) -> Message)
}
但是这显然不会编译,因为case
必须是T
的通用。我无法将整个SideEffect
泛化为T
,因为其他副作用与T
无关。
我可以以某种方式创建一个SideEffect
消息,其中Request<T>
稍后会发送Message
T
吗? (我想我想要像this feature discussed on swift-evolution这样的东西。)
答案 0 :(得分:1)
您需要键入erase T
- 通常可以使用闭包来完成,因为它们可以从创建它们的站点引用上下文,而不会将该上下文暴露给外部世界。
例如,使用模拟Request<T>
(假设它是异步操作):
struct Request<T> {
var mock: T
func doRequest(_ completion: @escaping (T) -> Void) {
// ...
completion(mock)
}
}
我们可以构建一个RequestSideEffect<Message>
,它包含一个接收给定(Message) -> Void
回调的闭包,然后在捕获的Request<T>
实例上执行请求,通过{{1转发结果然后,其结果可以传递回回调(从而保持闭包中包含的类型变量(T) -> Message
'):
T
现在您的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)
}
}
可能如下所示:
SideEffect<Message>
您可以像这样实施enum SideEffect<Message> {
case sendRequest(RequestSideEffect<Message>)
}
:
State