当前位置: 代码网 > it编程>App开发>苹果IOS > iOS数据持久化UserDefaults封装器使用详解

iOS数据持久化UserDefaults封装器使用详解

2024年05月18日 苹果IOS 我要评论
使用属性封装器来完美创建userdefaults封装器想象一下,你有一个应用想实现自动登录功能。你用userdefaults封装了关于userdefaults的读与写逻辑。你会用userdefault

使用属性封装器来完美创建userdefaults封装器

想象一下,你有一个应用想实现自动登录功能。你用userdefaults封装了关于userdefaults的读与写逻辑。你会用userdefaults封装来保持对自动登录”on/off“状态、username的跟踪。你可能会以下面这种方式来封装userdefaults

struct appdata {
    private static let enableautologinkey = "enable_auto_login_key"
    private static let usernamekey = "username_key"
    static var enableautologin: bool {
        get {
            return userdefaults.standard.bool(forkey: enableautologinkey)
        }
        set {
            userdefaults.standard.set(newvalue, forkey: enableautologinkey)
        }
    }
    static var username: string {
        get {
            return userdefaults.standard.string 
        }
        set {
            userdefaults.standard.set(newvalueds, forkey: usernamekey)
        }
    }
}

通过swift5.1对于属性封装器的介绍,我们可以对上面的代码进行精简,如下

struct appdata {
    @storage(key: "enable_auto_login_key", defaultvalue: false)
    static var enableautologin: bool
    @storage(key: "username_key", defaultvalue: "")
    static var username: string
}

这样就很完美了吗?接着看

什么是属性封装器?

在我们进入详细讨论之前,我们先快速地了解一下什么是属性封装器 基本上来讲,属性封装器是一种通用数据结构,可以拦截属性的读写访问,从而允许在属性的读写期间添加自定义行为。

可以通过关键字@propertywrapper来声明一个属性封装器。你想要有一个字符串类型的属性,每当这个属性被进行读写操作的时候,控制台就会输出。你可以创建一个名为printable的属性封装器,如下:

@propertywrapper
struct printable {
    private var value: string = ""
    var wrappervalue: string {
        get {
            print("get value:\(value)")
            return value
        }
        set {
            print("set value:\(newvalue)")
            value = newvalue
        }
    }
}

通过上述代码我们可以看出,属性封装跟其他struct一样。然而,当定义一个属性封装器的时候,必须要有一个wrapppedvaluewrapppedvalue get set代码块就是拦截和执行你想要的操作的地方。在这个例子中,添加了打印状态的代码来输出get和set的值

接下来,我们看看,如何使用printable属性封装器

struct company {
    @printable static var name: string
}
company.name = "adidas"
company.name

需要注意的是,我们如何使用@符号来声明一个用属性封装器封装的”name“变量。如果你想要在playground中尝试敲出上述代码的话,你会看到以下输出:

set value: adidas
get value: adidas

什么是userdefault封装器

在理解了什么是属性封装器以及它是如何工作的之后,我们现在开始准备实现我们的userdefaults封装器。总结一下,我们的属性封装器需要持续跟踪自动登录的”on/off“状态以及用户的username。 通过使用我们上述讨论的概念,我们可以很轻松的将printable属性封装器转化为在读写操作期间进行读写的属性封装器。

import foundation
@propertywrapper
struct storage {
    private let key: string
    private let defaultvalue: string
    init(key: stirng, defaultvalue: string) {
        self.key = key
        self.defaultvalue = defaultvalue
    }
    var wrappedvalue: string {
        get {
            return userdefaults.standard.string(forkey: key) ?? defaultvalue
        }
        set {
            userdefaults.standard.set(newvalue, forkey: key)
        }
    }
}

在这里,我们将我们的属性封装器命名为storage。有两个属性,一个是key,一个是defaultvaluekey将作为userdefaults读写时的键,而defaultvalue则作为userdefaults无值时候的返回值。

storage属性封装器准备就绪后,我们就可以开始实现userdefaults封装器了。直截了当,我们只需要创建一个被storage属性封装器封装的‘username’变量。这里要注意的是,你可以通过keydefaultvalue来初始化storage

struct appdata {
    @storage(key: "username_key", defaultvalue: "")
    static var username: string
}

一切就绪之后,userdefaults封装器就可以使用了

appdata.username = "swift-senpai"
print(appdata.username)

