我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

使用属性封装器来完美创建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的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

这篇文章主要为大家介绍了iOS数据持久化UserDefaults封装器使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-03

iOS数据持久化KeyChain数据操作详解

这篇文章主要为大家介绍了iOS数据持久化KeyChain,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-03

详解SpringBoot中使用JPA作为数据持久化框架

这篇文章主要介绍了SpringBoot中使用JPA作为数据持久化框架的相关知识,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-03-19

Redis数据持久化:RDB与AOF详解及数据恢复应用

Redis的RDB和AOF持久化机制各有特点和用途。RDB适用于对性能要求高、数据恢复精度要求不高的场景,而AOF则适用于数据一致性要求较高的场景。
RedisAOFRDB2024-11-29

数据持久化的利器,Python中的Pickle模块详解

Pickle是Python标准库中的一个模块,用于将Python对象序列化(Pickling)为二进制数据,以及从二进制数据反序列化(Unpickling)为Python对象。这个模块对于在不同的Python程序之间传递数据或将数据存储到文

docker<容器数据卷-v>对容器内数据持久化详解(备份)

容器的数据持久化主要是指宿主机与容器,以及容器与容器之间进行数据交互,下面这篇文章主要给大家介绍了关于docker<容器数据卷-v>对容器内数据持久化的相关资料,需要的朋友可以参考下
2023-03-10

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录