第 2 章
使用 Playgrounds 來體驗 Swift

現在你已經設定完 iOS App 開發所需的環境,再繼續往下之前,我先回覆另一個初學者常見的問題。很多人會問我:「開發 iOS App 之前需要先具備哪些技巧」。簡單地說,可以歸納成以下三個部分:

  • 學習Swift - Swift 現在是撰寫 iOS App所推薦的程式語言。
  • 學習 Xcode - Xcode 是你設計 App UI、撰寫 Swift 程式與建立你的 App所需的開發工具。
  • 了解 iOS 軟體開發工具 - Apple 提供軟體開發工具,使開發者能夠更輕鬆地開發程式。這個套件內建一組軟體開發工具與API 可以讓開發者強化 iOS App 的開發能力。舉例而言,如果你要在你的 App 中呈現一個網頁,這個 SDK 提供內建的瀏覽器,可以讓你馬上在 App 中嵌入它。

你需要具備以上三項的相關知識,而且還有許多的東西要學習。不過別擔心,當你學習完本書之後,你將學習到這些能力。

我們開始說明關於Swift 的一些歷史吧。

2014 年的世界開發者會議(Worldwide Developer Conference ),Apple 推出了一個新的程式語言,稱作「Swift」,這讓所有的開發者大為吃驚。Swift 標榜是一個「快速、現代、安全、互動」的程式語言。這個語言很容易學習,而且內建許多功能,可以讓程式開發更有效率。

在Swift 宣布之前,iOS Apps 主要是以 Objective-C 來撰寫。這個語言已經用了將近20 年,被 Apple 選為 Mac 與iOS 的主要開發語言。我和許多熱衷 iOS 開發的程式設計師談過,絕大多數的人認為 Objective-C 難以學習,而且語法看起來很怪異。簡單地說,這些程式碼嚇跑了一些想學習 iOS 程式開發的初學者。

Swift 的推出,或許是 Apple 對這些看法的答案吧 !這個語法能夠更簡潔地閱讀。我從 Swift 推出 Beta 版時就開始學習,至今已經超過三年,我可以保證使用 Swift 的開發效率會更高,它絕對會加速開發過程。一旦你習慣使用Swift 程式,你可能再也回不去 Objective-C 了。

在我來看,Swift 將會吸引更多的網頁開發者或初學者來開發 App。若你是一個擁有任何一種腳本語言程式經驗的網頁開發者,你可以利用現有的專業技術來擴充在 iOS 的開發知識。對你而言,學習 Swift 會非常容易。這麼說好了,即使你是一個完全沒有程式開發經驗的初學者,以 Swift 來開發 App 的話,你會發現這個語言讓人感覺更友善且自在。

在 2015 年6 月,Apple 宣布了 Swift 2,並且將這個程式語言變成開源(open source )。這真是一件大事,自此之後,開發者使用這個語言開發了一些有趣且非常棒的開源專案。你不只可以使用 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 年,Apple 推出了 Swift 4 與 Xcode 9,並做了更多的強化與改善,最新的 Swift 版本能夠向下相容,這表示由 Swift 3 所開發的專案也可以在 Xcode 9 運作,而不會有所改變。若是你必須要做移轉,從 Swift 3 移轉到 Swift 4,會比 2.2 版移轉到 3,來得簡單許多。

2018 年末,Apple 針對 Swift 與 Xcode 10 的更新幅度不大,版本已經來到 4.2 版,儘管不是大幅度的變動,這個新版本還是做了許多語言效能提升的改善。

在 2019 年3 月, Apple 正式釋出 Swift 5,這也是這門程式語言的關鍵里程碑,這個版本加入了許多新的功能,最重要的變更是 Swift 即時運行(runtime )已經加入了Apple 平台的操作系統,其中包括 iOS、macOS、watchOS 與 tvOS。其實,對於開發者來說,這是一件好事,從某個角度來看,這表示 Swift 語言已經變得更穩定且成熟了。

今年,Apple 也釋出了 Swift 5.9,加入更多強大的功能,能夠編寫出更為清楚優質的程式碼。舉例,支援 regular expression、macros 和 if let 簡寫語法。

如果你完全是個初學者,你可能心中會有些疑問,為什麼 Swift 會不斷變更呢?如果它不斷更新,Swift 到底能不能使用?

