第 2 章
使用 Playground 來首次體驗 Swift
現在你已經設定完 iOS App 開發所需的環境,再繼續往下之前,我先回覆初學者另一個常見的問題,很多人會問我:「開發 iOS App 之前需要先具備哪些技能」,簡而言之,可以歸納成以下三個部分:
- 學習Swift - Swift 現在是撰寫 iOS App 所推薦的程式語言。
- 學習Xcode - Xcode 是你設計 App UI、撰寫 Swift 程式與建立你的 App 所需的開發工具。
- 了解iOS軟體開發工具 - Apple提供軟體開發工具,使開發者能夠更輕鬆開發程式。這 個套件內建一組軟體開發工具與API,可以讓開發者強化iOS App 的開發能力。例如: 下一章將要討論的SwiftUI 框架,是建置使用者介面和動畫的基本框架之一。如果你想要在你的App 中顯示網頁,SDK還提供了一個內建的瀏覽器來讓你直接嵌入在App 中。
你需要具備以上三項的相關知識,而且還有許多的東西要學習。不過別擔心,當你學習完本書之後,你將學習到這些能力。
Swift的歷史
首先,我來告訴你一些關於 Swift 的歷史。
在 2014 年的 Apple 全球開發者大會上,Apple 推出了一個名為「Swift」的新程式語言, 這令所有的 iOS 開發者大吃一驚,Swift 被宣稱為一種「快速、現代、安全、互動」的程式語言,其更易於學習,且具有讓程式開發更有效率的許多功能。
在Swift 發布之前,iOS Apps 主要是以 Objective-C 來撰寫,這個語言已經存在 30 多年了,並被 Apple 選為 Mac 與 iOS 的主要開發語言。我曾和許多優秀的 iOS 開發者交談過, 絕大多數的人認為 Objective-C 難以學習,而且語法看起來很怪異。簡單來說,這些程式碼嚇跑了一些想學習 iOS 程式開發的初學者。
Swift 程式語言的發布,或許是Apple 對於其中一些評論的回答,其語法更為簡潔易讀。從 Swift 發布 Beta 版以來,我就一直使用 Swift 開發,至今已經超過 8 年了,我可以說使用 Swift 幾乎可保證你的開發效率更高,一旦你習慣了 Swift 程式語言,你就很難再切換回 Objective-C 了。
在我看來,Swift 將會吸引更多的網頁開發者或初學者來建立 App。如果你是擁有任何腳本語言編寫經驗的網頁開發者,你可以利用現有的專業技術來獲得有關開發 iOS App 的知識,對你而言,學習 Swift 會相當容易。話雖如此,即使你是一個完全沒有程式設計經驗的初學者,你也會發現Swift 語言更友善,並且在使用 Swift 開發 App 時感覺更自在。
2015 年6 月,Apple 宣布了 Swift 2,並且將這個程式語言變成開源,這真是一件大事, 自此之後,開發者就使用這個程式語言開發一些有趣且驚人的開源專案。你不僅可以使用 Swift 來開發 iOS App,而且 IBM 等公司還開發了網頁框架(Web Framework ),可以讓你以 Swift 建立 Web App,現在你也可以在 Linux 執行 Swift。
在 Swift 2 發布之後,Apple 於 2016 年6 月推出了Swift 3,該版本的程式語言已整合到 2016 年9 月所發布的Xcode 8 中,這被認為是這個程式語言誕生以來的最大版本之一。Swift 3 有許多的變更,API 被重新命名,並引入了更多的功能,所有的變更都有助於讓這個語言變得更好,並使得開發者可以編寫更漂亮的程式碼,不過所有的開發者都需要額外花工夫將專案移轉至新版本,以因應這些重大的變化。
2017 年6 月,Apple 發布了Swift 4 與 Xcode 9,其有更多的增強與改進,這個版本的 Swift 側重於能夠向下相容,這表示用 Swift 3 開發的專案在理想情況下不需任何修改,就可在 Xcode 9 執行。即使你不得不移轉,從Swift 3 移轉到 Swift 4 也比從 Swift 2.2 移轉到 Swift 3 要簡單得多。
2018 年,Apple 只發布了Swift 的小更新,並將Swift 的版本號推至 4.2,雖然它不是大版本,但是這個新版本也加入了很多的語言功能來提升生產力及效率。
2019 年3 月下旬,Apple 正式發布了Swift 5,這是該程式語言的重要里程碑,雖然它包含了許多的新功能,但最重要的變化是Swift 執行現在已被包含在Apple 平台作業系統中(包括 iOS、macOS、watchOS 與 tvOS),對於優秀的開發者來說,實際上是個好消息, 這表示 Swift 語言更加穩定、成熟了。而你在本書中所學到的一切,都將適用於 Swift 的未來版本。
2023 年,Swift 語言進一步更新到 Swift 5.9,其功能更加豐富,例如:巨集(macro)。
如果你完全是個初學者,你可能心中會有些疑問,為什麼 Swift 會不斷變化呢?如果它不斷更新,Swift 到底能不能使用呢?
幾乎所有的程式語言都會隨著時間而改變,Swift 也不例外,每年都會在 Swift 加入新的程式語言功能,以使這個程式語言更強大,並且對開發者更友善。這和我們的語言一樣,例如:英語仍然會隨著時間而變更,每年都會在字典中加入新的單字與片語(如 freemium )。
所有的程式語言一段時間就會做更新,理由有很多種,就和英語一樣。
出處: https://www.english.com/blog/english-language-has-changed
雖然Swift 不斷進化,但這並不表示它還不能被真正運用,當你想建立一個iOS App, 你應該使用Swift。事實上,它已經成為iOS App 的開發標準,如 LinkedIn、 Duolingo 與 Mozilla ),從最初的版本都已經完全使用 Swift 來撰寫。自 Swift 4 發布以來,這個程式語言變得更穩定,絕對適合企業與一般開發來使用。
開始學習 Swift
對於背景與歷史就談到這裡,我們來開始介紹Swift。
在嘗試開始使用Swift 程式語言之前,先來看下列的程式碼。
Objective-C
const int count = 10;
double price = 23.55;
NSString *firstMessage = @"Swift is awesome. ";
NSString *secondMessage = @"What do you think?";
NSString *message = [NSString stringWithFormat:@"%@%@", firstMessage, secondMessage];
NSLog(@"%@", message);
Swift
let count = 10
var price = 23.55
let firstMessage = "Swift is awesome. "
let secondMessage = "What do you think?"
var message = firstMessage + secondMessage
print(message)
T第一個程式碼區塊是使用 Objective-C 編寫的,第二個程式碼區塊則是使用 Swift 編寫的,你對於以上哪一種語言比較喜歡呢?我猜你更喜歡使用 Swift 來編寫,特別是當你對 Objective-C 語法感到沮喪時。Swift 較清楚且易於閱讀,每一個敘述末尾都沒有@符號與分號。以下這兩個敘述是將第一個訊息與第二個訊息串接在一起,我相信你大概能猜到下列 Swift 程式碼的含義:
var message = firstMessage + secondMessage
而你恐怕會對下列的Objective-C 程式碼感到困惑:
NSString *message = [NSString stringWithFormat:@"%@%@", firstMessage, secondMessage];
在 Playground 中試驗 Swift
我不想直接列出程式碼來讓你感到厭煩,而探索程式的最好方式就是「實際編寫程式」了。Xcode 有一個名為「Playground」的內建功能,它是供開發者試驗 Swift 程式語言的互動式開發環境,可以讓你即時看到程式碼的執行結果,稍後你將會了解我的意思以及 Swift Playground 的運作方式。
假設你已經安裝好 Xcode 15(或更高的版本),啟動這個應用程式(點選 Launchpad 的 Xcode 圖示),然後你應該會看到一個啟動對話方塊,如圖 2.1 所示。
Playground 是一種特殊類型的Xcode 檔案。在Xcode 主選單中,點選「File → New → Playground...」來建立一個新Playground 檔案,然後它會提示你為Playground 選擇模板。由於我們著重於在iOS 環境中探索Swift,因此在「iOS」區塊下選擇「Blank」來建立一個空白檔,接著點擊「Next」按鈕來繼續,如圖2.2 所示。
當你確認儲存檔案後,Xcode 會開啟Playground 介面,你的畫面應該如圖2.3 所示。
 畫面的左側窗格是你輸入程式碼的編輯區,當你想測試程式碼並看它是如何運作時, 則點擊「Play」按鈕,Playground 會立即解譯程式碼(直到「Play」按鈕那一行),並在右側窗格中顯示結果。預設上,Swift Playground 包含兩行程式碼,如你所見,當你在第 4 行點擊「Play」按鈕後,greeting 變數的結果會立即出現在右側窗格中。