同时,我们来添加enableautologin变量到我们的userdefaults封装器中

struct appdata {
    @storage(key: "username_key", defaultvalue: "")
    static var username: string
    @storage(key: "enable_auto_login_key", defaultvalue: false)
    static var username: bool
}

这个时候,会报下面两种错误:

cannot convert value of type ‘bool’ to expected argument type ‘string’

property type 'bool' does not match that of lthe 'wrappedvalue' property of its wrapper type 'storage'

这是因为我们的封装器目前只支持string类型。想要解决这两个错误,我们需要将我们的属性封装器进行通用化处理

将属性封装器进行通用化处理

我们必须改变属性封装器的wrappedvalue的数据类型来进行封装器的通用化处理,将string类型改成泛型t。进而,我们必须使用通用方式从userdefaults读取来更新wrappedvalue get代码块

@propertywrapper
struct storage<t> {
    private let key: string
    private let defaultvalue: t
    init(key: string, defaultvalue: t) {
        self.key = key
        self.defaultvalue = defaultvalue
    }
    var wrappedvalue: t {
        get {
            // read value from userdefaults
            return userdefaults.standard.object(forkey: key) as? t ?? defaultvalue
        }
        set {
            // set value to userdefaults
            userdefaults.standard.set(newvalue, forkey: key)
        }
    }
}

好,有了通用属性封装器之后,我们的userdefaults封装器就可以存储bool类型的数据了

// the userdefaults wrapper
struct appdata {
    @storage(key: "username_key", defaultvalue: "")
    static var username: string
    @storage(key: "enable_auto_login_key", defaultvalue: false)
    static var enableautologin: bool
}
appdata.enableautologin = true
print(appdata.enableautologin)  // true

存储自定义对象

上面的操作都是用来基本数据类型的。但是如果我们想要存储自定义对象呢?接下来我们一起看看,如何能让userdefaults支持自定义对象的存储

这里的内容很简单,我们将会存储一个自定义对象到userdefaults中,为了达到这个目的,我们必须改造一下storage属性封装器的类型t,使其遵循codable协议

然后,在wrappedvalue``set代码块中我们将使用jsonencoder把自定义对象转化为data,并将其写入userdefaults中。同时,在wrappedvalue``get代码块中,我们将使用jsondecoder把从userdefaults中读取的数据转化成对应的数据类型。 如下:

@propertywrapper
struct storage<t: codable> {
    private let key: string
    private let defaultvalue: t
    init(key: string, defaultvalue: t) {
        self.key = key
        self.defaultvalue = defaultvalue
    }
    var wrappedvalue: t {
        get {
            // read value from userdefaults
            guard let data = userdefaults.standard.object(forkey: key) as? data else {
                // return defaultvalue when no data in userdefaults
                return defaultvalue
            }
            // convert data to the desire data type
            let value = try? jsondecoder().decode(t.self, from: data)
            return value ?? defaultvalue
        }
        set {
            // convert newvalue to data
            let data = try? jsonencoder().encode(newvalue)
            // set value to userdefaults
            userdefaults.standard.set(data, forkey: key)
        }
    }
}

为了让大家看到如何使用更新后的storage属性封装器,我们来看一下接下来的例子。 想象一下,你需要存储用户登录成功后服务端返回的用户信息。首先,需要一个持有服务端返回的用户信息的struct。这个struct必须遵循codable协议,以至于他能被转化为data存储到userdefaults

struct user: codable {
    var firstname: string
    var lastname: string
    var lastlogin: date?
}

接下来,在userdefaults封装器中声明一个user对象

struct appdata {
    @storage(key: "username_key", defaultvalue: "")
    static var username: string
    @storage(key: "enable_auto_login_key", defaultvalue: false)
    static var enableautologin: bool
    // declare a user object
    @storage(key: "user_key", defaultvalue: user(firstname: "", lastname: "", lastlogin: nil))
    static var user: user
}

搞定了,userdefaults封装器现在可以存储自定义对象了

let johnwick = user(firstname: "john", lastname: "wick", lastlogin: date())
// set custom object to userdefaults wrapper
appdata.user = johnwick
print(appdata.user.firstname) // john
print(appdata.user.lastname) // wick
print(appdata.user.lastlogin!) // 2019-10-06 09:40:26 +0000

以上就是ios数据持久化userdefaults封装器使用详解的详细内容,更多关于ios数据持久化userdefaults的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com