幾乎所有的程式語言一段時間就會變更,Swift 也不例外,每年新的程式語言功能都會加入 Swift,使得這個程式語言更強大,並且對開發者更友善。這和我們的語言一樣,例如:英語,也是每隔一段時間就會有所變更,如freemium(免費加值)這樣的單字與片語每年都會加入字典中。

所有的程式語言一段時間就會做更新,理由有很多種,就和英語一樣。

出處: https://www.english.com/blog/english-language-has-changed

雖然 Swift 不斷進化,但這並不表示它還不能被真正運用。當你想建立一個 iOS App, 你應該使用 Swift。事實上它已經成為 iOS App 的開發標準, LinkedInDuolingoMozilla h從最初的版本都已經完全使用 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)

第一段程式碼是以 Objective-C 來撰寫的,第二段則是使用 Swift 程式語言,以上哪一種語言你比較喜歡呢?我猜你可能比較喜歡使用Swift 來編寫,特別是當你對 Objective-C 語法感到失望的話。Swift 比較清楚易於閱讀,每一段敘述結束的地方都沒有@的符號與分號。以下這兩段敘述是將第一段與第二段訊息串接在一起,我相信你已經猜到下列這段 Swift 程式碼的意義了。

var message = firstMessage + secondMessage

而你恐怕會對下列的 Objective-C 程式碼感到困惑吧:

NSString *message = [NSString stringWithFormat:@"%@%@", firstMessage, secondMessage];

Playgrounds中測試 Swift

我不想直接列出程式碼來讓你感到厭煩,而了解程式的最好方法就是實際撰寫程式。Xcode 有一個內建的功能稱作「Playgrounds」,它是一個讓開發者能夠實驗 Swift 程式語言的互動開發環境,可以讓你即時見到程式運作後的結果。待會你將會了解我的意思以及 Swift Playgrounds 的運作方式。

假設你已經安裝完 Xcode 15(或以上的版本),啟動這個應用程式(點選 Launchpad 的 Xcode 圖示),然後你應該會看見一個啟動對話視窗,如圖 2.1 所示。

圖 2.1. 啟動對話視窗
圖 2.1. 啟動對話視窗

Playground 是 Xcode 檔的特別型態。要建立 Playground 檔的話,你只要至 Xcode 選單, 並選取 File-> New ->Playground,就會出現一個選擇模板的提示。由於我們的重點是在 iOS 環境中探索Swift,因此在「iOS」區塊下,選取「Blank」來建立一個空白檔,接著點擊「Next」來繼續,如圖 2.2 所示。

圖 2.2. 建立一個Playground 檔
圖 2.2. 建立一個Playground 檔

一旦你要確認儲存這個檔案,Xcode 開啟 Playground 介面。你的畫面看起來像這樣:

圖 2.3. The Playground 介面  左側面板是讓你輸入程式碼的編輯區。當你想要在上面測試你的程式碼時,按下「Play」按鈕,Playground 會立即解譯你的程式碼(執行程式到「Play」按鈕上方那一行), 並將結果顯示在右側的面板中。Playground 中有預設兩行的程式碼,如你所見,在你按下第 4 行的「Play」按鈕後,str 變數宣告的結果立即出現在右側面板中。

我們將會在 Playground 中撰寫一些程式。請記住這個練習的目的是要讓你體驗 Swift 程式,並學習它的基礎。我不會介紹所有的 Swift 特點,而是將重點放在這些主題:

  1. 常數(constant)、變數(variable)與型別推論(type inference)。
  2. 控制流程(control flow)。
  3. 集合型別(collection types),如陣列(array)與字典(dictionary)。
  4. Optionals

這些都是你所需要知道有關 Swift 的基本型別。你將透過例子來學習,但我確信你將會對一些程式語言的觀念感到困惑,尤其是當你完全沒有學過程式的話。不過,不用擔心 ! 你將會在一些章節裡發現到我所提供的一些建議,只要遵循我的建議,然後持續學習即可。另外,若是你在學習上遇到障礙,記得要休息一下。

酷 !讓我們開始吧 !

常數與變數

常數與變數是兩個在程式中的基本元素。變數(或是常數)的觀念和你在數學所學的一樣。如下所示:

