在swift中存储临时数据的最佳实践

时间:2017-08-14 06:09:17

标签: ios swift core-data swift3 static

我开发了一个应用程序,可以从服务器获取大量JSON数据并在不同的VC上显示。我所做的是获取数据和将数据设置为静态变量,所以我可以在我的所有应用中访问数据。

现在我注意到我可以使用 CoreData 或者 UserDefaults 而不是 Static variables

我的问题是"此类应用的最佳做法是什么?为什么要?"

注意:该应用不需要脱机工作并保存数据以便在离线模式下显示。

5 个答案:

答案 0 :(得分:3)

您不应将数据存储到UserDefaults,用户默认值只是一个基于键值的文件,主要用于存储有关用户的一些数据,例如:

doNotShowAdds : true
isUserLogedIn :true.

你不应该使用钥匙串,因为钥匙串是加密容器,你存储有关用户的批评数据,例如:密码。

您不需要使用数据库

我的观点是你不应该使用全局变量,主要是我尝试使viewController数据特定于自身,不应该从项目的任何地方访问数据。 When to use global variables in Swift

大多数情况下,我使用特定视图控制器的实现来制作服务协议。 无论如何,每个控制器都应该有自己的数据部分,例如

class MyViewControllerOne {
    private var data :Array<DataExampleOne>;
}

class MyViewControllerTwo {
    private var data :Array<DataExampleTwo>;
}

从API加载数据后,数据就会存在,直到您关闭应用为止。

您可以随时创建另一个包含此数据的类,也可以创建更清晰的谓词:

protocol MyViewControllerOneService {
    func search() -> Array<DataExampleOne>;
}

class MyViewControllerOneServiceImpl :MyViewControllerService {
public var data :Array<DataExampleOne>;

public func search(searchString :String) -> Array<DataExampleOne>{
    let predicate = NSPredicate() ...
    let filteredArray = self.data.filter { evaluate...}
    return filteredArray;
    }
}

我使用类似的方法,而不是在我的服务类(业务逻辑)中保存数据,我得到了用于从数据库中获取数据的存储库。由于你没有任何数据库,这种方法是可以的。

然后代替

class MyViewControllerOne {
    private var data :Array<DataExampleOne>;
}

你可以使用

class MyViewController {
    private var myViewControllerService :MyViewControllerOneServiceImpl;
}

希望它有所帮助, 最好的问候!

答案 1 :(得分:2)

<强>讨论:

“此类应用的最佳做法是什么?为什么?”是一个非常好的问题。如果您描述了正在开发的应用程序类型,那将会有所帮助。

由于您在应用程序运行时似乎只需要/想要数据,所以我想最好的方法是将它保存在您已经在做的某些单例或静态变量的内存中。

但是,如果您在重新启动应用后没有想要存储您的数据,那么您可能希望您的数据在一段时间后过期,因为应用可能会在很长一段时间内打开。

然后问题是您希望如何在运行时接收数据。假设您已缓存数据,并且您仍希望刷新数据。然后,您可以设计数据管理,以便可以多次调用获取项目的闭包。假设有这样的事情:

    func fetchUsers(callback: ((_ users: [Any]?, _ isFromCache: Bool) -> Void)) {
        if let cachedUsers = MyCache.fetch(.users) {
            callback(cachedUsers, true)
        }

        APIManager.fetch(.users, { users, error in
            if let users = users {
                MyCache.insert(.users, items: users)
            }
            callback(users, false)
        })
    }

然后假设您想要在某些表视图中显示这些用户,您可以执行以下操作:

    func refreshData() {
        self.startActivityIndicator() // Start some minor activity indicator
        fetchUsers { (users, isFromCache) in
            self.users = users // Assign users whatever they are
            if users == nil {
                // Show error here
            }

            if  isFromCache == false {
                self.stopActivityIndicator() // Only stop activity when fresh data were received so user know he may expect the UI to change
            }

            tableView.reloadData()
        }
    }

这将设计数据提取的用户界面部分,但与数据的存储位置和方式无关。例如,MyCache仍然应该选择天气,缓存的响应仍然有效或过时。而MyCache决定将数据存储到某个数据库中或仅存储在内存中。

一些存储过程:

要将设计保留在以前的代码段中,您可能会有一个静态类MyCache,它具有存储类型的字符串枚举。然后它有一个静态字典[String: Any],它包含缓存数据,因此fetch方法看起来像

func fetch(type: FetchType) {
    return cacheDictionary[type]
}

插入非常相似。

要添加日期(或其中的任何其他数据),您可以将其定义为[String: [String: Any]]并使用静态密钥,以便您的数据为cacheDictionary[type]["date"] as? Date,数据为cacheDictionary[type]["data"]

然后,您可以将缓存扩展为例如使用用户默认值,如:

func fetch(type: FetchType) {
    if let inMemory = cacheDictionary[type] {
        return inMemory
    } else if let inDefaults = UserDefaults.standard.object(forKey: type) {
        cacheDictionary[type] = inDefaults // Keep it in memory for net time
        return inDefaults
    } else {
        return nil
    }    
}

这用于存储直接JSON数据或对象。可以将相同的内容应用于将数据(使用JSONSerializer序列化)存储到文件中。您可以在临时目录或库中创建基本路径,然后使用type之前的文件名。临时目录可能会被您可能需要的系统清除,并且库目录将是持久的。

