xcode Swift:链接到异步函数并等待执行另一个

ncgqoxb0  于 2022-11-17  发布在  Swift
关注(0)|答案(1)|浏览(139)

我不太理解等待从任何异步调用(任何:network,timer,任何异步执行的调用,我必须等待执行另一个),并在不同的地方同步使用它(如何链接到操作。我看到使用flatmap的示例,他们引用了2个web调用。在这种情况下,我必须从web中检索数据(一个会话Id),并保存它以备将来使用(它持续一个小时)。我阅读了关于操作,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()
    }
}

第一次单击时,

第二次单击时,

已保存正确的项目。
如何等到从服务器检索到数据(在本例中为字符串)并保存后才能正确地从存储中获取它?
我真的不太明白合并的模式。
多谢了

gmol1639

gmol16391#

在我看来,如果您所需要的只是在异步请求完成时做一些事情,那么在这里使用合并并没有什么真实的的好处(-您可以只使用一个常规的旧回调:

func getSessionKey(completion: @escaping () -> 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属性)。
总而言之,这不是一个学习合并模式的好例子--使用回调即可。

相关问题