x = y + 10

在這裡,xy都是變數。10 是一個常數,表示值是沒有改變的。

在 Swift,你以 var 關鍵字來宣告一個變數,另外使用 let 關鍵字來宣告一個常數。當你將上列的方程式寫成程式,看起來如下所示:

let constant = 10
var y = 10
var x = y + constant

試著在 Playground 輸入這些程式碼,然後按下第5 行的「Play」按鈕,結果如圖 2.4 所示。

圖 2.4. 方程式的結果
圖 2.4. 方程式的結果

你可以任意命名變數與常數名稱,只要確認它們是有意義的命名就好。例如:你可以將同樣的程式撰寫如下:

let constant = 10
var number = 10
var result = number + constant

為了讓你對 Swift 的常數與變數能更清楚了解,輸入下列的程式碼來變更 constantnumber 的值:

constant = 20
number = 50

接著,按下 shift+command+enter 鍵來執行程式,除了使用「Play」按鈕以外,你可以使用這個快捷鍵來執行程式。

你只是設定新值給常數與變數,不過當你變更常數的值,Xcode 會出現錯誤,number 反而沒有問題,如圖2.5 所示。

圖 2.5.  Playgrounds 中的錯誤提示
圖 2.5. Playgrounds 中的錯誤提示

這是 Swift 中常數與變數的主要差異。當常數以一個值來做初始化,你不能改變它,如果初始化後還想變更其值的話,則使用變數。

了解型別推論

Swift 提供開發者許多功能來撰寫簡潔的程式。其中一項功能是「型別推論」。以上這段相同的程式可以明確地撰寫如下:

let constant: Int = 10
var number: Int = 10
var result: Int = number + constant

每一種 Swift 中的變數有一種型別,在 : 後的關鍵字 Int,表示這個變數/常數的型別是一種整數,如果儲存的值是小數,我們使用 Double 型別。

var number: Double = 10.5

還有其他型別如 String,是用在文字型別的資料,而 Bool 則是布林(boolean )值(true / false )。

現在回到「型別推論」,在Swift 中它是一個強大的功能。當你在宣告一個變數/常數時,可以讓你省略型別。Swift 的編譯器可以檢查你所提供的預設值,來推論出其型別為何。這也是為何我們能像以下這樣,將程式寫得更簡單的原因。

let constant = 10
var number = 10
var result = number + constant

這裡所給定的值(也就是 10 )是一個整數,所以其型別會自動設定為Int。在 Playgrounds 中,你可以按住option鍵,然後點選任何一個變數名稱,來揭示由編譯器所推論出的變數型別,如圖 2.6 所示。

圖 2.6. 按住 option 鍵並選取這個變數來揭示其型別
圖 2.6. 按住 option 鍵並選取這個變數來揭示其型別

被一些程式的觀念壓得喘不過氣嗎?

先休息一下!不需要一氣呵成來看完整個章節。若是你等不及想要馬上建立一個App,你可以跳過此章,直接進入到下一章,或是你也可以隨時回來了解Swift 的基礎。

文字的處理

到目前為止,我們認識了 IntDouble 型別的變數。想要在變數中儲存文字資料, Swift 提供了一個名為String 的型別。