我們將會在 Playground 中撰寫一些程式,請記住這個練習的目的是要讓你體驗 Swift 程式,並學習它的基礎知識。我不會介紹 Swift 的所有功能,而是將重點放在下列的主題:
- 常數(Constant)、變數(Variable)與型別推論(Type Inference)。
- 控制流程(Control Flow)。
- 集合型別(Collection Types),如陣列(Array)與字典(Dictionary)。
- 可選型別(Optional)。
這些是你需要了解的Swift 相關基本主題,你將透過例子進行學習。然而,我很確信你將會對一些程式觀念感到困惑(尤其是程式菜鳥的話),但不用擔心,你將會在章節裡找到我的學習建議,只要遵循我的建議來持續學習即可,還有當你在學習上遇到障礙時,記得要休息一下。
酷 !讓我們開始吧 !
常數與變數
常數與變數是程式中的兩個基本元素。變數(或是常數)的觀念和你在數學所學的一樣,如下所示:
x = y + 10
這裡 x 與 y 都是變數。10 是一個常數,表示值是沒有改變的。
在 Swift 中,你使用 var 關鍵字宣告變數,並使用 let 關鍵字宣告常數。當你將上列的方程式寫成程式碼,看起來如下所示:
let constant = 10
var y = 10
var x = y + constant
在 Playground 輸入上列的程式碼,然後點擊第 5 行的「Play」按鈕,結果如圖 2.4 所示。
你可以為變數與常數選擇任意名稱,只要確保名稱是有意義的即可,例如:你可以重寫同一段程式碼如下:
let constant = 10
var number = 10
var result = number + constant
為了讓你清楚了解 Swift 的常數與變數的差異,輸入下列的程式碼來變更 constant 與 number 的值:
constant = 20
number = 50
接著,按下 shift+command+enter 鍵來執行程式,除了使用「Play」按鈕以外,你可以使用快捷鍵來執行程式。
你只需要為常數與變數設定新值,不過一旦你變更常數的值,Xcode 就會在控制台出現錯誤提示;反之,number 則沒有問題,如圖 2.5 所示。
這是 Swift 中常數與變數的主要差異,當常數使用值進行初始化,就不能再更改它,如果初始化後必須更改值的話,則使用變數。
型別推論
Swift 提供開發者許多功能來編寫簡潔的程式,其中一個功能是「型別推論」。我們剛才討論的相同程式碼片段可以明確編寫如下:
let constant: Int = 10
var number: Int = 10
var result: Int = number + constant
Swift 中的每個變數都有一個型別,在「:」後面的關鍵字「Int」表示變數 / 常數的型別是整數,如果儲存的值是小數,我們使用 Double 型別。
var number: Double = 10.5
還有其他型別,例如:用於文字資料的String 和用於布林值(true / false )的 Bool。
現在回到型別推論,Swift 中這個強大功能可讓你在宣告變數 / 常數時省略型別,以使程式碼更簡潔。Swift 編譯器可以透過檢查你給定的預設值來推論型別,這就是為何我們可將程式碼編寫如下:
let constant = 10
var number = 10
var result = number + constant
T這裡所給定的值(即10)是一個整數,所以其型別會自動設定為「Int」。在 Playground 中,你可以按 option 鍵,然後點選任何一個變數名稱,來揭示由編譯器所推論的變數型別,如圖 2.6 所示。
對所有的新程式觀念感到不知所措嗎?
休息一下,你不需要一氣呵成來讀完本章。若是你迫不及待地想要建立你的第一個App,則可以跳過本章來閱讀下一章,你可以隨時回到本章來學習Swift 的基礎知識。
處理文字
到目前為止,我們只使用了 Int 與 Double 型別的變數。想要在變數中儲存文字資料, Swift 提供了一個名為「String」的型別。
要宣告 String 型別的變數,你使用 var 關鍵字賦予變數一個名稱,並為變數指定初始文字。所指定的文字以雙引號(" )包裹,下面是一個例子:
var message = "The best way to get started is to stop talking and code."
在Playground 中輸入上列的程式碼並點擊「Play」按鈕後,你將會在右側窗格看到結果,如圖2.7 所示。
Swift 提供不同的運算子與函數(或是方法)來讓你操作字串,例如:你可以使用 + 運算子來將兩個字串串接在一起,如圖2.8 所示。
var greeting = "Hello "
var name = "Simon"
var message = greeting + name
如果你想要將整個句子轉換成大寫呢?Swift 提供了一個名為「uppercased()」的內建方法,可以將一個字串轉換成大寫。你可以輸入下列的程式碼來試驗:
message.uppercased()
Xcode 的編輯器有自動完成的功能,「自動完成」是一個很方便的功能,可加快程式編寫的速度。當你輸入「mess」,會看到一個自動完成視窗,它根據你輸入的內容顯示一些建議,你只需要選擇「message」,並按下enter 鍵即可,如圖 2.9 所示。
 Swift 採用點語法來存取內建的方法及變數的屬性。當你在 message 之後輸入點符號, 自動完成視窗會再次彈出,它會建議一串可透過變數存取的方法與屬性。你可以持續輸入uppercase(),或者從自動完成視窗中選取,如圖 2.10 所示。
  一旦輸入完成後,你會馬上看到輸出結果。當我們對 message 使用 uppercased(),這個 message 的內容會自動轉換為大寫。
uppercased() 是字串所內建的功能之一,你可以使用 lowercased() 來將 message 轉換成小寫。
message.lowercased()
或者,如果你想要計算字串的字元數量,則可以編寫程式碼如下:
message.count
字串的串接看起來非常簡單,對吧?你只要使用 + 運算子,就可以將兩段字串相加。不過,並不是一切都這麼單純,我們在 Playground 中編寫下列的程式碼:
var bookPrice = 39
var numOfCopies = 5
var totalPrice = bookPrice * numOfCopies
var totalPriceMessage = "The price of the book is $" + totalPrice
將字串與數字混合在一起的情況很常見。在以上的例子中,我們計算書的總價,並建立一個訊息告訴使用者總價為何,如果你在 Playground 中輸入這些程式碼,你會注意到一個錯誤,如圖 2.12 所示。
當 Xcode 在程式碼中發現錯誤時,這個錯誤會以帶有簡短錯誤訊息的紅色警示符號來指示錯誤,Xcode 有時會顯示可能的錯誤修復方案,有時則不會。
要揭示錯誤詳細訊息,則你可以參考除錯區 / 主控台。如果主控台未顯示在你的 Playground 中,則點擊右上角的「除錯區」按鈕。
在我告訴你如何解決這個問題之前,你知道程式碼為何無效嗎?請先花個幾分鐘來想一下。
首先,要記住 Swift 是型別安全(type-safe)的語言,這表示每一個變數都有一個型別, 以指定它可以儲存什麼樣的值。你知道 totalPrice 的型別是什麼嗎?回想之前我們學過的內容,Swift 可以透過檢查值來確定變數的型別。
因為39 是一個整數,Swift 判斷 bookPrice 的型別是 Int,numOfCopies 與 totalPrice 也是。
主控台中顯示的錯誤訊息會提到+ 運算子不能串接 String 變數與 Int 變數,它們必須具有相同的型別。換句話說,你必須要先將 totalPrice 從 Int 轉換成 String 才能執行。
你可使用下列的程式碼來將整數轉換成字串:
var totalPriceMessage = "The price of the book is $" + String(totalPrice)
還有一種名為「字串插值」(String Interpolation )的方式也可以辦到。你可以像這樣編寫來建立totalPriceMessage 變數:
var totalPriceMessage = "The price of the book is $ \(totalPrice)"
「字串插值」是在多個型別中建立字串的推薦方式,你可以將用於字串轉換的變數包裹在括號中,並使用反斜線作為前綴。
變更完成之後,點擊「Play」按鈕來重新執行這段程式,錯誤應該已經修正。
流程控制
關於自信心,我認為你們會意識到,你們的成功不僅僅是因為你們自己所做的一切,而是因為在朋友的幫助下,你們不害怕失敗。如果真的失敗了,爬起來再試一次,如果再次失敗,那就再爬起來, 再試一次;如果最後還是失敗了,或許該考慮做些其他的事情,你們能夠站在這裡,不僅是因為你們的成功,而是因為你們不害怕失敗。
- John Roberts, Chief Justice of the United States
出處: http://time.com/4845150/chief-justice-john-roberts-commencement-speech-transcript/
每天我們都會做很多的決定,不同的決定會導致不同的結果或行為。舉例而言,你決定明天 6 點若能起床,你就為自己做一頓豐盛的早餐,否則的話,你就出去吃早餐。
寫程式時,你會使用到 if-then 以及 if-then-else 敘述來檢查條件,然後決定下一步要做什麼。如果你要將以上的例子寫成程式,看起來會像這樣:
var timeYouWakeUp = 6
if timeYouWakeUp == 6 {
print("Cook yourself a big breakfast!")
} else {
print("Go out for breakfast")
}
你宣告一個 timeYouWakeUp 變數來儲存你睡醒的時間(24 小時制),並使用 if 敘述來評估一個條件,以決定下一步的動作。這個條件是放在if 關鍵字後面,這裡我們比較 timeYouWakeUp 的值,來看它是否等於 6。這裡的== 運算子是用來進行比較。
如果 timeYouWakeUp 等於 6,則執行大括號中的動作(或敘述)。在程式碼中,我們簡單使用 print 函數來將訊息輸出到主控台;否則,將執行 else 區塊中指定的敘述,來輸出另一個訊息,如圖 2.13 所示。
在 Playground 中,你會在主控台看到「Cook yourself a big breakfasts!」這個訊息,因為 timeYouWakeUp 的值被初始化為「6」,你可以試著變更為其他值,來看看會有什麼樣的結果。
在程式設計中,條件式邏輯很常見。假設你正在開發一個登入畫面,需要使用者輸入使用者姓名與密碼。使用者只能使用有效的帳號才能登入,在這種情況下,你可以使用 if-else 敘述來驗證使用者名稱與密碼。
if-else 敘述是 Swift 控制程式流程的其中一種方式,Swift 也提供 switch 敘述來控制要執行哪個程式碼區塊,你可以使用 switch 重寫上面的例子:
var timeYouWakeUp = 6
switch timeYouWakeUp {
case 6:
print("Cook yourself a big breakfast!")
default:
print("Go out for breakfast")
}
如果 timeYouWakeup 設定為「6」,也會得到相同的結果,switch 敘述是把一個值(這裡指的是 timeYouWakeUp 的值)和 case 內的值進行比較,預設的 case 是由 default 關鍵字指示,這和 if-else 敘述的else 程式碼區塊很像,如果所評估的值與任何一種情況不相符的話,就會執行預設的 case,因此如果你將timeYouWakeUp 的值修改為「8」,則會顯示「Go out for breakfast」的訊息。
至於何時要使用 if-else 敘述或者 switch 敘述,並沒有一定的準則,有時我們更喜歡其中一個,只是可讀性的緣故。假設你通常在年底獲得獎金,現在你正在為你的下一個旅行目的地制定計畫,計畫如下:
- 如果你獲得$10000的獎金(或者更多),你將前往巴黎或倫敦旅行。
- 如果你獲得$5000至$9999之間的獎金,你將前往東京旅行。
- 如果你獲得$1000至$4999之間的獎金,你將前往曼谷旅行。
- 如果獎金少於$1000,則待在家中。
當你將以上的計畫寫成程式碼,寫法如下:
var bonus = 5000
if bonus >= 10000 {
print("I will travel to Paris and London!")
} else if bonus >= 5000 && bonus < 10000 {
print("I will travel to Tokyo")
} else if bonus >= 1000 && bonus < 5000 {
print("I will travel to Bangkok")
} else {
print("Just stay home")
}
>= 是比較運算子(Comparison Operator ),表示「大於或等於」。第一個 if 條件檢查bonus 的值是否大於或等於 10000。要同時指定兩種條件,你可以使用&& 運算子。第二個 if 條件檢查值是否在 5000 與 9999 之間。其餘的程式碼應該無須解釋。
你可以使用switch 敘述來將上列的程式碼改寫如下:
var bonus = 5000
switch bonus {
case 10000...:
print("I will travel to Paris and London!")
case 5000...9999:
print("I will travel to Tokyo")
case 1000...4999:
print("I will travel to Bangkok")
default:
print("Just stay home")
}
Swift 有一個非常方便的範圍運算子「...」,其定義從下限至上限的範圍,例如: 「5000...9999」,表示範圍是從5000 到9999。對於第一個情況,「10000...」表示大於10000 的值。
這兩個程式碼區塊的執行完全一樣,但是你比較喜歡哪種方式呢?在這種情況下,我比較喜歡 switch 敘述,可以讓程式更簡潔。無論如何,即使你比較喜歡使用if 敘述來處理以上的問題,結果是一樣的。當你繼續探索Swift 程式語言,你將會了解 if 或 switch 的使用時機。
陣列與字典
現在你已經對變數與控制流程有了基本的了解,我來介紹另一個常會用到的程式觀念。
到目前為止,我們使用的變數只能儲存單一值。參考前面程式碼片段中的變數,不論變數型別為何,bonus、timeYouWakeUp 與 totalPriceMessage 都可存放單一值。
我們來看下列的範例。假設你正在建立一個書架應用程式來分門別類你的圖書蒐藏。在你的程式碼中,你可能會有一些變數存放你的書名:
var book1 = "Tools of Titans"
var book2 = "Rework"
var book3 = "Your Move"
除了在每一個變數儲存單一值之外,是否有其他方式能夠儲存更多的值呢?
Swift 提供一個名為「陣列」(Array)的集合型別,可以讓你在一個變數中儲存多個值。有了陣列,你可以像這樣儲存書名:
var bookCollection = ["Tool of Titans", "Rework", "Your Move"]
你可以使用一串值來初始化一個陣列,並將值以逗號分開,然後以方括號包裹起來。同樣的,因為Swift 是一種型別安全的語言,因此所有值必須要是相同的型別(如字串)。
如果你才剛開始學習寫程式,可能會對陣列值的存取感到奇怪。在 Swift 中,使用下標語法( Subscript Syntax )來存取陣列元素。第一個項目的索引是0,因此引用陣列的第一個項目,可以編寫如下:
bookCollection[0]
如果在 Playground 中輸入上列的程式碼並點擊「Play」按鈕,你應該會在輸出窗格看到「Tool of Titans」。
當你以 var 宣告一個陣列,你可以修改它的元素,例如:你可以像這樣呼叫 append 內建方法,來加入一個新的項目至陣列中:
bookCollection.append("Authority")
現在陣列有四個項目,那麼該如何知道陣列的總數呢?使用內建的 count 屬性:
bookCollection.count
你知道要怎樣才能把陣列的每個項目的值輸出到主控台嗎?
不要馬上看解答。
試著想一下。
好的,你也許會編寫程式碼如下:
print(bookCollection[0])
print(bookCollection[1])
print(bookCollection[2])
print(bookCollection[3])
這樣的寫法沒有問題,不過還有更好的方式。如你所見,上面的程式都是重複性的, 如果陣列有100 個項目,則輸入100 行程式碼會很乏味。在 Swift 中,你可以使用 for-in 迴圈,以特定的次數來執行一個任務(或一段程式)。舉例而言,你可以將上列的程式碼簡化如下:
for index in 0...3 {
print(bookCollection[index])
}
你指定要迭代的數字範圍「0...3」,在這個情況下,for 迴圈內的程式碼會執行 4 次,而 index 的值會跟著做變更。當 for 迴圈第一次開始執行,index 的值設定為「0」,它會輸出 bookCollection[0]。當敘述執行後,index 的值會更新為「1」,並輸出 bookCollection[1], 整個過程重複持續到所設的範圍(即3)為止。
現在問題來了,如果陣列中有10 個項目該怎麼做呢?你也許會將範圍從 0...3 改為 0...9; 那麼如果項目增加到100 個呢?你會將範圍改成 0...99。
有沒有通用的方法可以做到這一點,而不必每次項目總數更改時都要去更新程式碼?
你是否注意到0...3、0...9 與0...99 等這些範圍的模式?
範圍的上限等於項目總數減1,其實你可以將程式碼重寫如下:
for index in 0...bookCollection.count - 1 {
print(bookCollection[index])
}
現在,不管陣列項目的數量如何,這個程式碼片段都可執行。
Swift 的for-in 迴圈提供另一種迭代陣列的方式,範例程式碼片段可以重寫如下:
for book in bookCollection {
print(book)
}
當陣列(即 bookCollection )迭代時,每次迭代的項目會被設定給 book 常數。當迴圈第一次開始後,bookCollection 內的第一個項目設定給 book,下次的迭代中,陣列的第二個項目將會指定給 book,這個過程持續進行到最後一個陣列項目。
現在相信你已經了解 for-in 迴圈的原理,並知道如何使用迴圈來重複任務。我們來說明另一個常見的集合型別,稱為「字典」(dictionary )。
字典和陣列很相似,可以讓你在一個變數/常數中儲存多個值,主要的差異在於字典中的每個值會關聯一個鍵(key ),你可以使用唯一鍵來存取該項目,而不是使用索引來識別項目。
讓我繼續以藏書為例子,每本書有一個唯一的 ISBN(國際標準書號,International Standard Book Number 的縮寫),如果你想要將每本書以它的 ISBN 作為其索引,你可以像這樣宣告與初始化一個字典:
var bookCollectionDict = ["1328683788": "Tool of Titans", "0307463745": "Rework", "1612060919": "Authority"]
這個語法與陣列初始化的語法非常相似,所有的值都被一對方括號包裹起來,鍵與值分別以一個冒號(: )隔開。在範例程式碼中,其鍵為ISBN,而每本書都關聯一個唯一的 ISBN。
那麼你該如何存取一個特定項目呢?同樣的,和陣列非常相似,不過這裡不使用數字索引,而是使用一個唯一鍵。範例如下:
bookCollectionDict["0307463745"]
這會回傳一個值給你:「Tool of Titans」。要迭代字典中的所有項目,你也可以使用 for-in 迴圈:
for (key, value) in bookCollectionDict {
print("ISBN: \(key)")
print("Title: \(value)")
}
你可從主控台中的訊息中看出,項目的排序並沒有依照初始化的順序,和陣列不一樣,這是字典的特性,項目是以無排序的方式來儲存。
你可能想說,建立一個App 時,何時會用到字典?我們以另一個例子來看它之所以會稱為「字典」的原因。思考一下你是如何使用字典?在字典中查詢一個字,它會標示文字的含義。在這種情況下,這個文字就是鍵,其含義是關聯的值。
在進入到下一小節之前,我們做一個很簡單的練習來建立一個表情符號字典,這個字典存放表情符號的意義,為了簡單起見,這個字典具有下列幾個表情符號及其意義:
- 👻 - Ghost
- 💩 - Poop
- 😤 - Angry
- 😱 - Scream
- 👾 - Alien monster
你知道如何使用字典型別來實作表情符號字典嗎?以下是表情符號字典的程式架構, 請填入缺少的程式碼,以使它能夠執行:
var emojiDict = // 填入初始化字典的程式 //
var wordToLookup = // 填入鬼臉表情符號 //
var meaning = // 填入存取值的程式 //
wordToLookup = // 填入生氣表情符號 //
meaning = // 填入存取值的程式 //
要在Mac 上輸入表情符號,則按下 control-command+space 鍵。
你能夠完成這個練習嗎?
我們來看圖2.15 的解答及其輸出結果。
我相信你已經自己找出解答了。
現在,我們加入幾行程式碼來輸出 meaning 變數至主控台,如圖 2.16 所示。
你會注意到兩件事:
- Xcode指出這兩個輸出的敘述都有一些問題。
- 主控台區的輸出和我們之前經歷過的其他輸出有所不同。結果是正確的,但是可選型 別是什麼意思呢?
Note: 在Xcode 中,警告是以黃色來標示。警告和錯誤的差異在於,即使有一些警告,你的程式還是能夠執行。顧名思義,警告會預先告知你有某些問題。你最好能夠修復這些問題,以免有潛在的問題。
這兩個問題都和Swift 中一個名為「可選型別」(Optional )的新觀念有關。即使你有一些程式設計的背景,對你而言,這個觀念可能是新的。
我希望你能夠喜歡到目前為止的內容。如果有任何問題而卡住的話,不妨休息一下,喝杯咖啡來放鬆心情,或者你可以跳過本章剩下的部分,然後進入下一章來試著建立你的第一個 App,你隨時可以回來複習本章。
可選型別
你有過這樣的經驗嗎?你開啟一個 App,點擊一些按鈕,然後突然就當機了,我相信你應該有過這種經歷。
為什麼 App 會當機呢?一個常見的原因是,App 試著在執行期中存取一個沒有值的變數,然後便發生例外事件。
那麼有沒有辦法可以避免當機呢?
不同的程式語言有不同的策略來鼓勵程式設計師寫一些好的或者不易出錯的程式碼。導入可選型別,是Swift 幫助程式設計師編寫更好的程式來避免App 當機的方式。
一些開發者很難理解可選型別的觀念,它的基礎觀念其實十分簡單。在存取可能沒有值的變數之前,Swift 會建議你先驗證它,你必須先確保它有值才繼續,如此可避免 App 當機。
到目前為止,我們使用的所有變數或常數都有一個初始值,這在 Swift 中是必要的。一個非可選型別的變數一定要有值,當你試著宣告一個沒有值的變數,你會得到錯誤,你可以在 Playground 中做測試,試試看會發生什麼結果,如圖 2.17 所示。
在某些情況下,你必須宣告一個沒有初始值的變數。想像一下,你正在開發一個有註冊表單的 App,表單中的所有欄位並非都是必填,有些欄位(如工作職稱)是可以選填的,在這種情況下,這些可選欄位的變數可能沒有值。
技術上,可選型別只是 Swift 中的一個型別,這個型別表示變數可以有值或沒有值。要宣告變數為可選型別,你可以在變數後面加上問號( ? ),如下所示:
var jobTitle: String?
你宣告一個名為「jobTitle」、String 型別的變數,它也是可選型別。如果你將上面的程式碼放在 Playground 中,它不會顯示錯誤,因為 Xcode 知道 jobTitle 可以沒有值。
與編譯器可以從初始值推論型別的非可選型別變數不同,你必須明確指定可選型別變數的型別(例如:String、Int )。
如果你依照我的指示在Playground 中輸入程式碼(並點擊「Play」按鈕),你可能會注意到結果窗格顯示 nil。對於任何沒有值的可選型別變數,會為其指定一個名為「nil」的特別值,如圖 2.18 所示。
換句話說,nil 表示變數沒有值。
當你必須指定一個值給可選型別變數,你可以像往常一樣指定,如下所示:
jobTitle = "iOS Developer"
現在你應該對可選型別有些概念了,但是它如何幫助我們寫出更好的程式呢?如圖 2.19 所示來輸入程式碼。
當你一輸入完下列的程式碼,Xcode 會提示一個錯誤訊息。
var message = "Your job title is " + jobTitle
這裡的 jobTitle 被宣告為一個可選型別變數,Xcode 告訴你該行程式碼有潛在的錯誤, 因為 jobTitle 應該是沒有值,你必須在使用可選型別變數之前要先做一些驗證。
這就是可選型別如何避免你寫出有問題的程式的方式。每當你需要存取一個可選型別變數,Xcode 會強制你執行驗證來看這個可選型別是否有值。
強制解開
而你該如何執行這樣的驗證,並解開(Unwrap )可選型別變數的值呢?Swift 提供幾個方法。
首先,即所謂的「if 敘述與強制解開」(Forced Unwrapping )。簡單來說,你使用 if 敘述來將可選型別變數與nil 進行比較,即可驗證這個可選型別變數是否有值。若這個可選型別確實有值,你可以解開它的值來做進一步的處理,程式碼如下所示:
if jobTitle != nil {
var message = "Your job title is " + jobTitle!
}
!= 運算子表示「不等於」,因此當 jobTitle 不等於 nil,它必定有值。你可以執行 if 敘述中程式碼區塊的敘述,當你需要存取 jobTitle 的值時,可加入一個驚嘆號( ! )至可選型別變數的後面,這個驚嘆號是一種特別的指示符號,用來告知 Xcode:你確認這個可選型別變數有值,它是安全的,可以放心使用。
可選型別綁定
「強制解開」是存取可選型別變數值的一種方式,而另外一種方式稱為「可選型別綁定」(Optional Binding ),這是使用可選型別的推薦方式,至少你不需要使用「!」。
如果使用可選型別綁定,同樣的程式碼片段可以重寫如下:
if let jobTitleWithValue = jobTitle {
var message = "Your job title is " + jobTitleWithValue
}
你使用 if let 來找出 jobTitle 是否有值,如果有的話,這個值會被指定給臨時常數 jobTitleWithValue。在程式碼區塊中,你可以像平常一樣使用 jobTitleWithValue,如你所見,並不需要加入「!」這個後綴。
你是否必須給臨時常數一個新名稱?
不,其實你可以像這樣使用同樣的名稱:
if let jobTitle = jobTitle {
var message = "Your job title is " + jobTitle
}
Note: E即使名稱是相同的,上面的程式碼實際上有兩個變數。黑色字體的jobTitle 是可選型別變數,而藍色字體的jobTitle 是作為指定可選型別值的臨時常數。
這與Swift 可選型別有關。你是否被各種「?」與「!」符號感到困惑?我希望你已經沒有問題了,如果你還是不了解可選型別,可將你的問題刊登到我們的臉書社團 (https://facebook.com/groups/appcoda)。
好的,你還記得在圖 2.16 的警告提示嗎?當你試著輸出 meaning,Xcode 會給你一些警告。在主控台中,即使輸出了該值,它會以「Optional」為前綴,如圖 2.20 所示。
現在你已經知道原因為何了吧?為何這個 meaning 變數是一個可選型別呢?那麼該如何修改程式來移除這個警告訊息呢?
同樣的,先不要看解答,自行思考一下。
當你仔細看一下程式碼,meaning 實際上是一個可選型別,這是因為字典可能沒有給定鍵的值,例如:如果你在Playground 中編寫程式碼如下:
這個 meaning 變數會被指定為nil,因為emojiDict 並沒有鍵「 😍 」的值。
因此,每當我們需要存取 meaning 的值時,則必須先驗證其是否有值。想要避免產生警告訊息的話,我們可以使用可選型別綁定來測試值是否存在,如圖 2.21 所示。
變更完成後,這個警告訊息便會消失,你也會注意到顯示在主控台中的值,不再以「Optional」為前綴。
玩玩 UI
在結束本章之前,我們來建立一些UI 元件。我們要做的是在視圖中顯示表情符號及其對應的意義,如圖 2.22 所示。
如同我在本章開頭所說,除了學習 Swift,你還需要熟悉 iOS SDK 提供的框架。而基本的框架之一是 SwiftUI,它可讓你建立互動式 UI。
你也可以使用 Playground 來探索一些 SwiftUI 框架提供的UI 控制元件。現在輸入下圖所示的程式碼,然後點擊「Play」按鈕來執行程式碼。
這應該可讓你體驗一下 SwiftUI。你剛才使用 SwiftUI 框架在畫面上渲染視圖和一些文字標籤。
「視圖」(View )是 iOS 中的基本UI 元素。你可以把它想成一個用於顯示內容的矩形區域。ContentView 是我們的通用 View 的自訂版本,在視圖中,我們加入兩個文字元件來顯示表情符號圖示與標籤,我們還更改背景視圖為橘色。
下列程式碼是用於在Playground 中預覽UI:
PlaygroundPage.current.setLiveView(ContentView())
這就是 SwiftUI 和 iOS SDK 的強大之處,它有大量的預建元素,並讓開發者使用幾行程式碼就能自訂它們。
我猜你可能尚未完全了解 SwiftUI 的程式碼。不用擔心 !我只是想快速介紹一下 SwiftUI,我們將在下一章中帶你了解一些最常見的 SwiftUI 元件。
本章小結
N現在你已經嘗試過Swift 了,感覺如何呢?喜歡嗎?我希望你覺得Swift 對初學者友好以及本章內容不會嚇到你學習App 開發。
接下來,我將教你使用SwiftUI 建立你的第一個App,你現在可以進入下一章。不過, 如果你想學習更多的Swift 程式語言,我建議你要看一下Apple 官方的Swift 程式語言指南》 (https://docs.swift.org/swift-book/).。你將學會這個語言的語法,了解函數、可選型別以及其他內容,但這不是立即要做的事情。
若是你迫不及待想要建立你的第一個App,則翻到下一章,之後再來閱讀《Swift 程式語言指南,你可以學習到更多關於Swift 的內容。
本文摘自《iOS 17 App程式設計實戰心法》(SwiftUI)》一書。如果你想更深入學習Swift程式設計和下載完整程式碼,你可以從 AppCoda網站 購買完整電子版。