WFU

2023年4月12日 星期三

CHAPTER 02 使用 Playground 來首次體驗 Swift

讀者:楊于葳




本文為《快速精通 iOS 16 程式設計:從零開始活用 Swift 與 SwiftUI 開發技巧》這本書的閱讀筆記與實作記錄,目的是記錄自己的學習過程,以及幫助想利用 Swift 從頭建立一個自己心目中的 App 的人。以下是「CHAPTER 02 使用 Playground 來首次體驗 Swift」的筆記內容。




開發 iOS App 之前需要先具備哪些技能?學習 Swift、學習 Xcode、了解軟體開發工具。


2.1 Swift的歷史

在 Swift 發佈之前,iOS Apps 主要是以 Object-C 來撰寫,這個語言已經存在30多年了,並且被 Apple 視為 macOS 的主要開發語言。2014年Apple推出一個名叫 Swift 的新程式語言,標榜「快速、現代、安全、互動」,更容易學習而且讓程式開發更有效率,一旦習慣了 Swift 程式語言,就很難再換回 Object-C 了。

如同英語字典一樣,幾乎所有的程式語言都會隨著時間而改變,使這個語言更強大,對開發者更友善。


2.2 開始學習Swift

Swift 比 Objective-C 來得更簡潔易懂,每一段敘述結束的地方都沒有@符號和分號,而且更能直覺猜到程式碼的含義。


2.3 在Playground中試驗Swift

建立一個 Playground 檔


我們將會在 Playground 中撰寫一些程式,目的是要體驗 Swift 程式,並學習它的基礎知識,這裡不會介紹所有的功能,而是將重點放在這些主題:

(1)常數(Constant)、變數(Variable),與型別推論(Type Inference)

(2)控制流程(Control Flow)

(3)集合型別(Collection Types),像陣列(Array)與字典(Dictionary)

(4)可選型別(Optional)


2.4 常數與變數


在 Swift 語言中,let 用來宣告常數, var 用來宣告變數,我們可以為常數與變數選擇任意的名稱。

如果把 x = y + 10 用程式碼表示的話,就會像這樣:


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


2.5 型別推論

Swift 提供一項叫「型別推論」的功能,如果我們沒有明確指定型別,編譯器能自動推斷出最合適的型別,幫助開發者撰寫出簡潔的程式。


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


在 Swift 中,「 let constant: Int 」裡,let 的後面可以接一個識別符(identifier),用來定義常數的名稱,這個名稱可以是任何我們想要的字母、數字和底線的組合,而名稱的第一個字不能是數字。這裡的 constant 指的是常數。

let 和 var 的後面都可以加上「 : 」和特定關鍵字指定型別,而特定關鍵字的名稱是區分大小寫的。例如:「 myConstant 」和「 MyConstant 」是不同的常數。

Int 表示這個變數 or 常數的型,是一種整數。 

Double 表示這個變數 or 常數的型,是一種小數。 
 
String 表示一串文字。 
 
Bool 意思是布林(boolean)值,表示 true 或 false。


2.6 處理文字

要宣告 String 型別的變數,要使用 var 關鍵字賦予變數一個名稱,並為變數指定初始文字,文字資料以雙引號(")來包圍。


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


在 Swift 中,可利用不同的運算子與函數操作字串:

+ 可以把兩個字串串在一起。

uppercased() 可以把字串轉換成大寫。

lowercased() 可以把字串轉換成小寫。

message.count 可以計算一個字串有幾個字元。 


書本單本價格39,共買5本,顯示總價格,以下程式碼輸入後會顯示錯誤訊息,是因為字串和數字不能直接相加。


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


遇到這種狀況,有兩種解決方式:

第一種:從整數型別轉換為文字型別。


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



第二種:使用字串插值(string interpolations),建立 totalPriceMessage 變數。


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


2.7 流程控制


if let 敘述是 Swift 控制程式流程的一種方式。

「如果決定明天要六點起床,你將會為自己煮一頓豐盛的早餐,否則就是到外面去吃早餐」, 這個條件可用程式表示:


var timeYouWakeUp = 6
if timeYouWakeUp == 6 {
    print("Cook yourself a big breakfast!")
} else {
    print("Go out for breakfast.")
}


當中的 = 屬於賦值運算子,== 屬於關係運算子。

關係運算子用於比較兩個值之間的關係,常見的關係運算子包括: >(大於)、<(小於)、>=(大於或等於)、<=(小於或等於)、== (兩個值相等)、 != (不等於)。

 在年底通常會有一筆年終獎金,你將規劃一趟旅行,規劃如下:

  • 如果你獲得10,000的獎金(或者更多),你將決定到巴黎或倫敦旅行。
  • 如果你獲得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.")
}