要宣告一個 String 型別的變數,你使用 var 關鍵字,賦予這個變數一個名稱,並指定初始文字給它。所指定的文字以雙引號( " )來包圍,如下列的例子:

var message = "The best way to get started is to stop talking and code."

在 Playgrounds 輸入以上的程式之後,按下「Play」按鈕,你將會在右側面板看到結果, 如圖 2.7 所示。

圖 2.7. 字串立即顯示在右側面板
圖 2.7. 字串立即顯示在右側面板

Swift 提供不同的運算子與函數(或是方法)來讓你操作字串。例如:你可以使用 + 運算子來將兩個字串串接在一起,如圖 2.8 所示。

var greeting = "Hello "
var name = "Simon"
var message = greeting + name
圖 2.8. 字串串接
圖 2.8. 字串串接

如果你想要將整個句子轉換成大寫呢? Swift 提供了一個內建的方法, 稱作 uppercased(),可以將一個字串轉換成大寫。你可以輸入下列的程式碼來做個測試:

message.uppercased()

Xcode 的編輯器有自動完成功能,自動完成功能是一個很方便的功能,它能夠加快程式撰寫速度。當你輸入mess,你會見到一個自動完成視窗,依照你所輸入的文字而提供對應的建議。你只需要選擇 message,並按下enter 鍵即可,如圖 2.9 所示。

圖 2.9. 自動完成功能

Swift 採用點語法來存取內建的方法以及一個變數的屬性。當你在 message 之後輸入點符號,自動完成視窗會再次彈出,它會建議一串可以由變數所存取的方法與屬性。你可以持續輸入 uppercase(),或者從自動完成視窗中來選取,如圖 2.10 所示。

圖 2.10. 方法清單 當你輸入完成之後,你會馬上見到輸出結果。當我們針對 message 使用 uppercased(), 這個 message 的內容會自動轉換為大寫。

uppercased() 是字串所內建的功能之一,你可以使用 lowercased() 來將 message 轉換成小寫。

message.lowercased()

或者,如果你想要計算一個字串的字元有幾個,程式可以這樣寫,如圖 2.11 所示。

message.count
圖 2.11. 使用內建的函數來操作字串
圖 2.11. 使用內建的函數來操作字串

字串的串接看起來非常簡單,對吧?你只要使用 + 運算子就可以將兩段字串相加。不過,並不是一切都這麼單純,我們在 Playgrounds 寫下這些程式:

var bookPrice = 39
var numOfCopies = 5
var totalPrice = bookPrice * numOfCopies
var totalPriceMessage = "The price of the book is $" + totalPrice

將字串與數字混合在一起的情況很常見。在以上的例子中,我們計算書的總價,並建立一個訊息告訴使用者總價為何,如果你在 Playgrounds 輸入這些程式,你會注意到一個錯誤。

圖 2.12. 除錯區/主控台
圖 2.12. 除錯區/主控台

當 Xcode 在程式中找到一個錯誤,這個錯誤會以一個紅色的叉號,加上一段錯誤訊息的提示,有時候 Xcode 會顯示出這個錯誤可以修復的可能方案,有時候則不會。

除了錯誤的指示外,你應該會在除錯區/主控台中見到錯誤細節。如果你的 Playground 沒有見到這個主控台,則點擊左下角的顯示/ 隱藏除錯區按鈕,如圖 2.12 所示。此外,你也可以至 Xcode 選單並選取 View -> Debug Area -> Activate Console 來打開它。

在我告訴你如何解決這個問題之前,你可以先花個幾分鐘來想一下。

首先,要記得的是,Swift 是型別安全( type-safe )的語言。這表示每一個變數都有一個型別,指出是哪一種值被儲存。你知道 totalPrice 的型別是什麼?記得稍早我們所學習的,Swift 可以透過值的檢查來判斷變數的型別。

因為 39 是一個整數,Swift 判斷 bookPrice 的型別是 IntnumOfCopiestotalPrice 也是。

在主控台中顯示的錯誤訊息會提到這個+ 運算子,不能串接一個 String 變數與 Int。這兩個型別必須要相同才行。換句話說,你必須要先將 totalPriceInt 轉換成 String

你可使用下列的程式碼,來將整數轉換成字串:

var totalPriceMessage = "The price of the book is $" + String(totalPrice)

還有一種方式叫做「字串插值」(String Interpolations )也可以辦到。你可以像這樣來建立 totalPriceMessage 變數:

var totalPriceMessage = "The price of the book is $ \(totalPrice)"

字串插值是在多個型別中建立一個字串所比較推薦的方式。你可以用括號將變數包覆起來,前面加上一個反斜線來進行字串轉換。

變更完成之後,按下「Play」按鈕並重新執行這段程式,錯誤應該會被修正。

流程控制

關於自信心,我認為你們會意識到,你們的成功不僅僅是因為你們自己所做的一切, 而是因為在朋友的幫助下,你們不害怕失敗。如果真的失敗了,爬起來再試一次。如果再次失敗,那就再爬起來,再試一次。如果最後還是失敗了,或許該考慮做些其他的事情, 你們能夠站在這裡,不僅是因為你們的成功,而是因為你們不會害怕失敗

- John Roberts,美國首席大法官

出處: http://time.com/4845150/chief-justice-john-roberts-commencement-speech-transcript/

每一天,我們做了很多決定,不同的決定會有不同的結果或動作。舉例而言,當你決定明天要六點起床,你將會為自己煮一頓豐盛的早餐,否則的話,就是到外面去吃早餐。

寫程式時,你會使用到 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 所示。

圖 2.13. if 敘述的例子
圖 2.13. if 敘述的例子

在 Playgrounds,你會在主控台中見到「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 ),表示「大於或等於」。第一個條件檢查 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。針對第一個case,10000... 表示一個值是大於10000。

