付費限定

🔀UUID 的隨機特性很好,測試的困難就交給 Swift Dependencies

13-avatar-img
發佈於📦 推薦 Swift Package 個房間
更新 發佈閱讀 12 分鐘

上一篇 Swift Dependencies 文章以 Date 為範例,相信讀者已經體會到,即使是時間這麼基本的型別,也可以造成測試上的困難。而 Swift Dependencies 可以在測試環境下,替換掉當下的系統時間,使得測試時的行為能夠符合預期。

在這篇文章中,我們繼續延伸昨天的例子,加深印象的同時多介紹一點概念。

在一個筆記 app 中,指定 id 來存取特定一篇筆記,是很常見的需求。所以,我們打算把 Note 加上 id 欄位,並且使用 UUID 型別,以確保唯一性。

UUID 唯一性的用途

UUID 的特性就是在產生時會有唯一性、不會重複。這對於確保資料之間不會衝突,是非常好的特性。

但是,如果我們直接寫 let id = UUID(),就會跟上一篇的 Date() 發生一樣的問題──無法控制產生的結果。

struct Note {  // v5
let id = UUID() // 新增 id 欄位,隨機產生
let createdDate: Date
var modifiedDate: Date
var text: String = "" {
didSet {
@Dependency(\.date.now) var date
modifiedDate = date
}
}

init() {
@Dependency(\.date.now) var date
self.createdDate = date
self.modifiedDate = date
}
}

不控制 UUID 產生的結果,就不好測試

大部分情況,其實我們並不在意 UUID 的值,因為相信系統會做到產生唯一、不衝突的值,就夠了。

但假如我們把它當成某個關鍵邏輯的參考依據,那麼這種隨機特性,就會讓測試失敗。

舉例來說,我們會想要筆記有個穩定的排序規則,依照 modifiedDatetextid 這三個條件。理論上時間與內文是有可能重複的(比如我們做了複製同一則筆記的功能),所以最後就以 id 為依據。

我們可以幫 Note 簡單地實作 Comparable protocol。如果你不熟悉這個比較 tuple 的語法,簡單來說它會由左到右依序比對 modifiedDatetext、最後才是 id

extension Note: Comparable {
static func < (lhs: Self, rhs: Self) -> Bool {
(lhs.modifiedDate, lhs.text, lhs.id) < (rhs.modifiedDate, rhs.text, rhs.id)
}
}

但是在測試時,無法得到穩定的結果。

@Test(
"[non-deterministic] Ensure note default sorting order - will fail randomly"
)
func sortNotesWillFail() {
let notes = withDependencies {
$0.date.now = .init(timeIntervalSinceReferenceDate: 0)
} operation: {
[Note(), Note(), Note()]
}

let sorted = notes.sorted(by: <)

// UUID() 是隨機的,即使 `modifiedDate`、`text` 相同,也無法預測排序後的順序
#expect(notes == sorted)
#expect(notes[0] == sorted[0])
}
💡技術細節:
各種程式語言的 UUID 主要是參照 RFC 4122,並且有多種版本。

有幾個版本的 UUID,具備 time-ordered 的特性(例如 UUIDv7),產出的 id 直接當字串來排序,就具備時間遞增的效果。這類 UUID 能兼顧唯一性與遞增排序,特別適用於一些資料庫的應用。

而 Swift Foundation 的 UUID 是依照 RFC 4122 version 4 的規格產生的。它會參考當下時間產生,但是產出的隨機字串並沒有遞增特性。所以連續呼叫的 UUID() 的結果,不會是穩定的順序。

透過 Swift Dependencies 的 UUIDGenerator 來控制產生結果

就像 DateSwift Dependencies 也內建 UUID 的注入方式。方法是:@Dependency(\.uuid) var uuid