同時要指定兩種條件,可以使用 && 運算子。&& 是一個邏輯運算子(logical operator),代表「且」的意思,當左右兩邊的條件都成立時,結果會回傳 true,反之則為 false。

如果覺得 && 不夠直覺化,可以使用範圍運算子「 ... 」,定義下限到上限的範圍。 


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.")
}


2.8 陣列與字典


書裡面一再提到,Swift 是一種型別安全的語言,意思是能夠在編譯時檢查變數和運算式的型別是否一致,避免因不同型別間的運算或賦值而引發錯誤而引發的潛在問題。

(1)陣列


Swift 提供一個名為「陣列」的集合型別,讓我們可以在一個變數中儲存多個值,每個值用逗號分開,以方括號包裹起來,使用「下標語法」(Subscript Syntax)存取陣列元素,第一項為0,第二項為1,第三項為2,以此類推。

如果想把陣列每一個元素的值,輸出到主控台上,有一個比較簡潔的做法:


var bookCollection = ["Tool of Titans","Rework","Your Move"]
bookCollection[0]
bookCollection.append("Authority")
bookCollection.count
for index in 0...3 {
    print(bookCollection[index])
}


首先是指定範圍,從第一項到最後一項,也就是 0...3。接著利用迭代(iteration)重複執行一段程式碼,而 for-in 是一種迴圈,用來執行一段程式碼,直到滿足特定的條件才停止,是 Swift 語言中的一個迭代結構。

雖然指定範圍時,寫成 0...3 看起來很簡潔了,但是只要陣列的數量一更改,就必須要修正一次述職,十分不方便,因此可以再利用另一個更簡潔的方式指定範圍:


var bookCollection = ["Tool of Titans","Rework","Your Move"]
bookCollection[0]
bookCollection.append("Authority")
bookCollection.count
for index in 0...bookCollection.count - 1 {
    print(bookCollection[index])
}


使用 0...bookCollection.count - 1 ,就可以減少很多修改的時間,不管陣列項目數量是多少,程式都不會出現錯誤。


(2)字典


字典(dictionary)是另一個常見的集合型別,使用中括號 [ ] 定義,可以讓我們在一個變數或常數中儲存多個值。每個鍵(key)對應到一個值,鍵和值中間用冒號( : )分隔。

如果要讓ISBN作為書本的索引值,可以這樣宣告與初始化一個字典:


import UIKit
var bookCollectionDic = ["1328683788":"Tool of Titans","0307463745":"Rework","1612060919":"Authority"]
bookCollectionDic["0307463745"]
for (key, value) in bookCollectionDic{
    print("ISBN:\(key)")
    print("Title:\(value)")
}


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


實作:建立一個表情符號字典


在 Mac 上,按下 control + command + space 就會跑出輸入表情符號的虛擬鍵盤。


程式碼var emojiDict: [String:String] = ["👻":"Ghost",
                                  "💩":"Popp",
                                  "😤":"Angry",
                                  "😱":"Scream",
                                  "👾":"Alien minster" ]
var wordToLookup = "👻"
var meaning = emojiDict[wordToLookup]
wordToLookup = "👾"
meaning = emojiDict[wordToLookup]