這兩段程式都是相同的,但是你比較喜歡哪種方式呢?這種情況下,我比較喜歡 switch 敘述,可以讓程式更簡潔。不管如何,即使你比較喜歡使用 if 來處理以上的問題,結果是一樣的。當你繼續探索 Swift 程式語言,你將會了解 ifswitch 的使用時機。

了解Array與字典

現在你已經對變數與控制流程有了基本了解,我們來介紹另一個常會用到的程式觀念。

到目前為止,我們使用的變數只能儲存一個值。參考一下我們稍早的程式— bonustimeYouWakeUptotalPriceMessage,不論變數型別為何都只可以存放一個值。

我們以這個範例來看,想像你正在建立一個書架的應用程式,這個應用程式可以將你所蒐藏的書分門別類。在你的程式中,你可能會有一些變數存放你的書的標題:

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]

如果在 Playgrounds 輸入上列的程式碼,並且按下「Play」按鈕的話,你應該會在輸出面板見到「 Tool of Titans」。

當你以 var 宣告一個陣列,你可以修改它的元素。例如:你可以像這樣呼叫 append 這個內建的方法,來加入一個新的項目至陣列中:

bookCollection.append("Authority")

現在陣列有4 個項目。那麼該如何知道一個陣列的全部數呢?使用內建的 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)來做迭代( iterate )。在這個情況下,for 迴圈內的程式碼總共執行 4 次。而index 的值會跟著做變更。當 for 迴圈第一次開始執行,index 的值設為 0,它會輸出 bookCollection[0]。當敘述執行完成之後,index 的值會更新為1,接著會輸出 bookCollection[1]。整個過程重複持續直到所設的範圍(也就是 3)為止。

現在問題來了,如果陣列中有 10 個項目該怎麼做呢?你也許會將範圍從 0...3 改為 0...9。那麼如果項目增加到 100 個呢?那麼你會將範圍改成 0...99

有沒有更合適的方法,不需要每次數量改變都要更新程式一次呢?

你是否注意到 0...30...90...99 等這些範圍的模式嗎?

範圍的上限等於全部項目數減一。其實你可以將程式重寫如下:

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 ),這裡不使用一個索引( index )來識別一個項目, 你可以使用唯一的 key 來存取項目。

讓我繼續使用這個 book 集合作為例子。每一本書有一個唯一的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 迴圈,如圖 2.14 所示。

for (key, value) in bookCollectionDict {
    print("ISBN: \(key)")
    print("Title: \(value)")
}
圖 2.14. 對一個字典做迭代
圖 2.14. 對一個字典做迭代

你可從主控台中的訊息看出,項目的排序並沒有依照初始設定的順序,和陣列不一樣,這是字典的特性,項目是以無排序的方式來儲存。

你可能想說,建立一個 App 時,何時會用到字典。我們以另外一個例子來看,它之所以會稱作「字典」的理由。思考一下你是如何使用字典呢?在字典中查詢一個字,它會標示文字的意義。在這種情況下,這個文字就是鍵,而它所代表的意義也就是值。

在進入到下一節之前,我們以一個很簡單範例來建立一個表情符號字典,這個字典存放表情符號的意義,為了簡單點,這個字典內有下列幾個表情符號與其意義:

  • 👻​ - Ghost
  • 💩 - Poop
  • 😤​ - Angry
  • 😱​ - Scream
  • 👾​ - Alien monster

你知道如何使用字典型別來實作表情符號字典呢?以下是表情符號字典的程式架構, 請填入缺少的程式,來讓它能夠運作:

var emojiDict = // 填入初始化字典的程式 //

var wordToLookup = // 填入鬼臉表情符號 //
var meaning = // 填入存取值的程式 //

