【问题标题】:applicationDidBecomeActive runs after ViewController loadedapplicationDidBecomeActive 在 ViewController 加载后运行
【发布时间】:2018-04-29 22:15:30
【问题描述】:

我正在使用 socket.io 来使用实时机制。我现在面临的问题是加载 viewController 后创建的 Socket.io 对象

我正在使用单例设计

import SocketIO

class SocketIOManager: NSObject {

    static let sharedInstance = SocketIOManager()

    var socket: SocketIOClient!

    func establishConnection() {
       socket = SocketIOClient(socketURL: URL(string: mainURL)!, config: [.log(false), .compress, .connectParams(["token": "asdasdasdsa"])])
       socket.connect()

    }

    func closeConnection() {
        socket.disconnect()
    }

}

无论如何,我都需要在建立连接中设置套接字,以确保用户已与令牌一起建立与服务器的连接。如果我按照弗拉德的回答,将永远不会建立连接,因为令牌事先是空的,因此用户将无法连接。

用户登录

func logIn() {
   // Getting it from alamofire
   keychain["token"] = token
   //then only establish connection
   SocketIOManager.sharedInstance.establishConnection()
}

如果我使用 Init 方法,那么 socket 将为零,因为在 applicationDidBecomeActive 中有 SocketIOManager.sharedInstance.establishConnection(),所以逻辑是每当用户加载应用程序时,applicationDidBecomeActive 就会运行

所以从技术上讲,我使用 sharedInstance 来运行 socket.io 对象。

func applicationDidBecomeActive(_ application: UIApplication) {
        SocketIOManager.sharedInstance.establishConnection()
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

现在 didBecomeActive 中的套接字对象在视图加载后运行

override func viewDidLoad() {
    super.viewDidLoad()
    listeningOnMerchant()

}

func listeningOnMerchant() {
   // I got error around here
   SocketIOManager.sharedInstance.socket.on("listening") { ack, data in
      print("something")


   }
  }
}

这会崩溃,因为socket 为零。如何使 viewController 不崩溃。我可以使用let 方法来阻止崩溃

if let socket = SocketIOManager.sharedInstance.socket {

}

但如果我使用socket 并且套接字将不会运行,它将为零。如何不让socket不崩溃?

因为我真的需要将socket.connect 放在applicationDidBecomeActive 上以使其与连接与didLaunch 一致

谢谢!

【问题讨论】:

  • 如果你已经在使用单例,为什么不把套接字的初始化放在这个单例中呢?
  • 由于特定原因,初始化只能在establishConnection函数中调用。
  • 我已经编辑了我的问题
  • 不确定我是否直接理解了这个问题,但是为什么你不能在尝试调用.on之前初始化套接字,如果它是nil

标签: ios swift socket.io


【解决方案1】:

从技术上讲,要解决这个问题,您需要使用init 方法,这样套接字就不会为零。由于需要在应用暂停等情况下运行套接字,所以无论如何都需要使用 init 方法。

   private override init() {
        self.socket = SocketIOClient(socketURL: URL(string: mainURL)!, config: [.log(true), .compress, .forceNew(true), .connectParams(["token": getToken()])])
        super.init()
    }

添加forceNew(true),这样您就不会为一个用户创建多个连接

你需要做的下一步是创建另一个只有你的 loginViewController 可以访问的函数

 func establishConnectionWhenLogin(_ token: String) {
        self.socket =  SocketIOClient(socketURL: URL(string: mainURL)!, config: [.log(false), .compress, .connectParams(["token": token])])
        socket.connect()

    }

将令牌传递给参数,以便只有用户登录然后您要建立连接。如果您有任何问题,请告诉我

我将此解决方案用于我自己的应用程序,并且效果很好