創作者正在準備中
請加入 13+ 了解最新動態!
留言
avatar-img
13+
3.2K會員
161內容數
13 以 10+ 年 iOS 開發經驗為基礎撰寫,助你在 AI 時代成為更有自信的技術工作者。 ❤️ 支持 13 創作! 🤖 AI 工具實戰經驗與深度思考 🧠 軟體開發思維、職涯發展建議 💡 實用技巧與踩坑經驗分享 😔 開發者身心健康與職業傷害
13+的其他內容
2025/09/07
Swift Dependencies 是我最常使用的套件。它讓外部條件變成完全可控,解決了測試困難和 SwiftUI Preview 編譯緩慢的問題。使用它以後,我在區分元件職責、設計測試的功力都大幅提升。
Thumbnail
2025/09/07
Swift Dependencies 是我最常使用的套件。它讓外部條件變成完全可控,解決了測試困難和 SwiftUI Preview 編譯緩慢的問題。使用它以後,我在區分元件職責、設計測試的功力都大幅提升。
Thumbnail
2025/08/28
下載官方 App、預習活動時間表與交通資訊、提前抵達,用愉快的心情享受兩天的活動!
Thumbnail
2025/08/28
下載官方 App、預習活動時間表與交通資訊、提前抵達,用愉快的心情享受兩天的活動!
Thumbnail
2025/08/27
即使在現實中不擅長聊天,也可以享受研討會的互動。稍微做點準備即可!不論社交能力如何,這篇文章談到的準備適用於任何人。
Thumbnail
2025/08/27
即使在現實中不擅長聊天,也可以享受研討會的互動。稍微做點準備即可!不論社交能力如何,這篇文章談到的準備適用於任何人。
Thumbnail
看更多
你可能也想看
Thumbnail
賽勒布倫尼科夫以流亡處境回望蘇聯電影導演帕拉贊諾夫的舞台作品,以十段寓言式殘篇,重新拼貼記憶、暴力與美學,並將審查、政治犯、戰爭陰影與「形式即政治」的劇場傳統推到台前。本文聚焦於《傳奇:帕拉贊諾夫的十段殘篇》的舞台美術、音樂與多重扮演策略,嘗試解析極權底下不可言說之事,將如何成為可被觀看的公共發聲。
Thumbnail
賽勒布倫尼科夫以流亡處境回望蘇聯電影導演帕拉贊諾夫的舞台作品,以十段寓言式殘篇,重新拼貼記憶、暴力與美學,並將審查、政治犯、戰爭陰影與「形式即政治」的劇場傳統推到台前。本文聚焦於《傳奇:帕拉贊諾夫的十段殘篇》的舞台美術、音樂與多重扮演策略,嘗試解析極權底下不可言說之事,將如何成為可被觀看的公共發聲。
Thumbnail
柏林劇團在 2026 北藝嚴選,再次帶來由布萊希特改編的經典劇目《三便士歌劇》(The Threepenny Opera),導演巴里・柯斯基以舞台結構與舞台調度,重新向「疏離」進行提問。本文將從觀眾慾望作為戲劇內核,藉由沉浸與疏離的辯證,解析此作如何再次照見觀眾自身的位置。
Thumbnail
柏林劇團在 2026 北藝嚴選,再次帶來由布萊希特改編的經典劇目《三便士歌劇》(The Threepenny Opera),導演巴里・柯斯基以舞台結構與舞台調度,重新向「疏離」進行提問。本文將從觀眾慾望作為戲劇內核,藉由沉浸與疏離的辯證,解析此作如何再次照見觀眾自身的位置。
Thumbnail
本文深入解析臺灣劇團「晃晃跨幅町」對易卜生經典劇作《海妲.蓋柏樂》的詮釋,從劇本歷史、聲響與舞臺設計,到演員的主體創作方法,探討此版本如何讓經典劇作在當代劇場語境下煥發新生,滿足現代觀眾的觀看慾望。
Thumbnail
本文深入解析臺灣劇團「晃晃跨幅町」對易卜生經典劇作《海妲.蓋柏樂》的詮釋,從劇本歷史、聲響與舞臺設計,到演員的主體創作方法,探討此版本如何讓經典劇作在當代劇場語境下煥發新生,滿足現代觀眾的觀看慾望。
Thumbnail
《轉轉生》為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,融合舞蹈、音樂、時尚和視覺藝術,透過身體、服裝與群舞結構,回應殖民歷史、城市經驗與祖靈記憶的交錯。本文將從服裝設計、身體語彙與「輪迴」的「誕生—死亡—重生」結構出發,分析《轉轉生》如何以當代目光,形塑去殖民視角的奈及利亞歷史。
Thumbnail
《轉轉生》為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,融合舞蹈、音樂、時尚和視覺藝術,透過身體、服裝與群舞結構,回應殖民歷史、城市經驗與祖靈記憶的交錯。本文將從服裝設計、身體語彙與「輪迴」的「誕生—死亡—重生」結構出發,分析《轉轉生》如何以當代目光,形塑去殖民視角的奈及利亞歷史。
Thumbnail
這篇文章探討瞭如何在iOS應用程式中客製化Alert,包括改變字體大小、內嵌連結以及讓Alert的高度隨著字數增長並提供scroll操作。同時使用SwiftUI進行客製化,並介紹瞭解決高度超出範圍後文字捲動與scrollView固定高度的方法。
Thumbnail
這篇文章探討瞭如何在iOS應用程式中客製化Alert,包括改變字體大小、內嵌連結以及讓Alert的高度隨著字數增長並提供scroll操作。同時使用SwiftUI進行客製化,並介紹瞭解決高度超出範圍後文字捲動與scrollView固定高度的方法。
Thumbnail
本文介紹瞭如何在SwiftUI中調整元件的對齊方式,包括置中、向左/向右/向上/向下對齊的方法。透過調整HStack、VStack以及frame的maxWidth、maxHeight和alignment屬性,可以達到想要的對齊效果。
Thumbnail
本文介紹瞭如何在SwiftUI中調整元件的對齊方式,包括置中、向左/向右/向上/向下對齊的方法。透過調整HStack、VStack以及frame的maxWidth、maxHeight和alignment屬性,可以達到想要的對齊效果。
Thumbnail
本文檔介紹了在Swift中使用套件的詳細方法,包括如何引用第三方套件和自定義模組,如何創建自定義套件,以及一些常見的Swift套件。這些套件可以幫助開發者快速添加功能到項目中,提高開發效率和程式碼品質。
Thumbnail
本文檔介紹了在Swift中使用套件的詳細方法,包括如何引用第三方套件和自定義模組,如何創建自定義套件,以及一些常見的Swift套件。這些套件可以幫助開發者快速添加功能到項目中,提高開發效率和程式碼品質。
Thumbnail
本章節介紹了如何建立並設置Swift項目以及如何選擇和設置Swift代碼編輯器。這包括在Xcode和命令行中建立Swift項目,選擇Xcode、Visual Studio Code或AppCode作為編輯器,以及如何使用SPM安裝插件。
Thumbnail
本章節介紹了如何建立並設置Swift項目以及如何選擇和設置Swift代碼編輯器。這包括在Xcode和命令行中建立Swift項目,選擇Xcode、Visual Studio Code或AppCode作為編輯器,以及如何使用SPM安裝插件。
Thumbnail
這份文件的目的是介紹Swift語言,包括它的特性、應用範疇,以及誰在使用它。它也提供了一些學習Swift的資源和工具,以及一些常見的Swift庫和框架。
Thumbnail
這份文件的目的是介紹Swift語言,包括它的特性、應用範疇,以及誰在使用它。它也提供了一些學習Swift的資源和工具,以及一些常見的Swift庫和框架。
Thumbnail
需求情境: 在設計畫面時,資料來源是後台的 api,每一次畫面細節的修修改改,都會觸發 Xcode Preview 程序,導致不斷呼叫後台。此時若資料結構和大小都具有一定規模,就會導致效率低落,不斷等待,且消耗伺服器資源甚鉅。 解決方案: 將後台傳回的資料以檔案形式暫存在本地端,每次 pr
Thumbnail
需求情境: 在設計畫面時,資料來源是後台的 api,每一次畫面細節的修修改改,都會觸發 Xcode Preview 程序,導致不斷呼叫後台。此時若資料結構和大小都具有一定規模,就會導致效率低落,不斷等待,且消耗伺服器資源甚鉅。 解決方案: 將後台傳回的資料以檔案形式暫存在本地端,每次 pr
Thumbnail
Part.1 搞定基本的 UI 開始開發 iOS App。 首先準備一台 Mac,然後安裝 Xcode,新增專案,系統即刻生成基本的專案結構。coding 的起點在檔案 ContentView.swift: import SwiftUI struct ContentView: View {  
Thumbnail
Part.1 搞定基本的 UI 開始開發 iOS App。 首先準備一台 Mac,然後安裝 Xcode,新增專案,系統即刻生成基本的專案結構。coding 的起點在檔案 ContentView.swift: import SwiftUI struct ContentView: View {  
Thumbnail
只是 Swift 以 language level 支援 Optional 確實比用 API level 支援的 Java 要簡潔和更具可讀性。Swift 作為一個全新的語言,從一開始的設計就將許多好的語言特性加入,確實讓人驚豔。
Thumbnail
只是 Swift 以 language level 支援 Optional 確實比用 API level 支援的 Java 要簡潔和更具可讀性。Swift 作為一個全新的語言,從一開始的設計就將許多好的語言特性加入,確實讓人驚豔。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News