wordToLookup = // 填入生氣表情符號 //
meaning = // 填入存取值的程式 //

要在Mac 上輸入表情符號,按下 control+command+space 鍵

你能夠完成這個練習嗎?

我們來看圖2.15 的解答以及其輸出結果。

圖 2.15. 表情符號字典練習的解答
圖 2.15. 表情符號字典練習的解答

我相信你已經自己找出解答了。

現在我們加入幾行程式碼,來輸出 meaning 變數至主控台,如圖 2.16 所示。

圖 2.16. 輸出meaning 變數
圖 2.16. 輸出meaning 變數

你會注意到兩件事:

  1. Xcode 指出這兩個輸出的敘述都有些問題。
  2. 主控台區的輸出和我們之前經歷過的有所不同。結果是正確的,但是 「Optional」是什麼意思呢?
Note: 在 Xcode 中,警告是以黃色來標示。警告和錯誤的差異在於,即使有些警告,你的程式還是能夠運作,如同名稱所建議的,警告是預先告知你有某些問題。你最好能夠修復這些問題,以免有潛在的問題。

這兩個問題都和Swift 中的一個稱作「Optionals」的新觀念有關。即使你有一些程式背景,對你而言,這個觀念可能是新的。

我希望你能夠喜歡到目前為止的內容。如果有任何問題而卡住的話,不妨休息一下。喝杯咖啡來放鬆心情,或者你可以跳過這個章節剩下的部分,然後進到下一章來試著建立你第一個 App,你隨時可以回來複習本章。

了解Optionals

你有過這樣的經驗嗎?你開啟一個 App,按一個按鈕,然後突然就閃退,我相信你應該都碰過。

為什麼 App 會很常閃退呢?一個比較常見的原因是,在運作期間,App 試著要存取一個沒有值的變數,所以便發生了這個例外事件。

那麼是否有任何方式可以避免閃退呢?

不同的程式語言有不同的策略,來鼓勵程式設計師寫一些好的或者不易出錯的程式碼。Swift Optionals 的導入,可協助程式設計師撰寫更好的程式,以避免 App 閃退的方式。

一些開發者很難以理解 Optionals 的觀念。它的基礎觀念其實十分簡單,在存取一個沒有值的變數之前,Swift 會建議你先做個檢查,你必須要先確認它有存在一個值才能繼續,如此才能避免閃退的發生。

直到目前為止的內容,所有我們建立的變數或常數都有一個初始值,這在 Swift 是必要的。一個非 Optional 的變數一定要有一個值。當你試著宣告一個變數而沒有賦值的話, 你會得到錯誤,你可以在 Playgrounds 中做個測試,試試看會發生什麼結果,如圖 2.17 所示。

圖 2.17.  宣告一個變數/常數而沒有賦予初始值
圖 2.17. 宣告一個變數/常數而沒有賦予初始值

在某些情況下,你需要宣告一個沒有初始值的變數。想像你正在開發一個有註冊形式的 App,並不是所有的欄位都是必填,有一些欄位(如工作職稱)是可以選填的。而在這種情況下,那些欄位可以不需要任何值。

技術上,Optional 只是 Swift 中的一個型別,這個型別指出,一個變數可以有指定值或沒有值。要宣告一個變數為 Optional,你可以在變數後面加上問號( ? )。如下所示:

var jobTitle: String?

你宣告一個名稱為 jobTitle 字串型別的變數,它也是一個 Optional,如果你將上面這行程式輸入 Playgrounds 中,將不會出現錯誤,因為 Xcode 知道 jobTitle 這個變數可以沒有值。

如果是非 Optional 型別變數,編譯器能夠由變數的初始值推導出其型別,Optional 變數則必須清楚地指定它的型別(例如:StringInt )。

若是你已跟著我的指示,在 Playgrounds 輸入這些程式碼,並且按下「Play」按鈕,你也許會注意到 nil 顯示在結果的面板上。針對任一個沒有值的 Optional 變數,可指定一個名為 nil 的特別值給它,如圖 2.18 所示。

圖 2.18. 針對無值的 Optional 變數,指定一個特別的「nil」值
圖 2.18. 針對無值的 Optional 變數,指定一個特別的「nil」值

換句話說,nil 表示變數沒有值。

