『約翰紅茶公司訂飲料app』1.0
串接自訂api,上傳下載儲存網路資料
總算能在最後一堂課前做出訂飲料app雖然還有很多不足的地方!
邊理解上課內容及做app真的時間不夠用呀XD
約翰紅茶公司是我覺得全台北最好喝的鮮奶茶~
此次以它作主題!
架構
飲料menu
- 下載:利用自訂api串接顯示(解碼)
飲料編輯頁面
- 下載:利用自訂api串接顯示飲料選項(解碼)
- 儲存:利用自訂型別儲存
- 上傳:儲存後上傳至自訂的網路雲端(編碼)
飲料訂購顯示頁面
- 下載:取出上傳的資料下載顯示在頁面上(解碼)
Menu
一開始先設定menu的部分,品項先打在表單
再將表單網址產生api再串接api到cell上
約翰紅茶公司的品項剛好分成三類
所以我利用segment control製作三個分頁
為了讓他有三頁所以把表單api分成三份不知道有沒有更好的方法
以第一頁為例子
可以將原本連接的viewcontroller改成tableViewController
一樣要替tableViewController製作新頁面跟新cell還有設定cell的identifier
設定好要的label並在cell中拉好outlet
飲料menu程式碼
先struct產生自訂型別
struct Menu1 : Codable {let brand : Stringlet name : Stringlet price : Stringlet city : Stringlet detail : Stringlet recommendIce : Stringlet recommendSugar : String}
抓取設定的api網址
import UIKitclass page1TableViewController: UITableViewController {var drinks = [Menu1]()override func viewDidLoad() {super.viewDidLoad()if let urlStr = “https://sheetdb.io/api/v1/q5lajoxncprg8".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),let url = URL(string: urlStr){URLSession.shared.dataTask(with: url) { (data, response, error) inlet decoder = JSONDecoder()decoder.dateDecodingStrategy = .iso8601print(String(data: data!, encoding: .utf8))if let data = data,let DrinkData = try? decoder.decode([Menu1].self, from: data){self.drinks = DrinkDataDispatchQueue.main.async {self.tableView.reloadData()}}}.resume()}}
設定cell顯示的資料
這部分看起來很簡單但要注意的地方很多
不小心漏打或打錯資料就顯示不出來了~
override func numberOfSections(in tableView: UITableView) -> Int {// #warning Incomplete implementation, return the number of sectionsreturn 1 }override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {// #warning Incomplete implementation, return the number of rowsreturn drinks.count}override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {let cell = tableView.dequeueReusableCell(withIdentifier: “page1Cell”, for: indexPath) as! page1TableViewCelllet Drink = drinks[indexPath.row]cell.page1Name.text = Drink.namecell.page1Price.text = Drink.pricecell.page1City.text = Drink.citycell.page1Ice.text = Drink.recommendIcecell.page1Sugar.text = Drink.recommendSugarcell.page1Detail.text = Drink.detail// Configure the cell…return cell}
再來是訂購飲料的部分
飲料的選項一樣是串接網路api
所以我又再設了一個全部菜單的api 😅
飲料編輯頁面程式碼
struct Order: Codable {var orderDrink: Stringvar orderPrice : Int}
設定菜單、甜度冰塊等一下會串接到button上
import UIKitclass editTableViewController: UITableViewController {@IBOutlet weak var iceButton: UIButton!@IBOutlet weak var sugarButton: UIButton!@IBOutlet weak var drinkButton: UIButton!@IBOutlet weak var nameTextField: UITextField!var orders:[Order]=[]let sugarStyle = [“正常糖”,”少糖”,”半糖”,”微糖”,”無糖”]let iceStyle = [“正常冰”,”少冰”,”微冰”,”去冰”,”熱飲”]override func viewDidLoad() {super.viewDidLoad()if let url = URL(string:”https://sheetdb.io/api/v1/n4oty7ptgbfj7") {let task = URLSession.shared.dataTask(with: url) { (data, response, error) inlet decoder = JSONDecoder()if let data = data {do{let drinkData = try decoder.decode([Drink].self, from: data)print(drinkData.count)for i in 0…drinkData.count — 1 {let priceInt:Int = Int(drinkData[i].mPrice!)!let brand = drinkData[i].brandlet drink = drinkData[i].nameif brand == “約翰紅茶公司” {let drinkName = “\(drink)”+”大杯”let oneOrder = Order(orderDrink: “\(drinkName)$\(priceInt)”, orderPrice: priceInt)self.orders.append(oneOrder)//print(drinkName)}}}catch{print(error)}}//print(self.orders.count)}task.resume()}}
利用迴圈設定飲料選項
還有設定送出按鈕的內容
@IBAction func drinkButton(_ sender: UIButton) { let controller = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)for drink in orders {let action = UIAlertAction(title: drink.orderDrink, style: .default) { (_) insender.setTitleColor(UIColor.black, for: .normal)sender.setTitle(drink.orderDrink, for: .normal)}controller.addAction(action)}let cancelAction = UIAlertAction(title: “取消”, style: .cancel, handler: nil)controller.addAction(cancelAction)present(controller, animated: true, completion: nil)}@IBAction func sugarButton(_ sender: UIButton) {let controller = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)for sugar in sugarStyle {let action = UIAlertAction(title: sugar, style: .default) { (_) insender.setTitle(sugar, for: .normal)sender.setTitleColor(UIColor.black, for: .normal)}controller.addAction(action)}let cancelAction = UIAlertAction(title: “取消”, style: .cancel, handler: nil)controller.addAction(cancelAction)present(controller, animated: true, completion: nil)}@IBAction func iceButton(_ sender: UIButton) {let controller = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)for ice in iceStyle {let action = UIAlertAction(title: ice, style: .default) { (_) insender.setTitle(ice, for: .normal)sender.setTitleColor(UIColor.black, for: .normal)}controller.addAction(action)}let cancelAction = UIAlertAction(title: “取消”, style: .cancel, handler: nil)controller.addAction(cancelAction)present(controller, animated: true, completion: nil)}@IBAction func sentButton(_ sender: UIButton) {if nameTextField.text?.isEmpty == true {let controller = UIAlertController(title: “請填入名字稱呼”, message: nil, preferredStyle: .alert)controller.addAction(UIAlertAction(title: “ok”, style: .default, handler: nil))present(controller, animated: true, completion: nil)return}else{if iceButton.titleColor(for: .normal) == UIColor.black,sugarButton.titleColor(for: .normal) == UIColor.black,drinkButton.titleColor(for: .normal) == UIColor.black{keepData()tabBarController?.selectedIndex = 2
//若資料完整則送出上傳資料,並跳轉至統計頁面}else{let controller = UIAlertController(title: “請選擇飲料及甜度冰塊”, message: nil, preferredStyle: .alert)controller.addAction(UIAlertAction(title: “ok”, style: .default, handler: nil))present(controller, animated: true, completion: nil)return}}}
儲存、上傳資料的程式碼
自訂新的表單產生api,並設定新的自訂型別
func post編碼上傳至表單上
struct UploadData:Codable {var date : Stringvar name : Stringvar drink : Stringvar price : Int}struct Data : Encodable {var data : UploadData}func post(orderData:UploadData) {let url = URL(string:”https://sheetdb.io/api/v1/qtg6iphc08cdo")var urlRequest = URLRequest(url: url!)urlRequest.httpMethod = “POST”urlRequest.setValue(“application/json”, forHTTPHeaderField: “Content-Type”)let order = Data(data: orderData)let jsonEncoder = JSONEncoder()if let encoderData = try? jsonEncoder.encode(order){let task = URLSession.shared.uploadTask(with: urlRequest, from: encoderData) { (retData, res, error) inlet decoder = JSONDecoder()if let retData = retData, let dic = try? decoder.decode([String:Int].self, from: retData), dic[“created”] == 1 {print(“ok”)}else{print(“error”)}}task.resume()}}
儲存資料的function寫在編輯頁面中
func keepData(){let time = Date()let formatter = DateFormatter()
//設定時間格式formatter.dateFormat = “yyyy-MM-dd HH:mm:ss”let dateData = formatter.string(from: time)print(dateData)let nameData = nameTextField.text!let order = drinkButton.title(for: .normal)!.components(separatedBy: “$”)let priceData:Int = Int(order[1])!let orderData = “\(order[0]).\(sugarButton.title(for: .normal)!).\(iceButton.title(for: .normal)!)”print(priceData)let newData = UploadData(date: dateData, name: nameData, drink: orderData, price: priceData)post(orderData: newData)}}
array = content.components(separatedBy: “$”) ,將資料用指定的符號來分隔,並將分隔後的每一行資料放入array陣列中 。
飲料訂購顯示頁面
取資料的自訂型別
struct List:Codable {var date:Stringvar name:Stringvar drink:Stringvar sugar:Stringvar ice:Stringvar price:Int}struct DownLoad: Codable {var date:Stringvar name:Stringvar drink:Stringvar price:String}func fecth (urlStr:String, complation:@escaping ([DownLoad]?) -> Void) {if let url = URL(string: urlStr){let task = URLSession.shared.dataTask(with: url) { (data, response, error) inlet decoder = JSONDecoder()if let data = data, let drinkData = try? decoder.decode([DownLoad].self, from: data){complation(drinkData)}else {complation(nil)}}task.resume()}else{complation(nil)}}import UIKitclass showTableViewController: UITableViewController {@IBOutlet weak var loadingActivityIndicator: UIActivityIndicatorView!//設定UIActivityIndicatorViewvar orders:[List]=[]func download(url:String){orders = []let urlStr = urlfecth(urlStr: urlStr) { (drinkData) inif let drinkData = drinkData, drinkData.count >= 1 {for i in 0…drinkData.count — 1 {let date = drinkData[i].datelet name = drinkData[i].namelet drinks = drinkData[i].drink.components(separatedBy: “.”)let price = drinkData[i].pricelet oneOrder = List(date: date, name: name, drink: drinks[0], sugar: drinks[1], ice: drinks[2], price: Int(price)!)print(drinks)self.orders.append(oneOrder)DispatchQueue.main.async {self.tableView.reloadData()}self.orders.reverse()//reverse是讓array反序}}}}override func viewDidLoad() {super.viewDidLoad()}override func viewDidAppear(_ animated: Bool) {download(url: “https://sheetdb.io/api/v1/qtg6iphc08cdo")self.tableView.reloadData()}//開始載入動畫func startLoadingList(){loadingActivityIndicator.startAnimating()}//停止載入動畫func stopLoadingList(){loadingActivityIndicator.stopAnimating()loadingActivityIndicator.hidesWhenStopped = true // 當停止動畫後隱藏}
設定cell顯示訂購資料
override func numberOfSections(in tableView: UITableView) -> Int {// #warning Incomplete implementation, return the number of sectionsreturn 1}override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {// #warning Incomplete implementation, return the number of rowsprint(orders.count)return orders.count+1}override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {startLoadingList()if orders.count — indexPath.row != 0 {let orderDrink = orders[indexPath.row]let cell = tableView.dequeueReusableCell(withIdentifier: “showCell”, for: indexPath) as! showTableViewCellDispatchQueue.main.async {self.stopLoadingList()cell.orderName.text = orderDrink.namecell.orderDrink.text = orderDrink.drinkcell.orderPrice.text = String(orderDrink.price)cell.orderSugar.text = orderDrink.sugarcell.orderIce.text = orderDrink.icecell.orderTime.text = orderDrink.date}return cell}else {let cell = tableView.dequeueReusableCell(withIdentifier: “totalCell”, for: indexPath) as! SumTableViewCellif orders.count != 0 {var total = 0for orders in orders {total += orders.price//設定金額加總}cell.sumLabel.isHidden = falsecell.sumLabel.text = “總金額$\(total)元”cell.cupX.text = “總杯數\(orders.count)杯”}else{cell.sumLabel.isHidden = truecell.cupX.text = “目前沒有訂單”}return cell}}
做完這個app把一些觀念釐清也發現還有很多不足的地方
也還有很多可以再做修改的地方
☑️ 再增加中杯大杯及一些限制條件(有些不能做熱飲)
☑️ 資料會延遲,製作下拉式更新
☑️ 同步修改資料(在app及網路)
☑️ autolayout尚未拉完成
☑️ 店面資訊等等