您的位置:首页 >要闻 >

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

2023-06-18 14:55:34    来源:脚本之家
目录
使用属性封装器来完美创建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一样。然而,当定义一个属性封装器的时候,必须要有一个wrapppedValuewrapppedValuegetset代码块就是拦截和执行你想要的操作的地方。在这个例子中,添加了打印状态的代码来输出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读取来更新wrappedValueget代码块

@propertyWrapper
struct Storage {
    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 {
    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的资料请关注脚本之家其它相关文章!

相关阅读

精彩放送

山东即墨:“稻田课堂”体验劳动艰辛

【环球新要闻】高温天公交“车医生”钻“烤箱”修车

汉字找茬王衙找出15个字通关答案一览

dlink官网(dplink)

朽木不可雕啥木可雕(朽木不可雕)

嫁日新娘官方旗舰店_嫁日新娘

焦点精选!节操去哪了 节操新闻

网易再爆猛料:《天下贰》复活之后,还将推出《天下4》!

逆周期调节力度加大 MLF降息“靴子落地”-环球快看

环球微资讯!“日照蓝莓”IP形象正式发布

逍遥战甲免费全文_逍遥战甲

世界播报:苏州推出旅游联票,可在24小时/48小时内畅玩苏州

今热点:无线中继器怎么设置连接 无线中继器怎么设置

东港区陈疃镇:一颗蓝莓撬动亿级产业链

退出源码,张一鸣玩创投有自己的小心思

杨钧涵四毛刘忻_杨钧涵

环球热点!洛阳市人才住房补贴申请表下载地址(洛阳市人才住房补贴申请表下载地址填写)

太史慈怎么死的啊 太史慈怎么死

白袷从披趁肉芝什么意思_白袷|全球新视野

全球快播:居间费用由谁承担(居间费用是什么意思)

美版“杨永信”:“囚禁、性侵、强迫打胎,我这都是为你好。”

坚持绿色发展,擦亮生态底色 让更多美丽乡村走向共富

封神榜的作者 封神榜的作者是谁

美股高开纳指涨0.55% 热门中概股活跃 速读

省生态文明教育实践基地发布 同里国家湿地公园上榜

当前快播:stop灯亮了是什么原因(变频器stop灯亮了是什么原因)

wh是什么单位等于多少毫安(wh是什么单位)

最新:鹏华价值精选

直击考点|l爱心送考,一家人穿着都很有特色

力诺特玻不超5亿可转债获深交所通过 民生证券建功|今日快讯

乘联会崔东树:年轻人撑不起中国车市,还要靠中老年人

秋天的传统节日是什么? 天天微头条

天天视讯!何氏眼科眼基因研究院落户海南博鳌乐城

环球关注:从“天宫”到“天宫Plus”,11年间中国“太空家园”有何升级?

今日热议:台陆军官校新版踢正步 与中式、苏式类似

万亿美元英伟达,再刷历史新高!

塔克拉玛干“对话”撒哈拉——中非携手防治荒漠化观察-天天快资讯

济南市莱芜区凤城街道石花园社区:优化营商环境 检查食品安全

“千万工程”调研行丨这里的农民生活为何如此惬意?——浙江嘉兴乡村走访见闻-独家

12306网页登录不了_12306网站登录不_世界微资讯

天天通讯!死尸(关于死尸介绍)

一往情深深几许深山夕照深秋雨是什么意思_一往情深深几许深山夕照深秋雨的意思

苏州东吴今日客场挑战东莞,俱乐部发布主场变更公告_视点

球队输了600场 乔丹赢了一辈子!他刚卖掉NBA球队 入账30亿刀 成本仅2.7亿

15岁军迷:勇气、无畏、珍和平

事事有回应 件件有落实 大鹏69名“助企专员”上岗_全球热文

张雨绮699元买不了袜子,我天天“996”守着死工资不敢裸辞-环球关注

怎样用普通面粉在烤箱里作蛋糕?

【时快讯】三峡能源:6月16日融券净卖出45.17万股,连续3日累计净卖出322.9万股

焦点精选!园林设计资质比较_园林设计资质

最新资讯:建设者匠心建造民生工程

“博士乘组”已开工!网友:这眼镜咋不飘呢? 全球即时

华硕笔记本怎么开启触控板快捷键_华硕笔记本怎么开启触控板 通讯

中国中车首台氢动力机车在大同下线

西部利得尊逸三年定期开放债券型证券投资基金分红公告|独家焦点

海博夜读丨月影流金,照着西湖|天天日报

吴淞创新城建设提速,百年纺织厂蝶变“时尚+科技”产业高地

前沿资讯!小舞妈妈留下两件遗物,一件送给了唐三,另一件终于揭晓

和林微纳(688661):6月16日北向资金增持1.22万股

全球微动态丨电动汽车生产商Nikola将裁员150人

拿下问界商标,华为要造一个汽车联盟? 世界视点

快资讯:安诚保险销售有限公司山西分公司

环球时讯:首发新能源轮胎PS71 锦湖加快中国本土化进程

国美福利待遇怎么样(国美待遇怎么样)-快资讯

热点评!青鸟寓意友情吗 青鸟的寓意

“第三支箭”实操落地!A股首单重组项目花落招商蛇口,涉及资金174亿元

五羊石像作者 五羊石像

天天热资讯!广西工学院吧(关于广西工学院吧的基本详情介绍)

56亿美元!沙特看中了高合汽车

魏秋月 袁灵犀 现身_袁灵犀简介

村村响_村村 天天观热点

江西五十铃汽车有限公司总装厂EOL线_关于江西五十铃汽车有限公司总装厂EOL线简述-全球球精选

世界观热点:热门车讯全新奥迪Q3跨界车将会国产 全方位官图赏析

每日动态!描写夏天的作文600字初中 描写夏天的作文

七年级下册数学书内容人教版 七年级下册数学书内容

小学一年级美丽的校园作文250字怎么写_小学一年级美丽的校园作文250字

联合国教科文组织将在6月底召开特别会议 决定美国能否重新加入-每日热闻

内蒙古自治区阿拉善盟:绿水青山织锦绣 转型升级满目新

2023年灵活就业最低档、60%档和100%档费用缴费能领多少养老金-全球即时

江苏戒毒机关与院校共同成立“点点微光”禁毒宣传青春联盟_聚看点

街道献爱心,安全知识讲座走进工地 看热讯

快乐“村超”点燃中国人激情的背后

西部利得基金QDII资格获批 当前速读

新股申购时间:下周有5只新股开启申购(6月19日-6月23日)

世界讯息:7分钟理财创始人兼CEO罗元裳:家庭资产配置也要有“前锋、后卫、守门员”!

能链智电一季度净收入增长150% 全年预估增长5-6倍 天天快讯

视点!适合聚会发的朋友圈文案

小米2s标准版和电信版有什么区别_小米2s电信版和标准版哪个好_全球新视野

这张“纯天然”蓝底证件照,火了!

科技鸡翅的家常做法?|世界观点

wifi开启wmm_开启wmm|环球快看点

全球视点!全球首个干细胞合成人类胚胎模型,会引发伦理危机吗?

2023天津国际养老服务业博览会16日开幕

世界新消息丨最新粽子专项抽检情况公布 不合格样品均为超范围使用食品添加剂

培育选拔千名以上领军人才 江苏人才工作将这样推进|环球热文

海航航空旗下乌鲁木齐航空“寻找飞行锦鲤”主题活动上线

世界新消息丨大宗交易:泰恩康成交207.22万元,折价21.86%(06-05)

重庆永川综保区封关运行 将助推渝西和川南地区开放

世界关注:可以找附近人的交友软件_可以搜附近人的交友软件有哪些简介介绍

回望来路⑭|赤土:夏与秋|当前热闻

2023佛山陈村九价HPV疫苗接种指南(时间+流程+费用)

纽卡斯尔联2023/24赛季训练服系列来袭~酷,...-全球热闻

当前快播:中庸好学近乎知力行近乎仁(好学近乎知 力行近乎仁 知耻近乎勇的意思)

冯绍峰带娃与美女赤脚踩水,主动贴肩像夫妻,4岁儿子认可新女友 当前讯息

安耐克C-85_1GB-世界关注

杭州发布今年前5月经济运行数据,全市工业投资增长超40% 环球视点

黑龙江省二级造价师报名条件(二级造价师报名条件) 天天热推荐

借5万块钱该去哪里借?-世界速递