Thursday, December 08, 2016

Operator overloading in Swift

After years of having to write [@"bar" isEqualToString:@"foo"] I’m delighted that in swift we can simplify things and just write “bar” == “foo”.

However there is an important thing to note. Swift will match the overloaded operator to function that takes the same arguments. Simple.

So if I follow the example set by the equatable protocol, and wanted to compare two NSObject subclasses, I could create a function that looks something like:

And this would work everywhere as expected and print “hello world” …

Well, no. Remember I said exactly the same arguments.

So, If I was to change the optionality of one of the variables:

let a = Object(name: "foo")
var b:Object?

b = Object(name: "foo")

Our custom equality function will no longer be called, as it only expects unwrapped values. This means that the equality would fail, as the pointers do not match, and the test would fail and doSomethingWhenTheStringChanges would never be called.

The solution is fairly simple, you need to create a version of the overridden operator that accepts optionals, but again remember that the compiler is trying to match parameters, so you also need to cover the case where you have one/two unwrapped parameters.

What will now happen, is that when we have unwrapped parameters it will go directly to the first overridden operator, while optionals will use the second. We use the guard to ensure that we can unwrap both, and then do a pointer equality check which will return the correct result if one, or both are nil.

What makes this behaviour particularly “special” is that it will only happen with NSObject subclasses. When using doing the same thing with a pure Swift class, if you are missing the optional variation of the overridden operator the compiler will require you to explicitly unwrap the variable.

Ultimately I think the best way to avoid this mess is to just do things the old fashioned way and override isEqual: in your subclass.

It should also be noted that Xcode correctly syntax highlights the operator depending on whether you will use NSObject’s version of isEqual: or your own at runtime, but there is no warning.

Monday, January 11, 2016

Taking back disk space from Xcode

I have 256 GB SSD in my Macbook Pro, I have no external disks, and no cloud storage other than a Dropbox's free tier. So I think it's fair to say that I'm not a data hoarder. I live my digital life, much like I live my real life, lean and light.

In the last few months I've been constantly hovering around ~8GB of available space, and more recently I've opened my Macbook to messages of "No available disk space". Considering that I dilligently manage my storage I've been a little confused as to why this is the case.

After digging around a little, I discovered that the ~/Library/Developer folder is weighing in at 35.75GB, thats 13% of my disk space!

Digging a little deeper, ~/Library/Developer/Xcode/iOS DeviceSupport/ contains the symbols for every iOS version that you've connected to your machine. If you are like me, and have been doing development for a while, you'll probably have a few, I had 12, going as far back as 7.1.2. They range in size from 600MB, all the way upto 3.36 GB. For my development needs I only need the last two versions; 9.2 & 8.4.1. Removing the unused symbols, as well some dervived data from some old projects helped me recover around 13GB.

If you've installed Xcode Betas you may have had the issue where you have duplicate simulators, sometimes even 3 copies. Each simulator is usually 1GB+ so removing excess ones can save you some additional space. On the advice of this stackoverflow post I installed the snapshot tool, which is part of the fastlane toolkit. It has a handy command called "reset_simulators" which will remove all the simulators, and recreate only the simulators for the current primary SDK you have installed.

The above tips helped me recover 22 GB just from Xcode.