SwiftUI 框架

Swift 5.7:應用新的 Regex 語法 在 SwiftUI 和 Combine 驗證使用者的輸入

Regex 歷史悠久,在許多 UNIX 工具中獲廣泛應用。Apple 在 WWDC2022 重寫了 Regex 語法,讓使用者用起來更方便。在這篇文章中,Mark 會帶大家用更現代的 Regex 語法,建立一個框架來驗證使用者設定的密碼!
Swift 5.7:應用新的 Regex 語法 在 SwiftUI 和 Combine 驗證使用者的輸入
Photo by Nangialai Stoman on Unsplash
In: SwiftUI 框架, swift 5.7
本篇原文(標題:Validation With Regex in Swift 5.7 Using SwiftUI and Combine)刊登於作者 Medium,由 Mark Lucking 所著,並授權翻譯及轉載。

有關正規表示式(Regular expression,regex)的歷史,我們可以追溯到 1951 年,美國數學家 Stephen Cole Kleene 提出了一個想法,就是使用數學符號來表達字串規律。直到 1967 年,Ken Thompson 把這個想法套用於他的編輯器 QED 和 UNIX 系統 (OSX、iOS 和 Android 的前身)下的 ED,這個想法就開始獲廣泛應用。

可能有人會覺得簡潔的 Regex 深深影響到 C 語言的語法。C 語言在 1972 年推出,可以說是世界上最廣泛應用的編程語言,Objective C 和現在的 Swift 的先驅。

雖然 Regex 沒有被納入 C 語言,但它在許多 UNIX 工具中獲廣泛應用,像是 grep、awk、vi、和大部分現代編程語言。因此,我很意外 Apple 竟然過了接近四十年 ── 直到 2010 年 ── 才在 Objective C 中採用 Regex。

而又過了 10 年之後,Apple 在 WWDC2022 重寫了 Regex 語法,讓使用者用起來更方便。在這篇文章中,我會帶大家用更現代的 Regex 更新 Validate Passwords in SwiftUI Forms Using CombineData Validation in SwiftUI 2.0 這兩篇文章。不過,我會盡量使用新命令框架 (command framework) 內的原始語法。

範例程式碼

在開始之前,讓我們先定義本篇文章會用到的 12 條 Regex 語法規則。如我剛才所說,以下的語法規則自 1967 年已經開始推行,因此可以說是十分完善。

enum Rules:String, CaseIterable {
    case alphaRule = "[A-Za-z]+"
    case digitRule = "[0-9]+"
    case limitedAlphaNumericCombined = "[A-Za-z0-9]{4,12}"
    case limitedAlphaNumericSplit = "[A-Za-z]{4,12}[0-9]{2,4}"
    case currencyRule = "(\\w*)[£$€]+(\\w*)"
    case wordRule = "(\\w+)"
    case numericRule = "(\\d+)"
    case numberFirst = "^(\\d+)(\\w*)"
    case numberLast = "(\\w*)(\\d+)$"
    case spaceRule = "[\\S]"
    case capitalFirst = "^[A-Z]+[A-Za-z]*"
    case punctuationCharacters = "[:punct:]"
}

簡單來說,方括號([])就表示所指示範圍內的字符,加號(+)表示出現一次或以上。大括號({})中的數字是最小和最大字符數。雙反斜線(\\)後加上一個小寫字母是一個字符類別,而雙反斜線後加上一個大寫字母該類別的相反。星號(*)表示出現零次或以上。如果在方括號外有插入符號(^),就表示必須以其字符開頭。

而插入符號在方括號內,也表示相反的意思。金錢符號($)就表示必須以其字符為結尾。最後,我們會有一個 POSIX 類別 [:punct:]

以下是對應上文 Regex 語法規則的錯誤訊息(順序也是相同的):

enum Crips:String, CaseIterable {
    case alphaRule = "MUST be alpha only"
    case digitRule = "MUST be numeric ONLY"
    case limitRuleCombined = "MIN 4 AlphaNumeric MAX 12 AlphaNumeric"
    case limitRuleSplit = "START MIN 4 Alpha MAX 12 Alpha, FINISH MIN 2 numeric, MAX 4 numeric"
    case currencyRule = "MUST contain $£€"
    case wordRule = "MUST be alphanumeric"
    case numericRule = "MUST be numeric"
    case numberFirst = "MUST start with a number"
    case numberLast = "MUST finish with a number"
    case noSpaces  = "MUST not contain spaces or tabs"
    case leadingCapital = "MUST start with an uppercase letter"
    case punctuationCharacters = "MUST contain punctuation characters"
    
    static var cripList: [String] {
        return Crips.allCases.map { $0.rawValue }
    }
}

在開始之前,我們還需要一個 extension,來幫助我們把第一組 enum map 到第二組,然後它會回傳我們提交的 enum 的 index。

extension CaseIterable where Self: Equatable {
    public func ordinal() -> Self.AllCases.Index {
        return Self.allCases.firstIndex(of: self)!
    }
}

我們可以進入正題了,這篇教學。讓我們先定義一個結構,在結構中我們會使用密碼字符不符合 Regex 規則相應的錯誤訊息。

struct Diag: Hashable, Codable, Identifiable {
    var id = UUID()
    var message = ""
}

