There are three pieces of code I’d like to review, just to make sure you understand them fully.
The first thing I’d like to recap is NotificationCenter
, which is a system-wide broadcasting framework that lets you send and receive messages. These messages come in two forms: messages that come from iOS, and messages you send yourself. Regardless of whether the messages come from, NotificationCenter
is a good example of loose coupling – you don’t care who subscribes to receive your messages, or indeed if anyone at all does; you’re just responsible for posting them.
In project 19 we used NotificationCenter
so that iOS notified us when the keyboard was shown or hidden. This meant registering for the Notification.Name.UIKeyboardWillChangeFrame
and Notification.Name.UIKeyboardWillHide
: we told iOS we want to be notified when those events occurred, and asked it to execute our adjustForKeyboard()
method. Here’s the code we used:
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: Notification.Name.UIKeyboardWillHide, object: nil)
There are lots of these events – just try typing Notification.Name.
and letting autocomplete show you what’s available. For example, in project 28 we use the Notification.Name.UIApplicationWillResignActive
event to detect when the app moves to the background.
Like I said, it’s also possible to send your own notifications using NotificationCenter
. Their names are just strings, and only your application ever sees them, so you can go ahead and make as many as you like. For example, to post a “UserLoggedIn” notification, you would write this:
let notificationCenter = NotificationCenter.default
notificationCenter.post(name: Notification.Name("UserLoggedIn"), object: nil)
If no other part of your app has subscribed to receive that notification, nothing will happen. But you can make any other objects subscribe to that notification – it could be one thing, or ten things, it doesn’t matter. This is the essence of loose coupling: you’re transmitting the event to everyone, with no direct knowledge of who your receivers are.
The second piece of code I’d like to review is this, taken from project 21:
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
if granted {
print("Yay!")
} else {
print("D'oh")
}
}
In that code, everything from { (granted, error) in
to the end is a closure: that code won’t get run straight away. Instead, it gets passed as the second parameter to the requestAuthorization()
method, which stores the code. This is important – in fact essential – to the working of this code, because iOS needs to ask the user for permission to show notifications.
iPhones can do literally billions of things every second, so in the time it takes for the “Do you want to allow notifications” message to appear, then for the user to read it, consider it, then make a choice, the iPhone CPU has done countless other things.
It would be a pretty poor experience if your app had to pause completely while the user was thinking, which is why closures are used: you tell iOS what to do when the user has made a decision, but that code only gets called when that decision is finally made. As soon as you call requestAuthorization()
, execution continues immediately on the very next line after it – iOS doesn’t stop while the user thinks. Instead, you sent the closure – the code to run – to the notification center, and that’s what will get called when the user makes a choice.
Finally, let’s take another look at for case let
syntax. Its job is to perform some sort of filtering on our data based on the result of a check, which means inside the Swift loop the compiler has more information about the data it’s working with.
For example, if we wanted to loop over all the subviews of a UIView
, we’d write this:
for subview in view.subviews {
print("Found a subview with the tag: \(subview.tag)")
}
All views have a tag, which is an identifying number we can use to distinguish between views in some specific circumstances.
However, what if wanted to find all the labels in our subviews and print out their text? We can’t print out the text above, because a regular UIView
doesn’t have a text
property, so we’d probably write something like this:
for subview in view.subviews {
guard let label = subview as? UILabel else { continue }
print("Found a label with the text: \(label.text)")
}
That certainly works, but this is a case where for case let
can do the same job in less code:
for case let label as UILabel in view.subviews {
print("Found a label with text \(label.text)")
}
for case let
can also do the job of checking optionals for a value. If it finds a value inside it will unwrap it and provide that inside the loop; if there is no value that element will be skipped.
The syntax for this is a little curious, but I think you’ll appreciate its simplicity:
let names = ["Bill", nil, "Ted", nil]
for case let name? in names {
print(name)
}
In that code the names
array will be inferred as [String?]
because elements are either strings or nil
. Using for case let
there will skip the two nil
values, and unwrap and print the two strings.
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.