當你需要指定一個值給一個 Optional 變數,你可以像往常一樣指定,如下所示:

jobTitle = "iOS Developer"

現在你應該對 Optional 有些概念了,而我們該如何利用它來幫助我們寫出更好的程式呢?

試著如圖 2.19 來輸入程式。

圖 2.19. 當你存取一個Optional 變數時發生了錯誤
圖 2.19. 當你存取一個Optional 變數時發生了錯誤

當你一輸入完下列的程式,Xcode 會指示一個錯誤訊息。

var message = "Your job title is " + jobTitle

這裡的 jobTitle 被宣告為一個Optional 變數。Xcode 告訴你該行程式有潛在的錯誤,因為 jobTitle 應該是沒有值。你必須在使用 Optional 變數之前要先做個檢查。

這是 Optionals 如何避免你寫出有問題的程式的方法。每當你需要存取一個 Optional 變數,Xcode 會強迫你執行確認來看這個 Optional 是否有值。

強制解開

而你該如何執行這樣的確認,並解開(unwrap )Optional 變數的值呢? Swift 提供幾個方法。

首先,即所謂的「 if 敘述與強制解開」( forced unwrapping )。簡單地說,你使用 if 敘述結合 nil 做判斷,即可檢查這個 Optional 變數是否有值。若這個 Optional 真的有值,你可以釋放出它的值來做進一步的處理。程式如下所示:

if jobTitle != nil {
    var message = "Your job title is " + jobTitle!
}

這個 !=運算子表示「不等於」,因此當 jobTitle 不等於 nil,它必定有個值。你可以執行 if敘述內的程式。當你需要存取 jobTitle 的值的時候,你必須要加入一個驚嘆號(! )至 Optional 變數的後面。這個驚嘆號是一種特別的指示符號,用來告知 Xcode,你確認這個 Optional 變數有個值,它是安全的,可以放心使用。

Optional 綁定

強制解開是存取一個 Optional 變數值的一種方式。而另外一種方式稱作「Optional 綁定」(Optional Binding ),這是比較推薦處 Optionals 的方式,至少你不需要使用 !

如果使用Optional 綁定,同樣的程式可以重寫如下:

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: 即使名稱是相同的,上面的程式實際上有兩個變數。黑色字體的jobTitle 是 Optional 變數,而藍字體的 jobTitle 是作為被指定 Optional 值的暫時常數。

