the making of the weather app (Pt. 1)
Click to download project open source on my Github repo :)
Pt. 1 featuring: Dark mode / Light mode, textField, SF Symbols, MARK, extension.
This is by far the most complicated app I made, so we have a lot to talk about!
Essential things to share from making this app include: dark mode / light mode, delegate and protocol, extension, Networking, Core Location, API, parsing JSON, using “Mark“, using SF Symbols . Let’s dive right in:)
About Dark mode / Light mode
There are two places to display dark/light mode, here’s where and how:
In the storyboard, toggle this icon below:
2. In the simulator, from the top navigator, go to Features -> Toggle Appearance, or use hotkey SHIFT + ⌘ + A
Now how do we design different looks for light and dark mode and assign them? I like to divide this into two parts:
Color palette and background image.
Customize color palette for light and dark mode:
To add your customized color sets to Assets, click the “+“ button on the bottom left, choose “Color Set“, you end up with a new group of color palette that you can rename to whatever and pick a color from color picker to drag and drop into light or dark mode.
Make sure to have these selections dialed in:
2. Customize background images for light and dark mode:
Same method, but instead of “Color Set“, add “Image set“.
Below is the overall process in action:
Now let’s take a look of the file structure. We are still using the MVC design pattern here like previous apps. Make all the labels, buttons in the storyboard. Design light and dark mode. Add auto-layout constraints needed so Xcode can chill out with those warnings! (It triggers my OCD so I try to get them over with asap…)
Controller:
WeatherViewController
Models:
WeatherManager
WeatherData
WeatherModel
But we don’t always know these are the models we would need and what we are doing in those models right away in the beginning. However, I listed them out to make it easier for the blog viewers to have a clear idea of the structure, but they were built one by one as the app was being built and there would be a need to build them one by one. This is the workflow topic that always confused me, as I wasn’t sure how other people build files and in which order, I think this depends on an individual’s preferences and their experience level.
Structure reference
To create symbols, luckily we don’t have to create them from scratch thanks to the amazing Apple ecosystem. There are over 6000 built-in symbols designed for you to use for free when you download the SF Symbols app
All you need to do is to find the ones you like and type the name of the symbol into where you normally type in when choosing image, see below:
Link all the labels and buttons:
About textField
To add textField from scratch, it’s the same method as if adding a label or button.
There are a lot of customizable settings. For this app, we can make the first letter automatically become capitalized since we are supposed to type in a city’s name.
For the Return Key, we select “Go” to make it appear as “Go“ on the keyboard, you can also set it to other options such as “Search“ from the dropdown menu.
If your simulator’s keyboard doesn't come up, you can bring it up by using hotkey ⌘ + K or go to the simulator’s top navigation bar -> I/O -> Keyboard -> Toggle Software Keyboard
Honestly, by the time I’m writing this blog, it’d been a minute since I made the app, this is where I go back and rewatch the course video to refresh myself and this is crucial in my learning. By repeating back and forth with the module you have already learned reinforces the learning over and over again, repetition helps deepen the understanding of the knowledge. Writing about it is a great way, at least for me, to comb through and further clarify what I have learned that often seems like a huge chunk of blur after some time.
Now all the basics are laid out, we can get into the coding part of it.
Now we need to get a hold of the text in the textField by pressing “Go“ on the keyboard or pressing the Search button, meanwhile, we want to clear the text and end the editing process when “pressing“ happens so that we don’t leave the text in there, which could be kind of weird. To do all of these, there’s a concept to understand: protocol and delegate.
It was very confusing to me to wrap my head around this one, but after some reading and internalizing, I found the explanation below to be the most clean and simple to understand.
About delegate and protocol
The love triangle of class, delegate, and protocol:
Class:
A class is a blueprint for creating objects (instances) that encapsulate data (properties) and behavior (methods).
It defines the structure and functionality of objects.
Classes can inherit properties and methods from other classes (inheritance).
Protocol:
A protocol defines a set of requirements (methods, properties, etc.) that a conforming type (class, struct, or enum) must implement.
It acts as a contract, specifying what capabilities an object should have.
Protocols enable polymorphism, where different types can be used interchangeably as long as they conform to the same protocol.
Delegate:
The delegate pattern is a design pattern where one object (the delegating object) delegates certain responsibilities or tasks to another object (the delegate).
The delegating object typically has a property of type
protocol
that holds a reference to the delegate object.The protocol defines the methods that the delegate must implement to fulfill its responsibilities.
Relationship:
A protocol acts as the interface or contract between the delegating object and the delegate. It specifies what the delegate should be capable of doing.
A class can conform to a protocol, meaning it implements all the required methods and properties of the protocol.
The delegate is an instance of a class that conforms to the protocol. It is assigned to the delegating object's delegate property.
The delegating object can then call the methods defined in the protocol on its delegate, allowing the delegate to handle specific tasks or provide information back to the delegating object.
Example:
protocol DataFetcherDelegate {
func dataFetcher(_ fetcher: DataFetcher, didFetchData data: Data)
func dataFetcher(_ fetcher: DataFetcher, didFailWithError error: Error)
}
class DataFetcher {
weak var delegate: DataFetcherDelegate? // The delegate property
func fetchData() {
// ... perform network request ...
if let data = retrievedData {
delegate?.dataFetcher(self, didFetchData: data)
} else if let error = error {
delegate?.dataFetcher(self, didFailWithError: error)
}
}
}
class MyViewController: UIViewController, DataFetcherDelegate {
let dataFetcher = DataFetcher()
override func viewDidLoad() {
super.viewDidLoad()
dataFetcher.delegate = self // Set the view controller as the delegate
}
// Implement the delegate methods
func dataFetcher(_ fetcher: DataFetcher, didFetchData data: Data) {
// Handle the fetched data
}
func dataFetcher(_ fetcher: DataFetcher, didFailWithError error: Error) {
// Handle the error
}
}
In this example:
DataFetcherDelegate
is the protocol defining the delegate's responsibilities.DataFetcher
is the class that delegates data fetching tasks to its delegate.MyViewController
is the class that acts as the delegate, implementing the protocol's methods to handle fetched data or errors.
Key Takeaways
Protocols define the contract.
Classes conform to protocols.
Delegates are instances of classes that conform to protocols.
The delegate pattern facilitates communication and task delegation between objects.
One thing that confused me quite a bit that I would like to share the clarification in case anyone has the same struggle, because I often see protocols used as if it’s a String or Double, so I was wondering:
Is protocol a type?
Yes, its a named type!
Summery:
Protocols in Swift are types that define a blueprint of methods, properties, and other requirements.
They can be adopted by classes, structs, and enums.
Protocols can be used as types in function parameters, return types, and collections.
Protocol extensions can provide default implementations for protocol requirements.
This is why it acts as if it were a type or parameter, this is where the cloud in my head was finally cleared up, I was like wow the sky is clear now…that all makes sense now, phew! I truly hope it all makes sense to you as well after reading this one :)
Now let’s take a look at what the UITexfFieldDelegate is all about in Xcode:
Before we look at our code…( I know…there’s a lot here but I assure you this will pay off! Hang in there! )
If you haven’t already come across MARK and Extension, here we go:
About MARK
// MARK:
is a special type of comment used for code organization and navigation within Xcode.
Purpose:
Organization: It allows you to divide your code into logical sections, making it easier to read and understand.
Navigation: Xcode recognizes
// MARK:
comments and creates clickable markers in the Jump Bar (the dropdown menu at the top of the code editor) and the minimap (the code overview on the right side).
Syntax:
// MARK: - Section Name
// MARK: Sub-section Name
// MARK:
This is the keyword that indicates a section marker.
-
(Optional): Adding a hyphen afterMARK:
creates a separator line in the Jump Bar and minimap, visually separating sections.Section/Sub-section Name: Provide a descriptive name for the section or sub-section you're marking.
How to add it as your own customized code snippet:
Final result below, you can see that each chunk of code now has its own category and a nice dividing line, this is much more organized.
About EXTENSION
What is extension:
Extensions are a powerful way to add new functionality to existing types, even if you don't have access to the original source code. This includes adding computed properties, methods, initializers, subscripts, nested types, and even conforming to protocols.
Benefits of using extensions:
Extensions enhance the structure, readability, and maintainability of our code, especially when dealing with view controllers that need to conform to multiple protocols. They promote a clean separation of concerns and make your code more organized and easier to navigate.
Basic Syntax:
extension ExistingType {
// Add new computed properties
// Add new methods
// Add new initializers
// Add new subscripts
// Add nested types
// Conform to protocols
}
Here is how they are used in the app code:
//MARK: - UITextFieldDelegate
extension WeatherViewController: UITextFieldDelegate {
// allows us to use the UITextFieldDelegate methods, such as textFieldShouldReturn, textFieldDidEndEditing, etc.
@IBAction func searchPressed(_ sender: UIButton) {
searchTextField.endEditing(true) // dismisses the keyboard when the search button is pressed
print(searchTextField.text!)
}
// textFieldShouldReturn is called when the return key is pressed on the keyboard
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
searchTextField.endEditing(true) // dismisses the keyboard when the return key is pressed
print(searchTextField.text!)
return true
}
// textFieldShouldEndEditing is called so that we don't leave the text there after we press the search button
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if textField.text != "" {
return true
} else {
textField.placeholder = "Please enter a city" // if the text field is empty, remind the user to enter a city
return false
}
}
Our app’s code uses extensions to conform to multiple protocols. By visually separating the protocol implementations, we can quickly identify which part of the code handles user input ( UITextFieldDelegate
), weather updates ( WeatherManagerDelegate
), or location updates ( CLLocationManagerDelegate
). Extensions helped reduce the clutter from the main WeatherViewController
class, remaining the focus on its core responsibilities, while the protocol-specific code is neatly tucked away within the extensions.
Now finally, to get the weather data fetched from external source, let’s talk API in PART 2.