【 iOS Swift 】#13–4 可不可飲料訂購APP (POST API上傳訂購資料)

續上一集,本集將介紹如何讓使用者填寫訂單,並上傳訂購資訊到AirTable,成品如下:

StoryBoard架構如下:

AirTable

這時AirTable也需要準備一個檔案,專門存放我們上傳的訂單資訊

同時,把需要的欄位都製作好:

DetailViewController

選定飲料後的詳細選單,可選擇容量、冰塊、甜度、加料以及訂購人姓名

其中,接收上一頁傳來的飲料資訊:

//上一頁傳來的飲料資訊
let detailDrink: Fields

required init?(coder: NSCoder, detailDrink: Fields) {
self.detailDrink = detailDrink
super.init(coder: coder)

}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

訂購按鈕預設顯示中杯價錢:

//飲料容量價錢
var selectSizePrice: Int = 0

//飲料加料價錢
var selectExtraPrice: Int = 0

//飲料圖片網址
var imgString: String!


//顯示選定飲料的詳細資訊
func mainUI(){

navigationItem.title = detailDrink.fields.drinks

imgString = detailDrink.fields.img_url
let imgUrl = URL(string: imgString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)

detailImageView.kf.setImage(with: imgUrl)
detailPriceLabel.text = "中:\(detailDrink.fields.med_price) / 大:\(detailDrink.fields.large_price)"
detailDescTextView.text = detailDrink.fields.desc

selectSizePrice = detailDrink.fields.med_price

//訂購按鈕預設顯示 容量為中杯的飲料價錢
AddButton.setTitle("訂購 ($\(selectSizePrice.description))", for: .normal)

}

【 Segment Control 】選擇容量:

@IBOutlet weak var capacitySegmentControl: UISegmentedControl!

//容量 Segment Control
let capacity = ["中杯", "大杯"]


override func viewDidLoad() {
super.viewDidLoad()

//選定飲料資訊顯示
mainUI()

//設定SegmentConrtol樣式,選中時的文字顏色
capacitySegmentControl.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white], for: .selected)

//設定SegmentConrtol樣式,未選中時的文字顏色
capacitySegmentControl.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.lightGray], for: .normal)

}


//容量大小 Segment Control 改變價錢
@IBAction func changeCapacity(_ sender: UISegmentedControl) {
switch capacitySegmentControl.selectedSegmentIndex{
case 0:
selectSizePrice = detailDrink.fields.med_price
case 1:
selectSizePrice = detailDrink.fields.large_price
default:
selectSizePrice = detailDrink.fields.med_price
}
updatePriceUI()
}

當改變容量大小、選擇加料,會更新訂購按鈕上的價錢:

//訂購按鈕的價錢
func updatePriceUI(){
AddButton.setTitle("訂購 ($\(selectSizePrice + selectExtraPrice))", for: .normal)
}

【 Drop Down Menu 】選擇冰塊、甜度、加料:

由於選項較多,因此以「下拉選單」呈現。

製作方式為拉UIButton,把需要的選項存放到一個Array

透過for迴圈把每一個選項變成UIAction,再加到UIMenu裡

@IBOutlet weak var iceButton: UIButton!
@IBOutlet weak var sugarButton: UIButton!
@IBOutlet weak var extraButton: UIButton!

override func viewDidLoad() {
super.viewDidLoad()

//下拉選單 冰塊、糖度、加料
setDropDownMenu(buttonName: iceButton)
setDropDownMenu(buttonName: sugarButton)
setDropDownMenu(buttonName: extraButton)

}


//更多選項 下拉選單
func setDropDownMenu(buttonName: UIButton){

var selectArray = [String]()
var menuActions = [UIAction]()

//根據UIButton Name 選擇飲料細項
switch buttonName{
case iceButton:
selectArray = ["正常冰","少冰","微冰","去冰","完全去冰","常溫","溫","熱"]
case sugarButton:
selectArray = ["正常糖","少糖","半糖","微糖","兩分糖","一分糖","無糖"]
case extraButton:
selectArray = ["無", "白玉", "水玉", "甜杏"]
default:
selectArray = []
}

buttonName.showsMenuAsPrimaryAction = true
buttonName.changesSelectionAsPrimaryAction = true
buttonName.layer.cornerRadius = 5

//要加這行圓角效果才會有
buttonName.layer.masksToBounds = true

//每次重選加料時,價錢先歸0
selectExtraPrice = 0

//下拉選單的選項
for selectItems in selectArray{
menuActions.append(UIAction(title: selectItems, state: .on, handler: { action in

//判斷有沒有加料要加錢
switch action.title {
case "白玉":
self.selectExtraPrice = 10
case "水玉":
self.selectExtraPrice = 10
case "甜杏":
self.selectExtraPrice = 15
default: self.selectExtraPrice = 0
}
self.updatePriceUI()

}))
}

buttonName.menu = UIMenu(children: menuActions)
}