以上就是 Swift Optionls 的相關內容。你是否仍對「?」與「!」的符號感到困惑呢?我希望你已經沒有問題,若是你還是不了解 Optional 的話,則可以將你的問題刊登在我們的臉書社團(https://facebook.com/groups/appcoda).

好的,你還記得在圖 2.16 的警告提示嗎?當你試著輸出 meaning,Xcode 會給你一些警告。在主控台中,即使值已經輸出了,它在前面會寫著「Optional」,如圖 2.20 所示。

圖 2.20. 和圖 2.16 一樣出現了警告訊息
圖 2.20. 和圖 2.16 一樣出現了警告訊息

現在你已經知道原因為何了吧?為何這個 meaning 變數是一個 Optional 呢?那麼該如何修改程式來移除這個警告訊息呢?

同樣的,先不要看解答,自行思考一下。

當你仔細看一下程式, meaning 實際上是一個 Optional。這是因為字典針對所指定的鍵可能沒有對應的值。例如:如果你在 Playgrounds 將程式寫成這樣:

這個 meaning變數會被指定為 nil,因為 emojiDict 並沒有針對鍵​ ​😍​ 的對應值。

因此,每當我們需要存取 meaning 的值時,則必須先檢查是否有值。想要避免產生警告訊息的話,我們可以使用 Optional 綁定來測試值是否存在,如圖 2.21 所示。

圖 2.21. 使用Optional 綁定來檢查 meaning 是否有值,然後解開它
圖 2.21. 使用Optional 綁定來檢查 meaning 是否有值,然後解開它

變更完成之後,這個警告訊息便會消失,你也會注意到顯示在主控台中的值,前面不再加上 Optional 了。

玩玩 UI

在結束本章之前,我們來點好玩的,建立一些 UI 元件。我們準備要做的是,在一個視圖( View )中顯示一個表情符號的圖示以及它對應的意義(如圖 2.22 所示),取代輸出在主控台中。

圖 2.22. 在視圖中顯示表情符號
圖 2.22. 在視圖中顯示表情符號

如同我在本章一開始所說明的,為了建立 App,則除了學習 Swift 之外,你還需要熟悉 iOS SDK。

你也可以使用 Playgrounds 來探索一些由 iOS SDK 所提供的 UI 控制( controls )。視圖是在 iOS 中的基本 UI 元件,你可以把它想成是一個用來顯示內容的矩形區域。現在,至你的 Playground 中輸入下列的程式碼,然後按下「Play」按鈕來執行:

你應該對前面幾行很熟悉了,我們將重點放在程式的最後兩行。要建立一個視圖,你可以它的大小來實例化(instantiate )一個 UIView 物件。這裡我們將寬度與高度分別設為 300 點,而 x 與 y 是指定其位置。程式的最後一行則將視圖的背景顏色改為「橘色」。

要在 Playgrounds 中預覽 UI,你可以點擊「快速預覽」(Quick Look)或者「顯示結果」( Show Result )圖示。「快速預覽」功能以彈出的方式顯示這個標籤,若你是使用「顯示結果」功能,Playground 會將視圖顯示在程式內,位於你的程式的下方,如圖 2.23 所示。

圖 2.23. 快速預覽與顯示結果 現在這個視圖(即 containerView )是空的。如果你能夠加入表情符號至視圖中,是不是更好呢?

繼續輸入下列的程式碼,並點擊「顯示結果」(Show Result )圖示。

let emojiLabel = UILabel(frame: CGRect(x: 95, y: 20, width: 150, height: 150))
emojiLabel.text = wordToLookup
emojiLabel.font = UIFont.systemFont(ofSize: 100.0)

containerView.addSubview(emojiLabel)

表情符號只是一個字元。在 iOS 中,你可以使用標籤( label )來顯示文字。要建立一個標籤,你可以所想要的大小來實例化一個 UILabel 物件。這裡的標籤是設定為 150×150 點(point )。而 text 屬性則是包含了要顯示在標籤上的文字。為了讓標籤更大一些,你可以變更字體( font )屬性,來採用較大的字體大小。最後,要將標籤顯示在 containerView,你要呼叫 addSubview 來把標籤加入視圖中。

繼續輸入下列的程式碼,來加入另外一個標籤:

let meaningLabel = UILabel(frame: CGRect(x: 110, y: 100, width: 150, height: 150))
meaningLabel.text = meaning
meaningLabel.font = UIFont.systemFont(ofSize: 30.0)
meaningLabel.textColor = UIColor.white

containerView.addSubview(meaningLabel)

執行程式之後,點選「Show Result」按鈕來檢視結果,如圖 2.24 所示:

圖2.24. 書呆子表情符號顯示在視圖中
圖2.24. 書呆子表情符號顯示在視圖中

這就是 iOS SDK 的強大之處。它內建大量的預建元素,讓開發者透過幾行程式就可以自訂它們。

不過,不要誤會了,本書之後的內容,並不需要你寫程式來建立使用者介面。Xcode 提供一個介面建構器(Interface Builder )的功能,可以讓你以拖放的方式來設計 UI。我們下一章會進一步做介紹。

現在你已經嘗試過 Swift 了,感覺如何呢?喜歡嗎?我希望你發現 Swift 更容易學習與撰寫。最重要的是,我不希望嚇跑你,使你不學習 App 的開發。

下一章的課程建立第一個 App

接下來,我將介紹如何建立你的第一 個App,你現在可以馬上進入到下一章。然而,當你學習更多的 Swift 程式語言,我建議你要看一下 Apple 官方的程式語言指南(https://docs.swift.org/swift-book/),你將學會這個語言的語法,了解函數( function )、Optionals 以及其他內容。

但這不是馬上需要做的工作。

若是你等不及要馬上建立你的第一個App,則翻到下一章,之後再來閱讀這個Swift 程式語言指南,你可以學習到更多關於Swift 的內容。


本文摘自《iOS 17 App程式設計實戰心法》(Swift+UIKit)》一書。如果你想更深入學習Swift程式設計和下載完整程式碼,你可以從 AppCoda網站 購買完整電子版。

results matching ""

    No results matching ""