swift单例中调用异步函数的正确方法

swift单例中调用异步函数的正确方法,swift,asynchronous,Swift,Asynchronous,我想为我的应用程序构建一个分析类,我正在使用singleton 如果我运行它,tagEvent函数将立即运行,而不是首先运行openSession(),因此sessionId返回nil。 如何创建这样一个具有适当初始化的类,并像单例实例一样在应用程序范围内使用它 分析。swift final class Analytics { public static let instance = Analytics() private var baseUrl = "http://localh

我想为我的应用程序构建一个分析类,我正在使用singleton

如果我运行它,tagEvent函数将立即运行,而不是首先运行openSession(),因此sessionId返回nil。 如何创建这样一个具有适当初始化的类,并像单例实例一样在应用程序范围内使用它

分析。swift

final class Analytics {
    public static let instance = Analytics()
    private var baseUrl = "http://localhost"
    public var deviceId: String?

    private init(){
       self.deviceId = SomeFunctionGetsDeviceID()
   }

   func openSession(){
       // make an API call to create a session and save sessionId to UserDefaults
       if let url = URL(string: self.baseUrl + "/session"){
           let params:[String:Any] = ["deviceId": "\(self.deviceId!)"]

           var request = URLRequest(url: url)
           request.setValue("application/json", forHTTPHeaderField:"Content-Type")
           request.httpMethod = "POST"
           request.httpBody = try! JSONSerialization.data(withJSONObject: params, options: [])

           AnalyticsSessionManager.sharedManager.request(request as URLRequestConvertible).validate().responseObject(completionHandler: { (response: DataResponse<SessionOpenResponse>) in
               if response.result.value != nil {
                   UserDefaults.standard.set(response.result.value?.sessionId, forKey: "sessionId")
               }
           })
       }
   }

   func closeSession(){
      // make an API call to close a session and delete sessionId from UserDefaults
     ...
   }

   func tagEvent(eventName: String, attributes: [String : String]? = nil) {
        if let url = URL(string: self.baseUrl + "/event"),
            let sessionId = UserDefaults.standard.string(forKey: "sessionId"){
            ...
            // make an API call to create an event with that sessionId
        }
    }
}

我最好的猜测是openSession函数是异步工作的,而tagEvent调用是在异步代码完成之前传入的。有两种方法可以解决这个问题:

1) 添加同步,使tagEvent代码将等待openSession调用完成(如果正在进行)。如果没有进行,也许它应该自动调用openSession,等待完成,然后在该函数中执行代码

2) 从openSession添加一个完成处理程序,并在该存储模块内调用tagEvent,例如:

func openSession(completionHandler: @escapaing (Bool) -> ()){
    // make an API call to create a session and save sessionId to UserDefaults
    ...
    UserDefaults.standard.set(someSessionID, forKey: "sessionId")

    // when done with async work
    completionHandler(true)
}
然后在您的应用程序代理中:

Analytics.instance.openSession() { (success)
    Analytics.instance.tagEvent(eventName: "App Launch", attributes:["userID":"1234"])
}
3) *这是我修复它的方法*我不会在类外调用openSession。我将向Analytics类添加一个标志:

private var initialized = false
在openSession函数中,在完成所有操作后设置此选项

initialized = true
在tagEvent函数中:

func tagEvent(eventName: String, attributes: [String : String]? = nil) {
    // Check for initialization
    if (!initialized) {
        openSession() { (success) in
            // perform your tagEvent code
        })
    } else {
        if let url = URL(string: self.baseUrl + "/event"),
            let sessionId = UserDefaults.standard.string(forKey: "sessionId"){
            ...
            // make an API call to create an event with that sessionId
        }
    }
}
您可以实现一个“内部”类来提供对分析类的静态引用,如下所示(注意:删除
final
关键字):

在任何其他类中从分析类调用下一个方法,如下所示:

Analytics.sharedInstance.openSession()
Analytics.sharedInstance.closeSession()
Analytics.sharedInstance.tagEvent( ... )

openSession
肯定是在
tagEvent
之前调用的。最可能的问题是,
openSession
会立即返回,因为它进行了异步调用。“进行API调用”实际上是什么样子的?我编辑了“进行API调用”部分@rmaddyFYI-您的问题与该类是单例无关。这是对异步调用工作原理的误解。@rmaddy感谢您的通知。@rmaddy是绝对正确的。这是一个异步问题,不是单例问题。谢谢你的回答。但我想在AppDelegate中调用openSession一次,并在我想在应用程序范围内使用的任何地方调用tagEvent。在你的情况下,当我想标记一个事件时,它不是每次都会打开一个会话吗?选中我添加的第三个选项。你可以简化你的代码,但希望这表达了一般的想法。但是如果需要的话,我必须调用openSession或closeSession,我需要一个应用程序范围的会话,通过调用openSession并将会话ID存储到UserDefaults中来保持应用程序范围内的“初始化”不是一回事吗,您将同步逻辑隐藏在类内部(在tagEvent中),它将在执行tagEvent代码之前等待openSession完成。如果已经初始化,它将绕过此逻辑,直接执行tagEvent的代码。出现问题的原因是您没有在类内(或类外)构建同步。您必须以这样或那样的方式解决这个问题。感谢您的回答,但结果是相同的,tagEvent函数运行时不会等待openSession函数,openSession函数正在进行异步API调用以创建会话
class Analytics {

// properties ...

    class var sharedInstance: Analytics {
        struct Static {
            static let instance: Analytics = Analytics()
        }
        return Static.instance
    }

// other methods (init(), openSession(), closeSession(), tagEvent())

}
Analytics.sharedInstance.openSession()
Analytics.sharedInstance.closeSession()
Analytics.sharedInstance.tagEvent( ... )