2017-08-17 130 views
1

我想爲我的應用程序構建一個分析類,並且我正在使用單例。在swift singleton中調用異步函數的正確方法

如果我運行這個,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"]) 
} 
+0

'openSession'肯定是之前'tagEvent'調用。最可能的問題是'openSession'會立即返回,因爲它會進行異步調用。 – rmaddy

+0

「進行API調用」實際上是什麼樣子? –

+0

我編輯了一個API調用部分@rmaddy – invincible

回答

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

然後在您的應用程序代理:

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

@rmaddy是絕對正確的。這是一個異步問題,而不是單例問題。 – Shawn

+0

感謝您的回答。但是我想在AppDelegate中調用openSession一次,然後在應用程序範圍內調用tagEvent。在你的情況下,每當我想標記事件時,它都不會打開一個會話? – invincible

+0

檢查我添加的第三個選項。你可以簡化你的代碼,但希望這表達了總體思路。 – Shawn

0

您可以實現一個「內部」類,爲您的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.sharedInstance.openSession() 
Analytics.sharedInstance.closeSession() 
Analytics.sharedInstance.tagEvent(...) 
+0

感謝您的回答,但結果相同,tagEvent函數運行時無需等待正在進行異步API調用的openSession函數來創建會話 – invincible

相關問題