在swift singleton中调用异步函数的正确方法

时间:2017-08-17 17:10:43

标签: swift asynchronous

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

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

Analytics.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
        }
    }
}

AppDelegate.swift

func application(_ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    Analytics.instance.openSession()
    Analytics.instance.tagEvent(eventName: "App Launch", attributes: 
    ["userID":"1234"])
} 

2 个答案:

答案 0 :(得分:1)

我最好的猜测是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)
}

然后在你的app delegate:

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

答案 1 :(得分:0)

您可以实施&#34;内部&#34; class,为您的Google Analytics类提供静态引用,如下所示(注意:删除final关键字):

class Analytics {

// properties ...

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

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

}

接下来,在您的任何其他类的Analytics类中调用方法,如下所示:

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