[iOS study]模仿飲料APP,上傳資料到後台,以可不可熟成紅茶為例

學習串接後台 API,新增讀取後台資料

建立飲料菜單資料庫,並運用搭配雲端平台 SheetDB

下載飲料資料

struct Drink: Codable {var brand: Stringvar drink: Stringvar size: String?var price: String?var drinkID: String?var detail: String?}struct Order: Codable {var orderDrink: Stringvar orderPrice: Int}--var orders:[Order] = []override func viewDidLoad() {super.viewDidLoad()if let url = URL(string: "https://sheetdb.io/api/v1/7vd0ylfezf9zg"){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 brand = drinkData[i].brandlet drink = drinkData[i].drinklet size = drinkData[i].sizelet priceInt:Int = Int(drinkData[i].price!)!if brand == "可不可熟成紅茶" {let drinkName = "\(drink)\(size!)"+"杯"let oneOrder = Order(orderDrink: "\(drinkName)$\(priceInt)", orderPrice: priceInt)self.orders.append(oneOrder)}}}catch{print(error)}}}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)}

飲料介紹展示(segue到另一tableViewController)

struct Menu: Codable {var drinkName: Stringvar detail: Stringvar drinkID: String}--
class
DrinkTypeTableViewController: UITableViewController {
var menu:[Menu] = []override func viewDidLoad() {super.viewDidLoad()if let url = URL(string: "https://sheetdb.io/api/v1/7vd0ylfezf9zg"){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)for i in 0...drinkData.count - 1 {let brand = drinkData[i].brandlet drinkID = drinkData[i].drinkIDlet drink = drinkData[i].drinklet detail = drinkData[i].detaillet priceInt:Int = Int(drinkData[i].price!)!if brand == "可不可熟成紅茶" {let drinkMenu = Menu(drinkName: drink, detail: detail!, drinkID: drinkID!)self.menu.append(drinkMenu)DispatchQueue.main.async {self.tableView.reloadData()}}}}catch{print(error)}}}task.resume()}}override func numberOfSections(in tableView: UITableView) -> Int {return 1}override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return menu.count}override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {let drinkMenu = menu[indexPath.row]let cell = tableView.dequeueReusableCell(withIdentifier: "TypeTableViewCell", for: indexPath) as! TypeTableViewCellcell.drinkImage.image = UIImage(named: drinkMenu.drinkID)cell.drinkLabel.text = "\(drinkMenu.drinkName)\n----------\n\(drinkMenu.detail)"return cell}

注意:因網路下載需要時間,需加上self.tableView.reloadData() 才能讓飲料介紹資料成功展示

展示甜度冰塊資訊(顯示 popover 彈出視窗)

教學:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {segue.destination.preferredContentSize = CGSize(width: 400, height: 240)segue.destination.popoverPresentationController?.delegate = selfif segue.identifier != "0" {let imageName = String(segue.identifier!)if let controller = segue.destination as? SecondViewController {controller.imageName = imageName}}}func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {return .none}

由於我將兩個segue.destination的命名同圖片的名稱,故直接設定

let imageName = String(segue.identifier!)

segue.identifier 為0是 飲料介紹頁的segue

檢查所有資料是否填入完整

@IBAction func sentButton(_ sender: UIButton) {let mediumID = mediumTextField.textlet medium = "https://medium.com/\(mediumID ?? "@")"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 mediumTextField.text?.isEmpty == false ,URL(string: medium) != nil {if orderIceButton.titleColor(for: .normal) == UIColor.black, orderSugarButton.titleColor(for: .normal) == UIColor.black, orderIceButton.titleColor(for: .normal) == UIColor.black{keepData()tabBarController?.selectedIndex = 1}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}}else {let controller = UIAlertController(title: "請填入mediumID", message: nil, preferredStyle: .alert)controller.addAction(UIAlertAction(title: "ok", style: .default, handler: nil))present(controller, animated: true, completion: nil)return}}

若資料完整則送出上傳資料,並跳轉至統計頁面

