IOS11 APP『Swift4』實例說明 — 訂飲料App

訂飲料這App,其實不複雜,主要是要了解JSON的解析和如何使用Post與Put來串接後台 API,在這範例裡,我用了許多用不到的方法,當作了解與練習其他的功能,如檔案的讀取與寫入功能,還是使用enum與直接用String的方式判斷等等…。所以在看範例程式時,你可能會覺得這樣寫不是畫蛇添足嗎?呵呵,因為只是純粹為了練習。

檔案讀取說明(圖片以外的檔案):

Step1:將文字檔(txt)當直接拖曳到專案內,記得要選擇Copy與Target,Copy是會複製一份檔案到專案,不會因為拖曳的檔案刪除就無法使用,而Target是在生成App時,會將檔案加進去。

Step2:在程式中如何開啟呢?就需要呼叫Bundle.main.url()了,在IOS中URL所代表的是位置,可以是設備上的檔案,也可以是網路上的檔案,然而當路徑的檔案不存在時則會回傳nil,forResource就是要讀取的檔案名稱,,withExtension就是副檔名。需要注意一點,專案裡的檔案只能讀取不能修改。那如何讀取呢?就需要呼叫init(contentsOf urlL URL) throws,因為讀檔不一定會成功,如果失敗則需要如何處理,所以會加上try來處理Error Handling。如成功讀取則回傳回來的就是檔案內的資料。

Step3:回傳回來的資料是靠\n分行的,所以可以利用回傳資料的一個功能(components)來將\n移除,移除後的資料會回傳到一個陣列(Array),範例中利用components將\n移除後,再利用for in 將飲料名稱單獨取出儲存。

檔案寫入說明:

這功能在訂購飲料的App因該用不到,但是寫說有讀檔案就加個寫入檔案的功能吧,一般使用者的帳號密碼是記錄在後台Server或是手機上也有一份帳號密碼,讓使用者不需要每次都登入,在範例中使用user’s defaults database儲存修改密碼,每個App都有的property list,是會永久存在裝置上直到App移除,為什麼會用UserDefaults來儲存密碼呢?因為UserDefaults適合儲存較少資料,存取太多資料時,會花較久時間,然而訂購飲料通常是訂自己的,所以只需要儲存一組帳號密碼即可,因此添加了這個功能。

  • 寫入UserDefaults:寫入時要注意,最後要呼叫synchronize()將資料同步存入
  • 讀取UserDefaults:讀取時只需要使用對應的型別讀取,然後傳入哪一個Key的值需要被讀取即可。型別可支援Integer、Data、Dictionary、Array、Date、String與URL,範例中是用String讀取。
  • 移除UserDefaults:只需要呼叫removeObject(),並傳入要移除的key。

Picker View說明:

Step1:點選Storyboard,範例上是Main.storyboard,從Object Library拉一個Picker View到Cell(範例是OrderTableViewController的飲料Label旁)。

Step2:執行App時要Picker View顯示資料需要遵從UIPickerViewDelegate與UIPickerViewDataSource的協定(Protocol)。先選擇Picker View,然後按滑鼠右鍵拖曳到Controller,這時會顯示dataSource與delegate,請選擇dataSource連結到Controller,然後同樣的方式將delegate連結到Controller。

Step3:接下來切換檔案到剛剛連結的Controller file,範例是OrderTableViewController.swift,如同上面所說的需要遵從UIPickerViewDelegate與UIPickerViewDataSource的Protocol,所以要在Controller的Class前上增加繼承UIPickerViewDelegate與UIPickerViewDataSource。

Step4:增加繼承UIPickerViewDelegate與UIPickerViewDataSource之後會有錯誤,這是正常的,因為繼承UIPickerViewDataSource的Protocol,就需要實現UIPickerViewDataSource的功能(function)。所以按下Fix就會顯示你所要實現的Function。

  • func numberOfComponents(in pickerView: UIPickerView) -> Int { }:這是在這個Pick View上要顯示幾個Component,簡單說就是要有幾個類別的滾輪(Picker)。範例上只有一個飲料名稱,所以直接return 1就可。
  • func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {}:這是指在這個滾輪(Picker)上總共要顯示多少數量,簡單說就是有多少列。範例上就是所有飲料的數量。

Step5:接下來就是要顯示文字在滾輪(Picker)上了,第二個參數row就是現在要顯示的文字是在滾輪(Picker)上第幾個,如果剛剛的numberOfComponents()有設定超過1的話,這邊的第三個參數component就可以用來判斷每個component要顯示的文字。範例上就是回傳飲料的名稱。

Step6:如果想知道現在是第幾個被選到則可以用func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {}來得知。範例上會將選到飲料名稱儲存,並判斷是冷飲還是熱飲來限制只用者選擇冰度。

