我现在面临的问题是每当用户加载应用时。单例对象将运行
单身人士设计
import SocketIO
class SocketIOManager: NSObject {
static let sharedInstance = SocketIOManager()
var socket: SocketIOClient!
override init() {
socket = SocketIOClient(socketURL: URL(string: mainURL)!, .connectParams(["token": getToken()])])
super.init()
}
func establishConnection() {
socket.connect()
}
func closeConnection() {
socket.disconnect()
}
func getToken() -> String {
if let token = keychain["token"] {
return token
}
return ""
}
}
查看init()
和.connectParams
,为了让用户连接到服务器,必须存在令牌,因此传递getToken()
。
如果令牌不存在,它将在没有令牌的情况下初始化套接字对象。我在establishConnection
applicationDidBecomeActive
func applicationDidBecomeActive(_ application: UIApplication) {
SocketIOManager.sharedInstance.establishConnection()
}
只有在用户登录后才会出现令牌。
主要问题是,有没有办法重新初始化套接字对象?或者我使用didSet
或willSet
方法?
答案 0 :(得分:1)
也许是这样的?
var socket: SocketIOClient! {
didSet {
oldValue.closeConnection()
}
}
如果你愿意的话,你似乎也可以摆脱!
,因为你在init
中设置它,假设SocketIOClient.init
返回一个非可选实例。
答案 1 :(得分:0)
要做到这一点的一种方法是在SocketIOManager
中创建一个公共方法,并使用该方法初始化套接字:
func initializeSocket() {
socket = SocketIOClient(socketURL: URL(string: mainURL)!, .connectParams(["token": getToken()])])
}
在用户登录后调用此方法。
但顺便说一句,你的初始化程序必须是私有才能正确实现Singleton设计模式。
另一个注意事项是Swift中静态变量的初始化是懒惰的,这意味着它们只在第一次使用时才被初始化。查看此answer和Swift documentation on this topic以获取更多信息
答案 2 :(得分:0)
这很简单,你只需要在你的类中声明一个方法:
func resetConnection() {
socket.disconnect()
socket = SocketIOClient(socketURL: URL(string: mainURL)!, .connectParams(["token": getToken()])])
socket.connect()
}
并在以下
中使用SocketIOManager.sharedInstance.resetConnection()
let socket =
SocketIOManager.sharedInstance.socket // this will be the newer
答案 3 :(得分:0)
首先,你是从AppDelegate调用这个流程,这是你依赖这个令牌存在的问题。所以我在这里跳出来的是你错过了一个方法,在启动连接之前检查这个令牌是否真的存在,如果你不能生成令牌,该方法应该完全放弃连接套接字(也就是说,如果您的连接实际上是令牌依赖的,如果不是,那么之前的答案应该可以帮助您。)
由于您在管理器类的init覆盖范围内初始化套接字是正确的,因此它违背了我认为您想要的内容,即一旦令牌成为存在,就会重置连接。最初不在那里。为此,如上所述,你应该停止创建套接字。
我通常为单身人士做的事情:我给他们一个空白"配置"方法,将其提交到内存,通常在AppDelegate的establishConnection
上。如果此方法包含任何内容,则会检查单例依赖的任何值的方法,并根据这些值(如某些枚举情况)为单例分配自定义内部状态。然后我会像你在这里一样调用establishConnection,但appDidEnterForeground
应该是一个可以在每个import SocketIO
enum SocketIOManagerState {
case invalidURL
case launched
case tokenNotPresent
case manuallyDisconnected
case backgroundedByOS
}
class SocketIOManager: NSObject {
private var state : SocketIOManagerState = SocketIOManagerState.launched
private var staticSocketURL : URL?
static let sharedInstance = SocketIOManager()
var socket: SocketIOClient?
override init() {
super.init()
}
func configure() {
//fetch the url string from wherever and apply it to staticSocketURL
guard let url = URL(string: "The URL from wherever") else {
state = SocketIOManagerState.invalidURL
return
}
if getToken() == nil {
state = .tokenNotPresent
} else {
//only here can we be sure the socket doesn't have any restrictions to connection
staticSocketURL = url
state = SocketIOManagerState.launched
}
}
func evaluateConnection() {
guard let token = getToken() else {
//maybe something went wrong, so make sure the state is updated
if socket != nil {
return evaluateSocketAsNotNil()
}
return closeConnection(true, .tokenNotPresent)
}
switch state {
case .tokenNotPresent, .invalidURL:
closeConnection(true)
break
case .launched:
//means token was present, so attempt a connection
guard socket == nil else {
evaluateSocketAsNotNil()
return
}
guard let url = staticSocketURL else {
//maybe something went wrong with the url? so make sure the state is updated.
if socket != nil {
return closeConnection(true, .invalidURL)
}
return setState(.invalidURL)
}
if socket == nil {
socket = SocketIOClient(socketURL: url, .connectParams(["token": token]))
}
socket?.connect()
default:
//unless you care about the other cases, i find they all fall back on the same logic : we already checked if the token is there, if we get here, it means it is, so should we reconnect?
guard weCanReconnect /*some param or method which you create to determine if you should*/ else {
//you determine you should not, so do nothing
return
}
//you determine you do, so:
}
}
private func evaluateSocketAsNotNil() {
guard let sock = socket else { return }
switch sock.state {
case .notConnected:
//evaluate if it should be connected
establishConnection()
case .disconnected:
evaluateSocketAsNotNil()
case .connecting:
//do nothing perhaps?
case connected:
guard getToken() != nil else {
//token is not present, but the socket is initialized, this can't happen so disconnect and reset the instance
closeConnection(true, .tokenNotPresent)
return
}
break //nothing to do here
}
}
private func establishConnection() {
guard let sock = socket else { return }
sock.connect()
}
func setState(_ to: SocketIOManagerState) {
self.state = to
}
func closeConnection(_ clearMemory: Bool) {
guard let sock = socket else { return }
sock.disconnect()
setState(.launched)
if clearMemory {
socket = nil
}
}
private func closeConnection(_ clearMemory: Bool,_ to: SocketIOManagerState) {
socket?.disconnect()
setState(to)
if clearMemory {
socket = nil
}
}
func getToken() -> String? {
guard let token = keychain["token"] else {
state = .tokenNotPresent
return nil }
return token
}
}
方法运行的泛型方法,但不必担心改变事情,它应该重新建立事物当你的应用程序被落后时被删除了。
所以我建议你改变你的课程:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
SocketIOManager.sharedInstance.configure()
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
SocketIOManager.sharedInstance.closeConnection(false, .backgroundedByOS)
}
func applicationDidBecomeActive(_ application: UIApplication) {
// 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.
SocketIOManager.sharedInstance.evaluateConnection()
}
然后你的AppDelegate看起来像这样:
evaluateConnection()
从这里开始,您随时可以在应用中的其他任何位置调用closeConnection(_:, _:)
和evaluateConnection
,并添加更多状态案例,以及更多以逻辑方式处理这些案例的方法。无论哪种方式,您都可以根据令牌确定应该如何连接和重新连接。
使用此结构,如果您的用户登录,并且您在应用中正确设置了令牌,那么您应该能够在登录过程中调用map
时正确连接套接字。
还有很多评论,有些事情似乎是通用的(道歉),但是由您来填写用例的空白。
希望它有所帮助!