Put your Swift skills to the test and learn something new.
Previously I posted a Swift coding challenge that seemed simple at first, but of course took a little more thinking. These sorts of challenges are fun not because you’ll write perfect code (often the opposite!) but because they force you to think about how the language works and perhaps learn something along the way.
This weekend I tweeted out another challenge, and had some excellent responses. Now that my followers have had a chance to solve it themselves, it’s time to take a closer look at the problem and the various solutions.
SPONSORED Debug 10x faster with Proxyman. Your ultimate tool to capture HTTPs requests/ responses, natively built for iPhone and macOS. You’d be surprised how much you can learn about any system by watching what it does over the network.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Create a new Swift playground then give it this code:
if a == 1 && a == 2 && a == 3 {
print("All three match!")
}
How many ways can you make “All three match!” be printed?
Swift allows you to create your own operators freely, but it also allows you to modify existing operators however you want. In this case we’re looking at two operators: ==
and &&
. If we replace the default definition of either of them our condition will evaluate as true.
First let’s look at ==
, because that’s the easier of the two. This just checks for equality between the integer on its left-hand side and the integer on its right-hand side, so we can rewrite ==
so that it always returns true. That is, given any two numbers, we can tell Swift they are equal.
Here’s how that approach looks:
extension Int {
static func ==(lhs: Int, rhs: Int) -> Bool {
return true
}
}
// a can now be any integer
let a = 9
That will of course break all the other uses of ==
you have in your app, so if you wanted to keep the rest of your code intact you could overload ==
where the left-hand side is a string and the right-hand side is an integer, like this:
func ==(lhs: String, rhs: Int) -> Bool {
return true
}
let a = "9"
That no longer needs to be inside an Int
extension because we’re not replacing anything.
The other opportunity for operator overloading is the &&
operator. This is conceptually harder only because you might not know what the left-hand and right-hand side operands are – what “type” is a == 1
, for example?
Well, the truth is that it’s easier than you might think: a
is a type (probably an integer), and 1
is also a type (also probably an integer), so the type of a == 1
is the same as the result of calling ==(lhs: Int, rhs: Int
– i.e., a boolean.
In fact, the &&
operator is beautifully simple, and shows off so many features of Swift – I love showing it off to Swift classes, because it packs in so much power in hardly any code. Here is the entire default implementation of &&
, as taken from the Swift standard library:
public static func && (lhs: Bool, rhs: @autoclosure () throws -> Bool) rethrows -> Bool {
return lhs ? try rhs() : false
}
(If you want to find out how that all works and why, you should probably read Pro Swift.)
Of course, we don’t want that default implementation – we want to replace it with one that forces the result to be true even if both operands aren’t true.
In Swift, it looks like this:
extension Bool {
static func &&(lhs: Bool, rhs: Bool) -> Bool {
return true
}
}
If you thought operator overloading made this challenge a little easy, try the harder mode: given the code below, how do you make it print first “All three match” then “All three don’t match”?
if a == 1 && a == 2 && a == 3 {
print("All three match!")
}
if b == 1 && b == 2 && b == 3 {
print("All three match!")
} else {
print("All three don't match!")
}
Nate Cook proposed the following:
var fakedOut = false
func ==(lhs: Double, rhs: Int) -> Bool {
if !fakedOut {
print("All three match!")
fakedOut = true
}
return Int(lhs) == rhs
}
let a = 0.0
let b = 0.0
Can you do better?
Apple’s official documentation goes into great detail on how properties can be used: property observers, computed properties, getters and setters, and more. But it also adds this one important paragraph that many people skip over:
The capabilities described above for computing and observing properties are also available to global variables and local variables.
This is a feature of Swift: all those things you know about using property observers and computed properties can also be applied to global variables and local variables.
This functionality allows a trivial (and clean) solution to this challenge that doesn’t rely on operator overloading. The solution is this:
hiddenA
that has a default value of 0.a
that is computed – it doesn’t have a default value.a
is read, increase the value of hiddenA
by 1 then return it.Here’s how it looks in Swift:
var hiddenA = 0
var a: Int {
hiddenA += 1
return hiddenA
}
As you can see that takes hardly any code at all, and as far as the rest of our code is concerned a
is a regular local variable. It also doesn’t change the meaning of ==
or &&
, so any other code won’t be affected.
Earlier this month, Craig Hockenberry posted on Stack Overflow asking whether posts to that site could be fingerprinted using hidden Unicode characters. He gave an example using Swift, which provided the inspiration for this third solution.
Swift allows us to use a range of Unicode characters for variable names, including ones that aren’t normally visible. In this case we can use Unicode’s zero-width space to declare three variables – “a”, “a ”, and “a ” – each with their own values. Those spaces aren’t actually visible when you use the real Unicode zero-width space, which is why it’s called a zero-width space!
You should be able to copy and paste the following into a playground and have it work:
let a = 1
let a = 2
let a = 3
if a == 1 && a == 2 && a == 3 {
print("All three match!")
}
Those were the three solutions I came up with before writing the tweet, but two solutions from readers caught my eye as being particularly useful.
Here’s one from Toni Suter that creates a new enum type that can be created directly from integer literals. By making a
an instance of that enum Swift will automatically convert 1, 2, and 3 to the same type.
In code it looks like this:
enum Foo: ExpressibleByIntegerLiteral {
case bar
init(integerLiteral value: Int) {
self = .bar
}
}
let a = Foo.bar
Warren Gavin took a far sneaker approach, best appreciated with his animated GIF:
— Warren Gavin (@Apokrupto) January 20, 2018
I’m sure there are still more ways this challenge could be solved – tweet your best to me at @twostraws!
I wrote a whole book called Swift Coding Challenges that includes 64 problems in Swift, complete with hints to help you solve them, and worked solutions to help you learn as you go. If you’re applying for jobs or just want to your Swift brain finely tuned, check it out!
SPONSORED Debug 10x faster with Proxyman. Your ultimate tool to capture HTTPs requests/ responses, natively built for iPhone and macOS. You’d be surprised how much you can learn about any system by watching what it does over the network.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.