Reflections on Day 87
I really loved yesterday as a learning day! Strings are such a wonderful topic and the Documentation is really precise on that!
The challenges were easy but very interesting. The true challenge was how to make them absurdly simple, which took me a bit longer than what I would have liked, but I am so happy I could manage to learn a whole day of program and to complete the challenge day all in a single day.
Today we need to review what was learned, analyse some key learning points and complete the milestone project which, spoiler, will not be an app! We will have to write three extensions, easier to hard and, sincerely, I can’t wait for it, I really love extensions!
So, what did we learn yesterday?!
In the first project we hacked with the Core Location framework and with its CLLocationManager
which, quite clearly, manages the location permissions and updates in iOS. This came with some more fiddling with the Info.plist file, where we learnt to add the “Always” and “When in use” permission types. Once this was settled we met new friends in beacons and we learned how to identify these tiny little objects that are kind of everywhere in the world with an UUID (Universally Unique Identifier), a major and minor number and, possibly, even a string identifier. But that was not everything as, once a beacon was found, we learned to use CLProximity
to determine how far from us the beacon was. As they rely on bluetooth to save battery the position is a couple of meter in sensibility, not much less.
Then, in the second project, the longest ever in the series with about 600 lines of code, we got a second shot at Bezier curves with the UIBezierPath
class that allowed us to draw custom shapes before using SKShapeNode
to render them in the game. This was also our first chance to meet something related to my job, AVAudioPlayer
, from the AVFoundation framework. This was needed because using an SKAction
would have prevented us to stop the sound before completion. We now get a spoiler about Day 36 which, quite to my surprise, will be much after the end of these 100 days. I was thinking that this initiative would have brought us to the end of the Hacking with Swift book but, instead, it seems it won’t.
In the third and final project of this series, one of the most interesting ones for me, we learnt a whole lot about String(s) and how they are yes a collection but not just an array of characters. We delved into the most common methods for string manipulation and started to write our own magic extensions! Then, in the end, one of the most interesting things, we met the NSAttributedString
class which lets us treat text basically as if we would be in a word processing software! … after all, even Pages and Word had to program the thing, right?!
Key take-aways
Let’s now see the two most important pieces of code we need to take away with us after these lessons.
1.
After touching the surface of Core Location in Project 22 we need to move the tent and look behind it at what is waiting for us in the future: user location’s coordinates with the granularity (precision) we desire, tracking visited places, indoor locations, geocoding—that is converting coordinates to names!
For the first example we still modify the Info.plist file to request the permission and, if we want to track the user’s location, we should opt for the always option. Then we need to ask for the location only once, like this.
locationManager = CLLocationManager()
manager.delegate = self
// request the user's coordinates
locationManager.requestLocation()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print("Found user's location: \(location)")
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Failed to find user's location: \(error.localizedDescription)")
}
Paul uses the “visit monitoring” term which, in my limited English, I have no idea what could mean apart from “monitoring the location of the user continuously” but I am pretty sure that is not the case. If we want to use this functionality we need to call the .startMonitoringVisits()
.
If we instead want be notified when the user arrives or departs a location we will get what is defined as a callback method, which again is a term that I do not understand. What is “callback”? When will British people understand that even if their language is the dominating one, not everyone is a native speaker?
Fortunately, my good Swift friend Rob Baldwin told me that
[a c]allback method is just another term for a closure that runs after some other work is finished.
Looking at the Documentation for the CLVisit
class we get the following:
Information about the user’s location during a specific period of time.
It is of course once more a subclass of NSObject
and when we create a CLVisit
object we encapsulate information about places where the user has been. Visit objects are created by the system and delivered by the CLLocationManager
object to its delegate after we start the delivery of events. The visit includes the location where the visit occurred and information about the arrival and departure times as relevant. We do not create visit objects directly, nor should you subclass CLVisit
.
Visit objects contain as much information about the visit as possible but may not always include both the arrival and departure times. For example, when the user arrives at a location, the system may send an event with only an arrival time. When the user departs a location, the event can contain both the arrival time (if our app was monitoring visits prior to the user’s arrival) and the departure time.
2.
Extensions, oh… extensions! We can modify single types, but also whole protocols of types. For example, using a protocol to pass datas between view controllers is a pretty common technique and one that I would like to become proficient as soon as possible. There is a whole technique called protocol-oriented-programming that is based on that.
In this last technique project we extended the functionality of String(s) which is defined as a concrete type, or something that we can actually create. We can also extend the Int
type to see if a number is even or odd, like this:
extension Int {
var isOdd: Bool {
return !self.isMultiple(of: 2)
}
var isEven: Bool {
return self.isMultiple(of: 2)
}
}
But here a problem is clear at once: we are extending only the Int
type, not all the different types and sizes of integers that are supported by Swift. To do so we need to extend BinaryInteger
instead of Int
.
Another new very powerful functionality is to extend a subset of a protocol, such as any Collection
type (dictionary, set, array) that is made up of integer numbers. We can do so like this:
extension Collection where Element: Binary Integer {
If we want to extend a specific type so that it only applies to, say, arrays that contain Comparable
objects, suddenly the methods inside that extension will gain access to methods reserved previously only to, well, Comparable
objects. So we should write our extension like this:
extension Array where Element: Comparable {
func doStuff(with: Element) {
}
}
We are closing this part with the message that Swift is a protocol-oriented programming language exactly because we can extend specific types and, most of all, we can extend whole groups of them at once.
Let’s now move on to the challenges!
Challenges
Challenge 1: extend UIView
so that it has a bouncedOut(duration:)
method that uses animation to scale its size down to 0.0001 over a specified number of seconds.
So … just because humanity needs to learn from the ones who came before (right?!)… the biggest challenge here was how to tell the playground to show what I had done. Here is the whole code and it works:
import PlaygroundSupport // this is important!
import UIKit
// Challenge 1: extend UIView so that it has a bouncedOut(duration:) method that uses animation to scale its size down to 0.0001 over a specified number of seconds.
extension UIView {
func bouncedOut(duration: TimeInterval) { // TimeInterval is just a typealias for Double
UIView.animate(withDuration: duration) { [weak self] in
self?.transform = CGAffineTransform(scaleX: 0.0001, y: 0.0001) // you don't want to know how much time it took me to remember I had to use a parentheses and not dot notation.
}
}
}
let container = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200)) // this is required to begin with
let view = UIView(frame: CGRect(x: 10, y: 10, width: 180, height: 180))
view.backgroundColor = .red
container.addSubview(view)
PlaygroundPage.current.liveView = container
PlaygroundPage.current.needsIndefiniteExecution = true
view.bouncedOut(duration: 5)
Done! On to the next one!
Challenge 2: extend Int
with a times()
method that runs a closure as many times as the number is high. For example, 5.times { print("Hello!") }
will print “Hello” five times.
This was a nice one as well:
extension Int {
func times(handler: () -> Void) {
guard self > 0 else { fatalError("Please use only positive integers") }
for _ in 0 ..< self {
handler()
}
}
}
Happy because I didn’t have to look at any one of the solutions yet nor any of the hints!
Challenge 3: extend Array
so that it has a mutating remove(item:)
method. If the item exists more than once, it should remove only the first instance it finds. Tip: you will need to add the Comparable
constraint to make this work!
Wow! This should have been the most difficult of the three and, actually, to me, this was the easiest one! I checked my solution against Paul’s one and found that I preferred using the guard let
syntax
extension Array where Element: Comparable {
mutating func remove(item: Element) {
guard let index = self.firstIndex(of: item) else { fatalError("\(item) not found.") }
self.remove(at: index)
}
}
Beautiful!
My first ever Consolidation Day finished in under two hours… I am deeply moved!
So … I know you would like to stay with me much more than just this time but, I have a pasta to cook and a night to sleep!
Goodnight, my readers!
If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).
If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).
You can also support me by buying Paul Hudson’s books from this Affiliate Link.
Anyways, thank you so much for reading!
Till the next one!