然后是数据库:

数据库是另一个故事。将数据库应用到与上面相同的过程中是没有意义的,因为它是一种过度杀伤并且您根本不使用数据库的任何好处。

对于数据库,您可以为每个响应创建模型,并将数据插入数据库,然后通知UI控制器进行更新。这意味着回调不是最好的做法,而是代表。例如,DatabaseManager就会出现,你会看到DatabaseManager.shared.delegate = self然后Users.fetchNewUsers(),并等待委托触发。然后在委托上,您将从数据库中获取对象或创建一个获取结果控制器...

可能还有很多其他方法,但我希望这会让您了解如何在使用时存储数据。

答案 2 :(得分:2)

基本上我流了三个选项:

全局变量: 我需要定义Globally struct:

struct GlobalUser {
    static var name : String

}

现在我可以使用从项目中的任何位置调用全局变量:

GlobalUser.name = "Nazmul Hasan"

<强> UserDefaults

它用于数据存储但不是敏感数据:

      //key
       struct defaultsKeys {
        static var name = "name"   
       }

     //Setting 
        let defaults = UserDefaults.standard
        defaults.set("Nazmul", forKey: defaultsKeys.name)

    // Getting
    let defaults = UserDefaults.standard
    if let userName = defaults.string(forKey: defaultsKeys.name) {
        print(userName) // print what you store 
    }

<强>钥匙扣 它用于数据存储,但它是敏感数据,如用户名,密码或令牌。

Save and retrieve value via KeyChain swift

但我个人建议流动这个由apple发布的博客

Apple iOS Data Storage Guidelines

更感兴趣阅读这个问题主题:

How to save local data in a Swift app?

答案 3 :(得分:0)

您可以创建数据模型类来存储来自JSON响应的临时数据。

数据模型类的示例:-

class RootClass : NSObject, NSCoding{

    var array : [Int]!
    var booleanField : Bool!
    var custArray : [CustArray]!
    var nullField : AnyObject!
    var number : Int!
    var object : Object!
    var string : String!


    /**
     * Instantiate the instance using the passed dictionary values to set the properties values
     */
    init(fromDictionary dictionary: [String:Any]){
        booleanField = dictionary["Boolean"] as? Bool
        nullField = dictionary["Null"] as? AnyObject
        number = dictionary["Number"] as? Int
        string = dictionary["String"] as? String
        if let objectData = dictionary["Object"] as? [String:Any]{
            object = Object(fromDictionary: objectData)
        }
        custArray = [CustArray]()
        if let custArrayArray = dictionary["CustArray"] as? [[String:Any]]{
            for dic in custArrayArray{
                let value = CustArray(fromDictionary: dic)
                custArray.append(value)
            }
        }
    }

    /**
     * Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
     */
    func toDictionary() -> [String:Any]
    {
        var dictionary = [String:Any]()
        if booleanField != nil{
            dictionary["Boolean"] = booleanField
        }
        if nullField != nil{
            dictionary["Null"] = nullField
        }
        if number != nil{
            dictionary["Number"] = number
        }
        if string != nil{
            dictionary["String"] = string
        }
        if object != nil{
            dictionary["object"] = object.toDictionary()
        }
        if custArray != nil{
            var dictionaryElements = [[String:Any]]()
            for custArrayElement in custArray {
                dictionaryElements.append(custArrayElement.toDictionary())
            }
            dictionary["custArray"] = dictionaryElements
        }
        return dictionary
    }

    /**
     * NSCoding required initializer.
     * Fills the data from the passed decoder
     */
    @objc required init(coder aDecoder: NSCoder)
    {
        array = aDecoder.decodeObject(forKey: "Array") as? [Int]
        booleanField = aDecoder.decodeObject(forKey: "Boolean") as? Bool
        custArray = aDecoder.decodeObject(forKey: "CustArray") as? [CustArray]
        nullField = aDecoder.decodeObject(forKey: "Null") as? AnyObject
        number = aDecoder.decodeObject(forKey: "Number") as? Int
        object = aDecoder.decodeObject(forKey: "Object") as? Object
        string = aDecoder.decodeObject(forKey: "String") as? String
    }

    /**
     * NSCoding required method.
     * Encodes mode properties into the decoder
     */
    @objc func encode(with aCoder: NSCoder)
    {
        if array != nil{
            aCoder.encode(array, forKey: "Array")
        }
        if booleanField != nil{
            aCoder.encode(booleanField, forKey: "Boolean")
        }
        if custArray != nil{
            aCoder.encode(custArray, forKey: "CustArray")
        }
        if nullField != nil{
            aCoder.encode(nullField, forKey: "Null")
        }
        if number != nil{
            aCoder.encode(number, forKey: "Number")
        }
        if object != nil{
            aCoder.encode(object, forKey: "Object")
        }
        if string != nil{
            aCoder.encode(string, forKey: "String")
        }
    }
}

创建数据模型类的简单方法:-jsoncafe.com

答案 4 :(得分:-2)

对于像key:value这样的“小”数据,最好使用UserDefaults。如果您有更多要存储的内容,请使用CoreDara,因为它基本上是一个SQLite数据库。