【讨论】:

    【解决方案2】:

    据我了解,您的问题在于在 establishConnection 函数中设置 SocketIOClient。如果它以 nil 崩溃,可能你的构造函数在这里返回 nil:

    SocketIOClient(socketURL: URL(string: mainURL)!, config: [.log(false), .compress, .connectParams(["token": "asdasdasdsa"])])
    

    我看不出有理由在这个函数中这样做。很容易犯错误并在某些时候使 socket var 未初始化。我宁愿让它 let 并将其设置在构造函数中,如下所示:

    import SocketIO
    
    class SocketIOManager: NSObject {
    
        static let sharedInstance = SocketIOManager()
    
        let socket: SocketIOClient!
    
        init() {
            super.init()
            socket = YourFixedConstructorHere()
        }
    }
    

    如果你想确保它被初始化,你也可以使它成为惰性变量,像这样:

    lazy var socket = SocketIOClient(socketURL: URL(string: mainURL)!, config: [.log(false), .compress, .connectParams(["token": "asdasdasdsa"])])
    

    【讨论】:

    • 感谢弗拉德花时间回答我的问题
    • 您认为哪种方法适合我的用例?
    • 懒惰或使用 init() 谢谢!
    • @sinusGob,我会在 init 中初始化它。这是一种更安全的方式。如果 SocketIOClient 创建不便宜,可以使用惰性
    • 顺便说一句,我真的需要在建立连接上设置套接字,因为如果我不这样做,那么每当用户登录时,如果我使用你的方法,由于 getToken func 而创建连接跨度>
    【解决方案3】:

    我认为问题是时间问题。您应该在 ViewController 加载视图之前调用您的单例。所以你最好把这段代码放上去

    SocketIOManager.sharedInstance.establishConnection()
    

    进入 AppDelegate 中的函数(之前执行的)

        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    
             SocketIOManager.sharedInstance.establishConnection()
    
        return true
    }
    

    【讨论】:

    • 你说的是对的,但是如果应用程序暂停了怎么办,我肯定需要把它放在didApplicationBecomeActive我之前尝试过这个解决方案并且它有效,除非我将应用程序放在后台或如果应用程序暂停,那将是一个问题。
    • 我不能同时放入didBecomeActivedidFinishLaunching,这会为每个用户创建 2 个连接,这是不切实际的,如果我有超过 200 个用户,可能会导致很多问题.
    【解决方案4】:

    如果我正确理解您的问题,在某些时候您不能使用 establishConnection() 因为您缺少令牌。这种逻辑并不完全符合您的单例模式,因为您的对象的职责只是建立/取消连接,并且在创建实例后它已经无法做到这一点。我宁愿在这里使用服务,像这样:

    class SocketIOService {
        let token: String // I think if token is related to SocketIO, you can store it here
        init(token: String) {
            self.token = token
        }
        // Your code to establish connections, etc
    }
    

    这样您就可以保证,如果没有令牌,您的整个服务就无法创建。对我来说好多了

    单例模式的选项不太好,但它们确实存在。首先,您可以在方法中添加参数:

    func establishConnection(withToken token: String) {
        // code
    }
    

    使用此方法,您将需要传入令牌,因此没有它用户将无法建立连接。

    然后,无论你想在哪里使用它,你都可以这样做:

    if let token = // Your code to retrieve token {
        SocketIOManager.shared.establishConnection(withToken: token)
    }
    

    甚至:

    keychain["token"].flatMap {SocketIOManager.shared.establishConnection(withToken: $0)}
    

    很不错吧?

    还有其他几种方法可以做到这一点,但对于所有这些方法,您的工作流程几乎相同 - 首先您检查是否有令牌,然后调用此方法。如果您没有成功 - 检索令牌后,请重试。

    【讨论】:

    • 再次感谢弗拉德,我需要试试这个
    • 只是想知道这个解决方案仍然是单例设计吗?因为您正在创建一个新类 SocketIOService?
    • 对于 socket.io,最好创建一个单例设计,这样您就不会产生很多连接。这就是为什么我采用了单例设计
    • Twillio 人 twilio.com/blog/2016/09/… 有一个教程,如果你向下滚动,他们甚至使用这种单例设计
    • 告诉我你的想法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    • 2012-01-02
    • 2018-01-12
    • 2015-05-31
    • 2012-03-29
    • 1970-01-01
    相关资源
    最近更新 更多