提问者:小点点

正在保存对象以便在任何地方都可以访问


这里我有一个条件,假设一个用户通过调用API登录。并且响应包含用户的详细信息。我们是否可以将用户的详细信息保存为一个对象,并且可以全局访问?下面是我如何使用ObjectMapper调用模型类的api:

import ObjectMapper

class User: Mappable {

var id: Int?
var userId: Int?
var orgId: Int?
var type: Int?
var email: String?
var lang: String?
var nickname: String?
var status: Int?
var token: String?

required init(map: Map) {
    mapping(map: map)
}

func mapping(map: Map) {
    id <- map["id"]
    userId <- map["userId"]
    orgId <- map["orgId"]
    type <- map["type"]
    failedAttempt <- map["failedAttempt"]
    email <- map["email"]
    lang <- map["lang"]
    nickname <- map["nickname"]
    status <- map["status"]
    token <- map["token"]
}

}

从我的社交档案来看,

    func postLogin(params: [String: Any], controller: UIViewController, completion: @escaping (User) -> Void) {
    alamofire("/login", method: .post, token: false, params: params, controller: controller) { result in
        if let userDetails = Mapper<User>().map(JSON: result as! [String: Any]) {
            DispatchQueue.main.async {
                completion(userDetails)
            }
        }
    }
}

有些解决方案可能使用UserDefaults,但是使用9个UserDefaults来保存我们从这个响应中得到的9个键是不实际的。有什么建议的方法,我们可以这样做,当用户登录,我们保存这些细节作为一个对象全局,甚至当关闭应用程序后,细节没有重新设置?谢谢大家


共2个答案

匿名用户

我同意在userdefaults中保存9个单独的键是不实际的。但是为什么不将整个代码编码为JSON并保存生成的数据呢?

extension User: Codable { }

// Assuming user is an instance of User
guard let userJSON = try? JSONEncoder().encode(user) else {
    // handle encoding failure
}

let userDefaultsKeyForUser = "com.yourdomain.snazzyapp.userInfo"

UserDefaults.standard.set(userJSON, forKey: userDefaultsKeyForUser)

我认为Swift会自动为您的user类合成codable一致性,但您可能需要使其成为final。或者显式实现codable

取回它

guard let userJSON = UserDefaults.standard.data(forKey: userDefaultsKeyForUser) else {
    // Handle missing data (first run maybe?)
}

guard let user = try? JSONDecoder().decode(User.self, from: userJSON) else {
   // Handle decoding failure
} 

虽然符合codable是实现这一目标的首选方法,但这有时会给类带来问题,特别是在涉及继承的情况下。特别是codableencodabledecodable的组合。decodable有时是类的一个问题。这个问题与必需的init(from Decoder:Decoder)throws必须在类中直接声明而不是在扩展中声明有关,然后您可能会在对超级进行编码时遇到麻烦。如果您的类是基本的class,这应该不是问题。这主要是您从AppKit/UIKit类继承时的问题。

如果由于某种原因,user不符合codable,则可以改用nscoding。它的工作方式有点不同。

在最坏的情况下,您可以实现手动将每个属性显式编码/解码为数据的方法。您甚至可以将它们存储为二进制,但您可能需要对字符串进行存储字节计数,这样您就可以知道每个字符串的起点和终点,并且您还需要想出一个方案来指示它们在编码中何时为nil。它不像JSON那么灵活,这就是为什么它是最后的手段...虽然我会注意到它要快得多。这对user类没有多大关系,但我编写了一个神经网络库,希望在训练期间在检查点保存模型,这意味着对许多层非常大的矩阵进行编码。数以百万计的参数。使用我自己的二进制编码读写模型比让codable处理它快20倍左右,即使我使用codable将其保存为二进制plist。

第三种选择是重新处理user类。或者将其设置为结构,或者在内部使用结构存储其属性,并使用计算属性在中获取和设置它们。类的所有属性都已经符合codable,因此Swift完全可以将structcodable一致性与这些属性综合起来。如果您仍然将结构包装在中,那么您只需要一个初始值设定器,它接受数据并执行与上面所示相同的解码,以设置包装的结构和一个encode方法(甚至是计算的var),它对结构进行编码,并返回数据

我不认为你会需要这些替代的解决方案,但我提到它们只是为了以防万一我错了,并且因为它们对于将来的情况是有用的。

匿名用户

你必须问问自己,你想如何从应用程序中的不同地方使用编译器自动完成来访问这个对象。其次,物体的寿命是多少?在登录/注销、应用程序会话或应用程序生命周期期间,对象应该是可访问的?

让我们从对象寿命开始。

  • 登录/注销,对象可以存储在内存中
  • 应用程序会话,对象可以存储在内存中
  • 应用程序安装/删除时,对象应存储在UserDefaults、数据库或文件中。

自动完成驱动的开发也称为添加:

决定在何处存储userDetails对象以及如何访问它

假设您在视图控制器中有一个逻辑,它在ViewDidLoad中定义的初始配置中隐藏或取消隐藏视图。编写一些原型代码来决定您希望如何从应用程序的不同位置访问“用户详细信息”对象。

func viewDidLoad() {
   super.viewDidLoad()

   if userDetails.status {
     //userDetails is a global instance of a user object
   }

   if session.userDetails.status {
     //session is a global instance of a Session object which has a property which stores a User instance in userDetails
   }

   if Session.current.userDetails.status {
     // Session is a type and 'current' is a singleton of that type which has a userDetails property which stores an instance of User type
   }

   if User.userDetails.status {
     // User is your type which can have a static property 'userDetails' which stores a User instance
   }

}