var emojiDict: [String:String] 宣告了一個字典變數 emojiDict,它的鍵和值都是 String 類型。可以理解为,emojiDict 是一个包含字符串键和字符串值的字典。

var wordToLookup = "👻" 是指變數的定義,指定值為 "👻",代表著我們要查找的字典中的鍵。

var meaning = emojiDict[wordToLookup] 這行程式碼的意思,是從 emojiDict 字典中查找 wordToLookup 所代表的鍵,並將其對應的值賦值給變數 meaning。以這個例子來說,wordToLookup 的值為 "👻",所以 meaning 的值會被賦為 "Ghost"。

print(meaning) 的作用是將 meaning 變數的值在控制台印出來,也就是將 emojiDict 字典中 wordToLookup 這個 key 所對應的 value 印出來。


2.9 可選型別

為什麼 App 會很常閃退呢?一個比較常見的原因是,在運作期間,App 試著要存取一個沒有值的變數,所以發生了這個例外事件。Swift Optionals 的導入,可以協助程式設計師撰寫更好的程式,以避免 App 發生閃退。

可選型別的基礎觀念十分簡單,在存取一個沒有值的變數之前,Swift 會建議我們先做個檢查,我們必須要先確認它有存在一個值,才能繼續,如此就能避免閃退的發生。

到目前為止,我們建立的變數或常數都有一個初始值,初始值在 Swift 裡是必要的。一個非可選型別的變數,一定要有一個值。

技術上,可選型別只是 Swift 的其中一個型別,讓一個變數可以有指定的值,或者沒有值。如果要宣告一個變數為可選型別,可以在變數後面加上問號。


var jobTitle: String?


在 playgrounds 輸入這行程式碼,按下play按鈕後,右側的面板上會出現 nil 這個字。在程式設計中,nil 是一個表示「沒有值」的特殊常數。當變數或常數沒有被初始化或設置為 nil 時,讀取該變數或常數的值就會返回 nil。

在 Swift 語言中,nil 可以被用於任何可選值 (Optional Value) 的型別中,表示這個可選值目前沒有任何值。如果輸入下面這行,Xcode 會顯示一個錯誤的訊息,這是因為 jobTitle 是沒有值的。


var message = "Your job title is" + jobTitle


每當我們需要存取一個可選型別變數,Xcode 就會強迫執行確認可選型別是否有值,避免我們寫出有問題的程式。

if let 在 Swift 語言中,是一種對可選型別變數強制解開(forced unwrapping)的方式,以確保其值存在。它可以在可選型別變數有值時,將其解開unwrap)並綁定到一個新的變數中,以便在 if 語句塊中使用。如果可選型別變數為 nil,則 if 語句塊中的代碼將被跳過。這樣可以避免因為 nil 值造成的錯誤。

當 meaning 不為 nil 時,將其解包並指定給一個新的變數 let meaning,進而執行大括號內的程式碼。
如果 meaning 為 nil,則不會進入該程式碼。當 meaning 為非 nil 值時,會印出 meaning 的值。


2.10 玩玩UI

iOS SDK 指的是 iOS 軟體開發工具包(Software Development Kit),是蘋果公司提供的一套軟體開發工具,包含函式庫、應用程式介面(API)和範例程式碼等資源,幫助開發者快速地開發iOS應用。


使用 SwiftUI 渲染視圖



PlaygroundPage.current.setLiveView(ContentView())


上方這行程式碼是用於在 Playground 中預覽 UI。



2.11 本章小結


建立第一個 App 之前,建議要看一下Apple官方的程式語言指南,了解函數、可選型別以及其他內容,但這不是立刻要做的事。

同樣都是第二章,《iOS 12 App 程式開發實務心法》較為繁瑣,要理解的內容更多也有更細部的舉例,而在這本《快速精通 iOS 16 程式設計》精簡很多,可以更專注在體驗程式上,而不是延伸出更多問題點。