【 InputTextField 】輸入訂購人欄位:

@IBOutlet weak var inputNameTextField: UITextField!

//訂購人輸入Text Field樣式設定
func setTextFieldColor(){

let placeholderText = "請輸入您的姓名"

//Placeholder樣式設定,置中顯示、白色
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center

let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.white,
.font: UIFont.systemFont(ofSize: 14),
.paragraphStyle: paragraphStyle
]

inputNameTextField.attributedPlaceholder = NSAttributedString(string: placeholderText, attributes: attributes)

//使用者打字後也要置中
inputNameTextField.textAlignment = .center

}

【 DateFormatter 】紀錄使用者下訂的時間:

取用的時間要特別設定時區,設定TimeZone.current,這樣時間才會是正確的

//紀錄當下的日期、時間
func recordDatetime() -> String {

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone.current

let currentTime = dateFormatter.string(from: Date())

return currentTime
}

當使用者按下「訂購 ($XX) 」按鈕後,記下當下所有的選擇

並跳出視窗讓使用者再次確認訂購、訂購成功通知

@IBOutlet weak var AddButton: UIButton!

@IBAction func addToCart(_ sender: Any) {

//選了什麼飲料?
let selectedDrink = detailDrink.fields.drinks

//選了什麼容量?
let selectCapacity = capacity[capacitySegmentControl.selectedSegmentIndex]

//現在時間?
let currentTime = recordDatetime()

//冰塊、甜度、加料分別選中什麼?
if let selectedIce = iceButton.titleLabel?.text,
let selectedSugar = sugarButton.titleLabel?.text,
let selectedExtra = extraButton.titleLabel?.text,

//使用者有沒有輸入訂購者?
let inputName = inputNameTextField.text,
inputName.count != 0 {

//都沒問題就發送,飲料訂購確認通知
let confirmMessage = "\(selectedDrink), \(selectCapacity)\n \(selectedIce), \(selectedSugar), 加料:\(selectedExtra),\n 訂購人:\(inputName),\n $\(selectSizePrice + selectExtraPrice)"

let controller = UIAlertController(title: "訂購確認", message: confirmMessage, preferredStyle: .alert)

//確認
let okAction = UIAlertAction(title: "確認", style: .default) { _ in

//再多跳一個訂購成功通知
let successController = UIAlertController(title: "訂購成功", message: "", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
successController.addAction(okAction)
self.present(successController, animated: true)

//最終訂單資訊
self.orders.append(uploadFields(fields: uploadOrder(Name: inputName, Drink: selectedDrink, Sugar: selectedSugar, Ice: selectedIce, Capacity: selectCapacity, Others: selectedExtra, Img_url: self.imgString, Price: self.selectSizePrice + self.selectExtraPrice, UploadDatetime: currentTime)))

//真的真的都沒問題了吼,上傳AirTable
self.uploadItems()
}

controller.addAction(okAction)

//取消
let cancelAction = UIAlertAction(title: "取消", style: .cancel)
controller.addAction(cancelAction)
present(controller, animated: true)


} else{
let controller = UIAlertController(title: "訂購失敗", message: "請輸入訂購人姓名", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
controller.addAction(okAction)
present(controller, animated: true)
}
}

真的真的沒問題了吼?最後一步,上傳AirTable

建立struct放置對應的上傳欄位

//上傳JSON內容
struct uploadRecords: Codable {
let records : [uploadFields]
}

struct uploadFields: Codable {
let fields : uploadOrder
}

struct uploadOrder: Codable {
let Name: String
let Drink: String
let Sugar: String
let Ice: String
let Capacity: String
let Others: String
let Img_url: String
let Price: Int
let UploadDatetime: String
}

上傳資料到AirTable,HTTP Method選擇POST:

//上傳資料到AirTable
func uploadItems(){
let url = URL(string: "https://api.airtable.com/v0/appUt8q96SSLnVvKS/Imported%20table")!
var request = URLRequest(url: url)

request.setValue("Bearer YOUR_API_KEY_HERE", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

request.httpMethod = "POST"

let encoder = JSONEncoder()

let data = try? encoder.encode(["records": orders])
request.httpBody = data

URLSession.shared.dataTask(with: request) { data, response, error in
if let data {
let decoder = JSONDecoder()
do {
let createUserResponse = try decoder.decode(uploadRecords.self, from: data)
// print(createUserResponse)
print("資料已上傳")
} catch {
print(error)
}
}
}.resume()
}

大功告成!

完成Swift Code:

--

--

Yen Lin
彼得潘的 Swift iOS / Flutter App 開發教室

iOS Developer in Taiwan | A person who loves to learn cool stuffs. Check out my website to discover more about me: https://yenlin.webflow.io/