Segmented Controll說明:

  • Disable Segment或是Enable Segment:如果有5個Segment,但是要將其中一個設為不可選擇,那該如何做呢?

其實並不難,利用setEnabled()就可以達成,如要設Segment4為不可選擇,就直接將其設為False即可。

  • 得知Segment是Enable還是Disable:使用isEnabledForSegment()。
  • 更改Segmented Control的顏色:使用tintColor。
  • 隱藏Segmented Control的方式:使用isHidden。
  • Disable Segmented Control或是Enable Segmented Control的方式:使用isEnabled。

範例程式中有使用到setEnabled()、isEnabledForSegment()與tintColor,讓使用者選擇飲料時自動隱藏不能選的冰度,如選擇的飲料是熱飲,就無法選少冰。

網路上讀取JSON Data:

這因該是訂飲料App裡面的核心技術了,相對於其他的功能,這是比較複雜的難懂的,首先要有一個觀念,就是Json的解譯必須配合後台(Server)所提供Json資料的編譯,因此你必須要先知道後台提供你什麼樣Json格式的資料,才能夠在App上解析使用。所以範例的內容只能供參考,除非你的欄位與用的API與範例相同。

範例是使用SheetDB所提供的API將Google Sheets(試算表)內的資料轉換成Json。

當然你也可以使用其他的方式將Google Sheets的資料轉換成Json,如Google Apps Script 與Firebase等等…。

基本觀念介紹:

  • 將網址轉換成百分號編碼(Percent-Encoding),也稱作URL編碼(URL encoding),這是 URI 規範中的百分比編碼,也就是俗稱的 URI 編碼或 URL 編碼。確保網址是正確可以解譯的標準的網址。可以使用addingPercentEncoding()做轉換。再利用URL將字串轉換成網址。
  • 利用 URLSession.shared.dataTask() 在背景thread下載資料。
  • 解析Json可以使用網路上提供的工具或是網頁來解析,方便閱讀,如postman或是JSON Editor Online。

http://jsoneditoronline.org/

在Swift中Json解析,可以使用內建的jsonObject(),來解析取得的Json資料

範例裡的Json資料為陣列(Array)包物件(Object),其實這物件你可以當作與Swift的字典(Dictionary) 一樣,所以在解析的時候[[String: Any]]或是[[String: String]]都可以,因為所有的value都是String。

儲存資料時則可以利用struct 將Json所解析的物件一個一個生成,這樣方便閱讀程式看起來也簡潔,如範例利用struct OrderInformation {}生成所有訂單資料。

更新Table View:

更新UI要在Main thread處理,所以在抓取完資料後,在呼叫更新UI相關的程式時,必須要放入DispatchQueue.main.async。

上傳JSON Data(POST):

範例中使用SheetDB的POST API規範如下:

上傳必須先將httpMethod設為”POST”,然後告訴後台是什麼格式,所以要設定setValue(“application/json”, forHTTPHeaderField: “Content-Type”)。

這裡要注意的就是API提供的Post功能需要什麼設定與JSON的結構。在範例中使用的SheetDB在Post時JSON的結構為物件(Object)包陣列(Array),陣列(Array)再包物件(Object),而最外層的物件(Object)的Key為data,value為上傳資料的陣列(Array)。所以範例上利用Dictionary將上傳資料的變成陣列(Array)包物件(Object),然後在將這Dictionary當作Value使用。

上傳主要是利用URLSession.shared.uploadTask()在背景thread上傳資料。

上傳JSON Data(PUT):

範例中使用SheetDB的Put API規範如下:

如果要修改已經上傳過的資料必須先將httpMethod設為”PUT”,然後告訴後台是什麼格式,所以要設定setValue(“application/json”, forHTTPHeaderField: “Content-Type”)。

然而Put還需要知到要更新哪一個Row,所以必須在網址後依照API所規定的方式,提供要更新的Row是哪一個,所以範例中是以name為Column作為選擇更新的條件,然後再用訂購者為Value,更新整行。

這裡要注意的就是API提供的Put功能需要什麼設定與JSON的結構。在範例中使用的SheetDB在Post時JSON的結構為物件(Object)包陣列(Array),陣列(Array)再包物件(Object),而最外層的物件(Object)的Key為data,value為上傳資料的陣列(Array)。所以範例上利用Dictionary將上傳資料的變成陣列(Array)包物件(Object),然後在將這Dictionary當作Value使用。其實這邊與POST是相同的。

偵測搖晃手機:

偵測搖晃手機的功能其實很簡單,只要複寫func motionBegan()或是func motionEnded(),然後再判斷是否偵測到手機搖晃就可。

--

--