期末作業-Step3

接續前次練習,本次的目標是將飲料訂單資訊上傳到Airtable的訂單表中存放。

首先到Airtable上建立訂單表,表格名稱與格式可參照下方截圖

接著到Airtable API網站查看表單上傳範例與說明

從範例中我們需要取得下列資訊

  1. API網址列
  2. 上傳的標頭:含呼叫類型,驗證表頭與內文,上傳文件的類別
  3. 上傳的JSON編碼格式

接著一樣要取得token,範例如下

https://airtable.com/create/tokens

創建token時會產生一組id, 存下來就能上傳下載表單資料

我們新增playground腳本來測試資料上傳,首先依照JSON格式建立struct資料型別(value type為資料的集合,雖然特性很像class但不是物件喔)


struct OrderData: Codable {
let records: [Record]
}

struct Record: Codable {
let fields: Field
}

struct Field: Codable {
let username: String
let drink: String
let size: String
let ice: String
let sugar: String
let memo: String?
}

接著設定上傳用的Request Session


let api = "patWEGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
let url = URL(string: "https://api.airtable.com/v0/appFhXTZ/Order")!

// 建立Request連線表頭與內文(需編碼)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(api)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

// 將資料編碼後存入request.httpBody屬性內
let encoder = JSONEncoder()
let field = Field(username: "Jason Chen", drink: "Red Tea", size: "Big", ice: "Regular", sugar: "Regular", memo: "Good")
let orderData = OrderData(records: [Record(fields: field)])
let data = try? encoder.encode(orderData)
request.httpBody = data

最後來測試上傳

// 啟動連線
URLSession.shared.dataTask(with: request) { data, response, error in

// 這邊的data是指Server端回傳的資料,編碼後可知道上傳結果
if let data {
let decoder = JSONDecoder()
let content = String(data: data, encoding: .utf8)
print(content ?? "")
}

}.resume()

結果可能會有幾種狀況

  1. 驗證失敗:請確認是否有正確輸入token ID與表頭
  2. 格式不正確:請檢查JSON上傳的格式範例,資料要放在fields型別內
{
"records": [
{
"id": "recW1lKzvHV7An3iG",
"createdTime": "2023-07-16T02:11:16.000Z",
"fields": {}
}
]
}

上傳成功會得到伺服器回傳的值

{"records":[{"id":"recxyeHDKPbD53ZPz","createdTime":"2023-07-16T07:54:53.000Z","fields":{"username":"Jason Chen","drink":"Red Tea","size":"Big","ice":"Regular","sugar":"Regular","memo":"Good"}}]}

到Airtable上檢查,資料確定有填入

接著就是到Project中把設定值謄寫到Model中啦

//  Order.swift
// OrderDrink-API
//
// Created by Jason Chen on 2023/7/16.
//

import Foundation

struct OrderData: Codable {
let records: [OrderRecord]
}

struct OrderRecord: Codable {
let fields: OrderField
}

struct OrderField: Codable {
let username: String
let drink: String
let size: String
let ice: String
let sugar: String
let memo: String?
}

接著就到訂單通知視窗的Closure中把上傳設定補上,改寫前


@IBAction func order(_ sender: Any) {

let iceIndex = iceSegmentedControl.selectedSegmentIndex
let sugarIndex = sugarSegmentedControl.selectedSegmentIndex
let sizeIndex = sizeSegmentedControl.selectedSegmentIndex
if sizeIndex == 1 {
dollar = drink.dollar! + 20
} else {
dollar = drink.dollar!
}

let message = "您的訂單資訊如下\n\n \(drink.name!)\n 容量:\(size[sizeIndex])\n 甜度:\(sugar[sugarIndex])\n 冰塊:\(ice[iceIndex])\n $\(dollar)\n 備註:\(memoTextField.text!)"

let alertController = UIAlertController(title: "Order", message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { _ in
// 要改寫的部分
print("存檔")
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
alertController.addAction(okAction)
alertController.addAction(cancelAction)

present(alertController, animated: true, completion: nil)

}

改寫後多了一個物件方法UploadOrder.upload()來呼叫資料上傳,注意Closure內如果需要讀取外部的屬性跟變數,需要加上self, 我則是用[self]來統一取代


@IBAction func order(_ sender: Any) {

let iceIndex = iceSegmentedControl.selectedSegmentIndex
let sugarIndex = sugarSegmentedControl.selectedSegmentIndex
let sizeIndex = sizeSegmentedControl.selectedSegmentIndex
if sizeIndex == 1 {
dollar = drink.dollar! + 20
} else {
dollar = drink.dollar!
}

let message = "您的訂單資訊如下\n\n \(drink.name!)\n 容量:\(size[sizeIndex])\n 甜度:\(sugar[sugarIndex])\n 冰塊:\(ice[iceIndex])\n $\(dollar)\n 備註:\(memoTextField.text!)"

let alertController = UIAlertController(title: "Order", message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { [self] _ in
// 改寫後
let field = OrderField(username: "Jason Chen", drink: drink.name!, size: size[sizeIndex], ice: ice[iceIndex], sugar: sugar[sugarIndex], memo: memoTextField.text)

UploadOrder.upload(url: UploadOrder.url, api: UploadOrder.api, field: field)
print("存檔")
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
alertController.addAction(okAction)
alertController.addAction(cancelAction)

present(alertController, animated: true, completion: nil)

}

Closure使用參考資料

新增訂單上傳物件與方法

//  UploadOrder.swift
// OrderDrink-API
//
// Created by Jason Chen on 2023/7/16.
//

import Foundation

class UploadOrder {

static let api = "patWEGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
static let url = URL(string: "https://api.airtable.com/v0/appFhXTZ/Order")!
// 建立Request連線表頭與內文(需編碼)

let field = OrderField(username: "Jason Chen", drink: "Red Tea", size: "Big", ice: "Regular", sugar: "Regular", memo: "Good")

static func upload(url: URL, api: String, field: OrderField) {

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(api)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

// 將資料編碼後存入request.httpBody屬性內
let encoder = JSONEncoder()
let orderData = OrderData(records: [OrderRecord(fields: field)])
let data = try? encoder.encode(orderData)
request.httpBody = data

// 啟動連線
URLSession.shared.dataTask(with: request) { data, response, error in

// 這邊的data是指Server端回傳的資料,編碼後可知道上傳結果
if let data {
let decoder = JSONDecoder()
let content = String(data: data, encoding: .utf8)
print(content ?? "")
}

}.resume()

}

}

再次執行下訂作業,得到伺服器回傳資訊

{"records":[{"id":"recz6MBrysjlg4zrg","createdTime":"2023-07-16T09:21:45.000Z","fields":{"username":"Jason Chen","drink":"白玉歐蕾","size":"大杯","ice":"少冰","sugar":"微甜","memo":"Test"}}]}

更新表單資料,訂單資料上傳的部分大功告成。

--

--