我不太了解等待从任何异步调用(任何:网络,计时器,任何异步执行的调用,而我不得不等待执行另一个调用)并在另一个同步中使用它的模式位置(如何链接到操作。我看到了使用Flatmap的示例,它们涉及2个Web调用。在这种情况下,我必须从Web上检索数据(会话ID),并将其保存以供进一步使用(持续一个小时)我读到了有关Operations,DispatchGroups的信息,但并没有让他们在这里工作。 我有一个简单的类,它从Web服务获取数据,我必须等到其下载并保存。
import Foundation
import Combine
import CoreData
struct SessionId:Codable {
let key:String?
let dateTime:Date?
}
class ColppyModel {
var session:SessionId?
var key:String?
var cancellable:AnyCancellable?
init() {
print("saving")
let sess = SessionId(key: "1", dateTime: DateComponents(calendar:Calendar(identifier: .gregorian), year:2020, month:1, day:1).date)
guard let data = try? JSONEncoder().encode(sess) else {return}
let defaults = UserDefaults.standard
defaults.set(data, forKey: "sessionIdKey")
print("Saved \(sess)")
}
func getSessionKey(){
let requestData = ColppyAPIRequests.createSessionIdRequestData()
cancellable = ColppyAPI.sessionKeyRequest(sessionKeyJsonData: requestData)
.replaceError(with: nil)
.map{$0?.response?.data?.claveSesion}
.receive(on: DispatchQueue.global(qos: .userInteractive))
.sink(receiveValue: { (clave) in
let data = try! JSONEncoder().encode(SessionId(key: clave!, dateTime: Date()))
UserDefaults.standard.set(data, forKey: "sessionIdKey")
})
}
func getSessionIDFromUserDefaults() -> SessionId? {
let defaults = UserDefaults.standard
let data = defaults.data(forKey: "sessionIdKey")
guard let safeData = data else { return nil }
guard let sessionId = try? JSONDecoder().decode(SessionId.self, from: safeData) else {return nil}
return sessionId
}
}
我以这种方式在SwiftUI View中使用它
import SwiftUI
struct ContentView: View {
let ss = ColppyModel()
var body: some View {
Text("Press")
.onTapGesture {
self.getInvoices()
}
}
private func getInvoices() {
let id = ss.getSessionIDFromUserDefaults()
print(id)
ss.getSessionKey()
print(ss.getSessionIDFromUserDefaults())
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
return ContentView()
}
}
我第一次单击时得到
第二次单击我得到
正确的项目已保存。
我该如何等待直到从服务器中检索到数据(在这种情况下为字符串)并保存以正确从商店中获取数据?
真的,我不太了解组合模式。
非常感谢
答案 0 :(得分:1)
如果您只需要在此异步请求完成后执行某些操作,则在此处使用合并并没有真正的好处(在我看来)-您可以仅使用常规的旧回调:
func getSessionKey(@escaping completion: () -> Void) {
// ...
.sink(receiveValue: { (clave) in
let data = try! JSONEncoder().encode(SessionId(key: clave!, dateTime: Date()))
UserDefaults.standard.set(data, forKey: "sessionIdKey")
completion()
})
}
(我只是复制了您的代码,但我不鼓励使用try!
)
然后,您可以(使用结尾的闭包语法)
ss.getSessionKey() {
print(ss.getSessionIDFromUserDefaults())
}
如果您坚持使用合并,则getSessionKey
需要返回发布者,而不是sink
设置值。假设发布者发出一个Void
值来表示完成:
func getSessionKey() -> AnyPublisher<Void, Never> {
// ...
return ColppyAPI
.sessionKeyRequest(sessionKeyJsonData: requestData)
.map { $0.response?.data?.claveSession }
.replaceError(with: nil)
// ignore nil values
.compactMap { $0 }
// handle side-effects
.handleEvents(receiveOutput: {
let data = try! JSONEncoder().encode(SessionId(key: $0, dateTime: Date()))
UserDefaults.standard.set(data, forKey: "sessionIdKey")
})
}
这现在返回一个发布者,您可以在其他地方订阅该发布者(并在其中存储可取消的内容):
ss.getSessionKey()
.receive(on: ...)
.sink {
print(ss.getSessionIDFromUserDefaults())
}
.store(in: &cancellables)
当然,现在您需要弄清楚可取消对象的存储位置,如何在不可变的视图中实现此目的不是立即显而易见的(您需要将其设置为@State
属性)。 / p>
总而言之,这不是学习合并模式的好例子-仅使用caallbacks。