Part 1 — Creating iOS Apps Programmatically!
In this tutorial, I’m going to try and explain all the basics you may need to know to get started with making iOS apps programmatically, i.e. without storyboards.
IECSE CRASH COURSE: DEVELOPMENT WITH SWIFT
This article is going to be the first article of this ten part series. Through the course of 10 articles, we are going to make a small app which will display a set of users that we will fetch from the net. While making the app, we will cover some of the most important topics that every iOS developer must know.
By the end of this article, you will have a clear idea about the basics of iOS and how we can start making iOS apps programmatically.
First of all, let’s create an Xcode project by opening Xcode and selecting “Create a new Xcode project”.
Once our project has been created on Xcode, we will end up on a screen that looks like this.
Why Programmatically?
When we think about iOS development, there are two approaches to it:
- Using Storyboards: This is the more visual approach. This feature was introduced with iOS 5. In simple terms, what you do here is drag the various elements that make up the views that we want on our screen. It also helps us design transitions between the different views that we make.
- Programmatically: This is the approach that we’ll be using. Here, we make our apps with nothing but code, we do not have any visual cues of the app without building it.
I personally prefer a programmatic approach because it gives you better control over all the elements in your app. Anything that is achievable through a storyboard can be achieved through code. One of the other main advantages is the reusability of code. You can use one function to make your labels throughout your project. Also, when working with other developers, the storyboard approach may give merge conflicts, this problem is also taken care of in a programmatic approach.
Removing Main.storyboard and its References
Now, before we start with the coding part of it, we need to make a few changes so that our app doesn’t need a storyboard. So first, we will delete the reference to the storyboard “Main” from the general settings of our app.
Now that we’re done with these 3 steps, we’re ready to start coding.
Creating the Table View
Cocoa Touch
Cocoa Touch is a name given to the collection of UI frameworks available as part of the iOS SDK. For us, CocoaTouch provides us with the implementation of most UI elements that we need. When we make a file for an element using CocoaTouch, we get some pre-written functions that we might need for implementing the respective element. We can change these functions and add new ones as and when we need them.
First, let’s add a new file with Cocoa Touch Class.
Let the Class be TableViewController for now, this will also become the name of the file that will get created.
Delete the “ViewController.swift” file, we won’t be using that.
App Delegate
The AppDelegate is one of the default classes that gets created by Xcode in any new project that we create. The App Delegate is considered to be the entry point of any app that we make. This class is also responsible for handling the application’s life cycle events, i.e., responding to the app being launched, backgrounded, foregrounded, receiving data and so on.
Here, we need to create an instance of UIWindow which is a subclass of UIView. Every app has exactly one window. This window is created at launch time and is never destroyed or replaced. It is in this window that all our Controllers are contained.
Now we will set up the code in the AppDelegate.
For now, we only need to make changes in application(_didFinishLaunchingWithOptions)
in this class, the other functions remain the same.
Now, let’s try to break down what is happening in these lines-
var window: UIWindow?
, the question mark means it’s an optional type and that the value can be absent or present; this is similar to a nil value in other languages, but it works for all types not just classes.- We will talk about the conditional
if #available(iOS 13.0,*)
, when we take a look at the SceneDelegate. window = UIWindow(…)
, this is done to create an instance of UIWindow, we name it window.- The
rootViewController
is the ViewController which is displayed as soon as our app is launched. window?.rootViewController = TableViewController()
, this line sets up the rootViewController of our app to TableViewController(), ie this class which will contain our table.window?.makeKeyAndVisible()
, this makes the window that we have created visible and makes it the key.
Scene Delegate
When you consider the AppDelegate to be the object that’s responsible for your application’s life cycle, the SceneDelegate is responsible for what’s shown on the screen; the scenes or windows. It is the SceneDelegate’s scene(_:willConnectTo:options:)
, that creates our initial content view, creates a UIWindow, sets the window’s root view controller and makes the window key. As you can see, this is what we did for the devices that had older versions of iOS in the AppDelegate
.
It was only since iOS 13, that the SceneDelegate class was introduced, before that all the tasks of the SceneDelegate used to be taken care of in the AppDelegate itself (this is exactly what we have done) but with iOS 13.0 SceneDelegate was introduced and all the work involving scenes is done in the SceneDelegate since then. If you want to read more about SceneDelegate, visit here.
Now in the scene(_:willConnectTo:options:)
, we’ll add code similar to what we added in the AppDelegate.
guard let windowScene = (scene as ? UIWindowScene) else {return}
, in this line of code we are making an instance of the WindowScene which we swill later embed in the window that we instantiate in the next line.
Next, we set the window’s root view controller to TableViewController() and we declare the window as key and make it visible. Also, make an instance of AppDelegate and set it’s window to the instance of UIWindow we just created.
Window can be thought of as a container in which scenes can be set.
Now when you build the app, it should look like this
In the next section, we will work on configuring the TableViewController.
The TableViewController
Any app consists of several views. Views are what we see on our screens. The table views are the views that help us in displaying lists of data in our applications, the class used for this is UITableViewController.
UITableViewController is the subclass of UIViewController, it is a convenience class used to manage a table view. It aids in understanding some of the commonly used classes in iOS such as delegates and datasources.
Coming to our TableViewController.swift, here is a function viewDidLoad()
. This is the function that is called once our view has been loaded into the screen. It is also the function where most of our logic will go.
Now, we are going to initialise the TableViewController.
Here, inside viewDidLoad()
we first call, super.viewDidLoad()
, as we already know now UITableViewController is a subclass of UIViewController. Even though here, the viewDidLoad() does not do anything, it is good practice to always call super on all the functions that you override because there might be some important initialisations taking place in these functions.
Next, what we are doing is making a separate function to set up the TableView, this just helps us in keeping the code clean. Let’s take a deeper look into the function setUpTableView()
.
tableView.translatesAutoresizingMaskIntoConstraints = false
, is the command that tells iOS that we are going to use AutoLayout, we’ll be looking at AutoLayout in detail in the next article, but we are going to give the relative positions of all the elements we’re going to be using and restrict iOS in generating Auto Layout constraints on its own. Especially when making UI programmatically, always make sure that you use this command.- Next, we need to inform the tableView about the type of cells that we will be using inside of it. That is that
tableView.register(UITableViewCell.self,forCellReuseIdentifier: “Cell”)
does, it registers the tableViewCells as the cells that we will be using in our tableView. we will get into details of the reuse identifier in detail in the next section. - In the next two lines, we make the tableView conform to two protocol methods, what this means is that these give us a blueprint of how the data should be handled by the tableView.
- At last, we set up the height of each cell of the tableView and we make the tableView cells non-selectable and the last line removes the default lines that separate two cells.
In the next and the last section of the tutorial, now we just have to configure our cells.
Setting up the cells
Let’s first add the final lines of our code and then try and understand what they do.
First, we change the function numberOfSections(..)
to return 1, this, as the name suggests, is the number of sections our tableView will have. For now, we are going to keep it one. The function tableView(_numberOfRowsInSection section:Int)
, returns the number of cells that we want the section to have. (As we only have one section for now in our tableView).
It is very important to understand the next function, tableView(_cellForRowAt indexPath:)
, this is where our cells are created and initialised.
Reusable Cells
Suppose our table had 10000 rows, in this case, if we made 10000 cells and we started scrolling towards the bottom of the table, eventually we would run out of memory and our app would crash. Hence what iOS does is, when a cell goes outside the view, i.e. the cell can no longer be seen, the same cell is used and the data inside it is changed accordingly and is again shown as a different cell. Hence, even if we have 10000 rows in our table, we can use very few cells to represent all the rows. This is because of the cells being reusable.
In the tableView that we made, where we registered the cells we were going to use, we also defined the forReusableIdentifier=”Cell”, now in the function tableView(_cellForRowAt indexPath:)
, we instantiate the cell with,let cell = tableView.dequeueReusableCell(withIdentifier:”Cell”,for: indexPath)
, here we need to make sure that the identifier we use is the same as what we registered in the tableView.
IndexPath
Index paths describe an item’s position inside a table view or collection view, storing both its section and its position inside that section. For example, the first row in a table would have section 0, row 0, whereas the eighth row in the fourth section would have section 3, row 7.
Like for the code we have written, we store the row of the cell we are configuring in a variable t. Next, we use the ternary operator, this operator works with 3 values instead of 2. One of them is a check/conditional that is run, one is the value that is returned if the check is evaluated as true and one more value that is returned if the check is evaluated as false. For us, we check if t is odd, in which case we give the cell a color teal, otherwise the color returned is pink.
The final part is configuring the tableViewCell, which we will cover in the next tutorial about making a custom UITableViewCell.
The app that we have made until now looks like this.
Wrapping Up
In this article, we’ve looked at how we can get started with making iOS apps programmatically. We’ve looked at some of the very important concepts including AppDelegate, SceneDelegate and we’ve set up our TableViewController so that we can use this going ahead.
Congratulations🎉! You have just completed the first tutorial!
All of the above-written code is available on Github. Watch this space for more interesting challenges, up next in this series!
Confused about something? Let us know in the responses below. 😄