Swift Tips 016 - Avoiding mocking UserDefaults

每天了解一点不一样的 Swift 小知识

代码截图

小笔记

这段代码在说什么

代码截图里是一个关于模拟登录的单测用例,在这个测试用例中,测试的结果是与用户的 UserDefaults 设定相关联,所以需要在 setup() 方法中设置好初始状态。

我们可以看到代码里根据 #file(包含这个符号的文件的路径)创建了一个 UserDefaults 对象,并清除了 key 值为 #file 的 value,由此保证不会存有其他值。

在完成上述初始操作后,使用 userDefaults 对象初始化了与登录相关的控制类 LoginManager,并将其赋值给 manager 属性,以便后续使用。

一些疑惑

初看这段代码,你可能会疑惑 UserDefaults(suiteName: #file) 这段代码到底在干什么,为了展开这个话题,我们需要先解释 2 个问题:

  • App Groups 是什么东西
  • UserDefaults(suiteName:) 的入参是什么东西

App Groups 是什么东西

App Groups 是 iOS 8 之后提供的新特性,主要用于同一个开发团队开发的 App 之间进行数据共享,这包括 App 和 Extension 之间共享同一份读写空间。同一个团队开发的多个应用之间如果能直接数据共享,将会大大提高用户体验。例如某公司的旗下有两个 App,当用户已经登录一个 App A 的情况下,再登录另一个 App B 时,B 不再需要繁琐的登录过程就可以直接使用 A 已经登录的信息。

另外 iOS 8 提供 Extension 功能之后,App Group 就显得尤为必要,例如 Containning app 没有运行,Extension 运行的情况下,Extension 和 Containning app 共享信息将是一个十分常见的需求。

关于如何开启和使用 App Groups 可以阅读文章:Shared User Defaults in iOS

UserDefaults(suiteName:) 的入参是什么东西

在 iOS 中,每个应用必须被沙盒化的,这就意味着应用之间是被隔离的,不能直接互相访问。而 UserDefaults 作为一种数据持久化解决方案,是能结合 App Groups 做到跨 App 之间访问的,那么它是怎么做到的呢?

这就要提到 UserDefault 的 Domain 类型,主要有五种,但要做到跨 App 之间的数据通信,我们仅需要关注 Application Domain 这一层级。

在沙盒机制的加成下,我们应该很容易理解一点,那就是每个 App 里的 UserDefault 文件,它的 Application Domain 是不一样的。这些文件的作用域通常是通过它们项目的 Bundle Identifier,或者是 App Group 中约定的 Suite Name 来区分的。就像我们在 A 项目里调用 UserDefaults.standard 的时候是不会返回 B 项目里的的 UserDefault 文件。

这样一来,我们再来看 UserDefaults(suiteName:) 这个 API,它的入参其实就是用来区分 Application Domain 的,以便开发者取得对应的 UserDefaults。

关于 UserDefault 作用域的更多内容可以阅读文章:UserDefaults under the hood

这样做的好处是什么

笔者不才,我认为今天的例子里的唯一好处是,相比于调用 UserDefaults.defaultUserDefault(suiteName:) 避免了直接操作原始文件带来的烦恼。

除了这些,还有哪些好处,聪明的你愿意分享出来么?