這個結構是 HashableCodable、和 Identifiable 的,因此我們可以在 SwiftUI loop 內使用它。在結構裡面,我們有兩個主要函式,第一個就是在 enum 內匹配 Regex。

fileprivate func matchRegex() {
        for rule in Rules.allCases {
            let formulae = try! Regex(rule.rawValue)
            if let _ = passText.wholeMatch(of: formulae) {
                // is good, right size alpha upper + lower & numeric
            } else {
                let diag = Crips.cripList[rule.ordinal()]
                diagMsgs.append(Diag(message: "\(diag)"))
                let foo = /\a/
                print(foo)
            }
        }
    }

它會檢查所有 Regex 是否能夠符合所有規則,並為相應的錯誤訊息構建一個陣列 (array)。

而第二個函數則顯示一個類似控制台的診斷清單 (diagnostics list),這個清單如果太長,會變成一個滾動視圖。

fileprivate func displaceFailedMatches() -> ScrollViewReader<ScrollView<VStack<some View>>> {
        return ScrollViewReader { moveTo in
            ScrollView(.vertical) {
                VStack(alignment: .leading) {
                    ForEach(diagMsgs, id: \.id) { text in
                        Text("\(text.message)")
                            .font(Fonts.neutonRegular(size: 16))
                            .id(text.id)
                    }.onChange(of: diagMsgs) { _ in
                        moveTo.scrollTo(diagMsgs.last?.id, anchor: .bottom)
                    }
                }
            }
        }
    }

最後,Main body 應該是這樣的:

var body: some View {
        VStack {
            HStack {
                Spacer(minLength: 4)
                TextField("Pass ", text: $passText)
                    .font(Fonts.neutonRegular(size: 32))
                    
                    .onChange(of: passText) { newValue in
                        diagMsgs.removeAll()
                        matchRegex()
                        if passText.isEmpty {
                            diagMsgs.removeAll()
                        }
                    }
                    .border(Color.black)
                    .padding(.top, 64)
                Spacer()
            }
            displaceFailedMatches()
        }
    }

我們需要使用以下的 two-state 變數定義主體,而其 reference 的方法在前文已經提過了。

struct ContentView: View {
    @State var passText = ""
    @State var diagMsgs:[Diag] = []

把所有程式碼放在一起,螢幕頂部會出現一個 field,讓我們可以輸入字詞。讓我們試試輸入一個字詞,下面就會顯示所有無法符合的規則。

如你所見,由於規則有衝突,我們根本無法清除所有錯誤訊息。大家可以想想需要刪除哪些規則,才可以建立出可行的範例。

這篇教學文章到此為止。如果你有興趣了解更多,可以花點時間看看相關的 WWDC2022 影片。雖然 Apple 已經為 Regex 開發了一種新的語法,不過我建議大家可以先了解已經存在了 60 年以上的版本(如這篇教學文章所示)。

以上就是隨處可見的 Regex 語法的簡介。

本篇原文(標題:Validation With Regex in Swift 5.7 Using SwiftUI and Combine)刊登於作者 Medium,由 Mark Lucking 所著,並授權翻譯及轉載。
作者簡介:Mark Lucking,編程資歷超過 35 年,熱愛使用及學習 Swift/iOS 開發,定期在 Better ProgrammingThe StartUpMac O’ClockLevel Up Coding、及其它平台上發表文章。
譯者簡介:Kelly Chan-AppCoda 編輯小姐。
作者
AppCoda 編輯團隊
此文章為客座或轉載文章,由作者授權刊登,AppCoda編輯團隊編輯。有關文章詳情,請參考文首或文末的簡介。
評論
更多來自 AppCoda 中文版
iOS 18 新API:使用 Navigation Transition 創建 Hero 動畫式過場
SwiftUI 框架

iOS 18 新API:使用 Navigation Transition 創建 Hero 動畫式過場

Apple 的工程師可能早已認識到,許多 iOS 開發者都希望能夠重現 App Store 應用程式中的優雅 Hero 動畫。由於從頭實現這種動畫通常需要耗費大量時間與精力,Apple 在 iOS 18 SDK 中納入了這項功能。 透過這次更新,你現在只需少量的程式碼就能在自己的應用程式中實現類似的動畫過渡效果。這項重大改進讓開發者能夠創造出更具視覺吸引力且流暢的過渡效果,
如何使用 Vision APIs 從圖像中辨識文字
AI

如何使用 Vision APIs 從圖像中辨識文字

Vision 框架長期以來一直包含文字識別功能。我們已經有詳細的教程,向你展示如何使用 Vision 框架掃描圖像並執行文字識別。之前,我們使用了 VNImageRequestHandler 和 VNRecognizeTextRequest 來從圖像中提取文字。 多年來,Vision 框架已經顯著演變。在 iOS 18 中,Vision
iOS 18更新:SwiftUI 新功能介紹
SwiftUI 框架

iOS 18更新:SwiftUI 新功能介紹

SwiftUI的技術不斷演進,每次更新都讓 iOS 應用程式開發變得更加便捷。隨著 iOS 18 Beta 的推出,SwiftUI 引入了多個令人興奮的新功能,使開發者僅需幾行程式碼即可實現出色的效果。 本教學文章旨在探索這個版本中的幾項主要改進,幫助你了解如何運用這些新功能。 浮動標籤列 (Floating Tab Bar)SwiftUI中的標籤視圖(Tab
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。