keepData()tabBarController?.selectedIndex = 1

儲存語法

func keepData(){let time = Date()let formatter = DateFormatter()formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"let dateData = formatter.string(from: time)print(dateData)//now datelet nameData = nameTextField.text!let mediumData = mediumTextField.text!//orderDrink & pricelet order = orderDrinkButton.title(for: .normal)!.components(separatedBy: "$")let priceData:Int = Int(order[1])!let orderData = "\(order[0]).\(orderSugarButton.title(for: .normal)!).\(orderIceButton.title(for: .normal)!)"//將新增資料上傳網路let newData = UploadData(date: dateData, name: nameData, mediumID: mediumData, drink: orderData, price: priceData)post(orderData: newData)}

將新增資料上傳sheetDB

先建立sheetDB

struct UploadData: Codable {var date:Stringvar name:Stringvar mediumID:Stringvar drink:Stringvar price:Int}struct Data:Encodable {var data:UploadData}func post(orderData:UploadData){let url = URL(string: "https://sheetdb.io/api/v1/2bnkitnsc8xzd")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, err) 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()}}

上傳資料成功可在網路後台看到資料

在統計頁面用tableView下載顯示

struct List: Codable {var date:Stringvar name:Stringvar mediumID:Stringvar drink:Stringvar sugar:Stringvar ice:Stringvar price:Int}struct DownLoad: Codable {var date:Stringvar name:Stringvar mediumID: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)}}struct NewList:Codable {var drinkName:Stringvar cupCount:Int}

tableViewController (不寫在viewDidLoad,而寫在viewDidAppear)

override func viewDidAppear(_ animated: Bool) {download(url: "https://sheetdb.io/api/v1/2bnkitnsc8xzd")self.tableView.reloadData()}

下載訂購資料

var orders:[List] = []var newList:[NewList] = []func download(url:String){orders = []newList = []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 medium = drinkData[i].mediumIDlet drinks = drinkData[i].drink.components(separatedBy: ".")let price = drinkData[i].pricelet oneOrder = List(date: date, name: name, mediumID: medium, drink: drinks[0],sugar:drinks[1],ice: drinks[2], price:Int(price)!)let list = NewList(drinkName: drinkData[i].drink, cupCount: 1)self.orders.append(oneOrder)self.newList.append(list)DispatchQueue.main.async {self.tableView.reloadData()}}self.orders.reverse()}}}

reverse是讓array反序,我讓最新資料展示在最上方

新增表格刷新功能

@objc func handleRefresh(refreshControl:UIRefreshControl){download(url: "https://sheetdb.io/api/v1/2bnkitnsc8xzd")self.tableView.reloadData()refreshControl.endRefreshing()}override func viewDidLoad() {super.viewDidLoad()self.refreshControl?.addTarget(self, action: #selector(self.handleRefresh(refreshControl:)), for: UIControl.Event.valueChanged)}

這個是我另外問彼得潘問到的教學說明:

更新效果

另外新增的mediumID用連結出網頁展示(目前只是堪用 不太流暢)

訂購飲料重新分類統計

除了按訂購人訂購時間展示訂購清單外,我認爲應該依照訂購飲料類別重新分類統計及排序:

var list2:[NewList] = []list2.append(newList[0])for i in 1...newList.count-1 {var a = 0for j in 0...list2.count-1 {if newList[i].drinkName != list2[j].drinkName {a += 1if a == list2.count {list2.append(newList[i])}}else{list2[j].cupCount += 1a = 100}}}var b = 0for z in 0...list2.count-1 {b += list2[z].cupCount}list2.sort { (a, b) -> Bool ina.drinkName > b.drinkName}controller?.list2 = list2controller?.cupCount = b

github:

--

--

Yolanda H.
彼得潘的 Swift iOS / Flutter App 開發教室

悠琅妲之愛梯熙緹推敲推敲潑墨坊 { iOS App | website SEO | Python | Aroma | Zumba | Chinese Poems | Tabletop game }