[iOS] Final Assignment for Peter’s swift class — DrinksOrder

With Airtable API

MYH
彼得潘的 Swift iOS App 開發教室

--

Hello there. It has been a while since my last post. I’d had a lot of stuff going on. Last year I got enlisted and joined the marine due to our four-months mandatory military service here in Taiwan. When I finally got out of it, I had to prepare for my job interviews in hope of landing a job as an iOS developer, which I did (hooray~). So, I didn’t really have time to work on Medium posts. Now that I’ve worked as an iOS engineer for almost two months, I feel like it’s time to hone my skills once again and incorporate what I’ve learnt from work by jumping back into more side projects.
Anyway, enough with the chitchat. Here’s the idea of this app:
1. Students can login by inputing their “name” and the “group”. (There’s also a pickerView to help them choose existed groups in Airtable.)
2. After login, the app will show all the drinks on the menu. (Just like Foodpanda, UberEat, etc.)
3. Like Foodpanda and UberEat, after tapping the drink on the menu, the app will direct to a more detailed page and ask for the details of the order. (Sweet level, ice level, cup size, etc)
4. Post order to Airtable using its API.
5. Another page to show all the orders from different students within the same group.
6. Students would be able to delete their order and reorder.

Yep. That’s about it. Not really a complicated project. And, as always, here’s a quick demo. (Most of its data is written in Traditional Chinese, but you get the idea)

Demo

demo gif

Here are some interesting aspects of this project. (Or at least in my opinion.) I‘m gonna try to make this article concise and focus on topics that are (maybe) a little bit more advanced. (cause I know y’all are all pros, we all are :) well… maybe not me, but I’m getting there.I hope. Lol.)
1. Creating an app without storyboard.
2. Airtable API.
3. Method I used to pass data in this project.
Without further ado, let’s dive in.

Creating an app without storyboard

It’s always easier to use storyboard to layout your UI when you first start learning swift. But as I progress, I found out that creating your UI programmatically is not just faster but also easier if you know what your are doing. There are just three simple steps:

How to add objects in your view.
1. Create your object. (UIButton, UITextField, UIView, etc)
2. use (superview).addSubview((your object))
3. Auto layout: Set horizontal and vertical constraints to the object. Make sure it make sense! Or else it won’t appear on the screen. For example:

1.Create Your Object
private
let aLabel: UILabel = {
let label = UILabel()
label.text = "drink name"
label.font = UIFont.systemFont(ofSize: 25, weight: .bold)
label.textColor = UIColor.black
label.numberOfLines = 0
return label
}()
2.Use (superview).addSubview(your object)
view.addSubView(aLabel)
3. Auto Layout
aLabel.leftAnchor.constraint(equalTo:view.leftAnchor).isActive = true
aLabel.rightAnchor.constraint(equalTo:view.rightAnchor).isActive = true
aLabel.bottomAnchor.constraint(equalTo:view.bottomAnchor).isActive = true
/*So now I'll still need either a top constraint or a height constraint*/
aLabel.heightAnchor.constraint(equalToConstant: 80).isActive = true

There are a lot of functions that can help you get your auto layout done, but since those functions are pretty self-explanatory, I’ll leave that job to you.

How to change your initial view controller
Now that you can add all kind of UI objects into your screen, let’s see how we can change our initial view controller.
Simple go to SceneDelegate (change the rootViewController below).

How to go to other view controllers
In the past, we can just create a segue to connect two view controllers and use performSegue(identifier:) to go to other pages. But now, there wouldn’t have any segue for us because we are not using storyboard. (Well…you can still create segues programmatically, but I mean…there is a better way.) Remember when we changed our initial view controller in SceneDelegate, we wrote rootViewController to an UINavigationController? Since it’s a UINavigationController, we can just use pushViewController and popViewController to go back and forth. Like this:

//A push to B
navigationController?.pushViewController(B(), animated: true)
//B pop back to A
navigationController?.popViewController(animated: true)

Alternatively, you can do this to make it a popover or other presenting styles. (You can also use instantiateViewController(identifier:) and present(ViewController(), animated:) to direct to other view controllers if you don’t want to use NavigationController.)

//A to B
let nav = UINavigationController(rootViewController: BViewController())
nav.modalPresentationStyle = .popover
self.present(nav, animated: true)
//B back to A
self.dismiss(animated: true)

And that’s probably all you need to know if you want to create you UI programmatically. Once you get used to it, you will find doing it this way is much more time-efficient. For example, you only need to copy and paste and make some small adjustments, and boom, a new view controller within seconds. It’s really as easy as it can get. Still, everyone has their own preferences, just giving you another option.

Airtable API

I know Pros like y’all probably don’t need me to explain how to use an API. LOL. If you still need help, Airtable is also kind enough to give you an API document to show you how to get its data through different parameters. All you need to do is set up your url and sit back and let the magic happen.

Here is a GET example:

A POST example:

Last but not least, a DELETE example:

Notice that I used @escaping when I defined my method. It’s because I want the result of this function to outlive its scope. Take func getDataByGroupFromAir(completion: @escaping ([Records]) -> Void) for example, I want to make sure that I have access to the [Records] data that this function get back from the API when I call this function elsewhere. Like this:

APIService.shared.getDataByGroupFromAir { data in
self
.groupDetail = data
print("DEBUG: ListViewController getDataByGroupFromAir \(data)")
DispatchQueue.main.async {
self.tableView.reloadData()
}
}

Method I used to pass data in this project

I’m gonna mention a few methods that I found interesting when I’m developing this app.

Passing data to different view controller using init

I pass A view controller’s data to B view controller by initializing B view controller. What I meant by that is to write something like this.

/*In B view controller's file*/
class BViewController: UIViewController {
private let drinkDetail: DrinksDetail
/*DrinkDetail is a struct model*/
init(drink: DrinksDetail) {
self.drinkDetail = drink
super.init(style: .plain)
}
/*Don't forget to also add required init*/
}

And this is how A would pass data to B.

/*In A view controller's file*/
func
tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let nav = UINavigationController(rootViewController: BViewController(drink: DrinksDetail(name: "drinkName", description: "drinkDescription", menu: "drinkMenu", Price: "drinkPrice")))
nav.modalPresentationStyle = .popover
self.present(nav, animated: true)
}

Passing data using protocol and delegate

Because I didn’t want a massive view controller, (Yep. I’m using MVC. Still learning MVVM) I customized this floating view that can decide how many cups you want to order and send its value to B view controller by protocol and delegate. Let’s take a look.

And it would look like this in B view controller.

Now you might wonder when does this function(sendNumberOfCup) get called since it says func sendNumberOfCup(n: Int) {} in B view controller instead of using the usual format like this .sendNumberOfCup().
This is where it gets pretty interesting. This function in B controller gets called whenever you tap the“ Add to Cart” button because I wrote its delegate in @objc func handleGoToCart(). It might still be confusing for some people since I’m doing a terrible job at explaining this. Hence, let me give you another example.

We see the same situation here if you conform VC to UITableViewDelegate and UITableViewDataSource. These functions are all written as func instead of, for example, cellForRowAt(). So that’s how protocol works. It triggers its protocol function where the delegation lies.

Alright, let’s a wrap for this side project. I don’t want to write about this project into more than one article like I used to. So, if you have any more questions, please feel free to drop it down in the comment section, I’ll gladly explain it much more further.

Finally, here’s the GitHub link.

--

--