Dec 1, 2014

Why I think Swift is not ready yet

UPDATE Feb '15: With Xcode 6.3, Apple fixed some of the issues this post is talking about. However - there are still some major issues for me, the number one of them is the fact I can't use Swift classes in Objective-C without porting the tests for them to Swift.

Like every relatively-early-adpoter-iOS-developer, I decided to give Swift a try in the past few features I worked on for Piano Maestro.

It was a lot of fun to get into, and I wrote some pretty code and features came out very nice. However, I ran into some issues, that made me eventually come to the conclusion that swift isn't quite ready yet, and I should stick to Objective-C as the main language for developing iOS apps. At least for now.

Here are my top reasons:

Inaccuracies in compilation errors
Often, when a line is relatively complex and has a compilation error, compiler will give you an error message that's simply not describing the real issue.

The most common use case is a simple error like sending the wrong primitive type as an argument to a function (a problem that happens a lot to us Objective-C/C/C++ developers, which are used to implicit conversion of primitives).

Consider the following example:
dispatch_after receives exactly these types arguments. So what's wrong here?

Turns out the problem is that dispatch_time expects Int64 to be sent as the second argument, and I actually sent NSTimeInterval.
But how could I know that from that irrelevant error message?

There went an hour of banging my head until I figured this out.

UPDATE Feb '15: at least for this example, was fixed in Xcode 6.3 Beta 1 and error message is now very descriptive and relevant.

Debugging is hard
More head banging often happened when trying to see the value of a variable in the debugger, I eventually find myself adding debug prints instead.
A tip about this: If you get "Some" as the output of trying to 'po var', you can 'po print(var)' instead, and it will (hopefully) work.

Can't use Swift code within Objective-C in unit tests
Update May 2015: I found workaround!

While it's absolutely possible to mix Swift <-> Objective-C code in the app's main target (apart for some limitations I will rant about later), in the test target, I can't import $(PRODUCT_MODULE_NAME)-Swift.h in Objective-C code.
This does not only mean that tests for Swift classes can only be written in Swift (fair enough), it also means that any Objective-C code in my app that uses Swift classes cannot be tested at all. Build will fail if it's included in the test target of the project.


Can't use tuples and trivial enums in Objective-C
As the official documentation says, you can't use Swift-only features in your Objective-C code.
This makes sense for some things, but it would make a huge difference if they could have made an effort to expose some of the more trivial features in the $(PRODUCT_MODULE_NAME)-Swift.h bridge:
- Trivial enums can become C enums with a prefix (like it's done in the opposite direction)
- Tuples can become trivial classes
For now, I can't expose functions that receive a tuple or a Swift enum to Objective-C, and must add some kind of bridge myself (e.g. get an Int parameter instead of an enum and use the constructor with rawValue:). 
Maybe it was too much of an effort for Apple to deal with, but this makes it harder to use these features if I know the class will be used in Objective-C code.

UPDATE Feb '15: you can now use trivial enums!

Can't use c++ in Swift
If like us, your app uses Objective-C++ code and not only Objective-C, you're in trouble and won't be able to use any code that uses c++ types in the header. You'll have to create some kind of Objective-C bridge that will hide the c++ types.

Can't use mocking frameworks
Because of limitations in the availability of the language runtime in Swift, there's no good way to use a mocking framework for tests right now. That's a big problem.
In the meanwhile - best workaround IMO is adding an Inner Class called "MockMyClass" in your test, and overriding any methods you want to mock (like we did in 2006 before mocking frameworks were introduced).


Xcode sucks, AppCode has only limited support
So, as you probably know from my Xcode vs AppCode comparison (a bit outdated, but most of it still applies), I don't use Xcode for coding. It's lacking some very basic IDE mechanisms that AppCode is excellent at (e.g. Extract Method, Quick Fix, Find Usages, ...).
This is still true for Xcode 6.1 and Swift support. Sure, Xcode improved since writing this comparison, but still - I can't even extract method? This sucks.
Unfortunately, Swift support in AppCode is still very rudimentary. But the good news is that they are working on it.
In the meanwhile, here's a link to AppCode EAP that contains some basic features of syntax highlighting and some basic refactoring in Swift. I still don't recommend it over Xcode's current Swift support though.

UPDATE Feb '15: AppCode 3.x has basic Swift support, but still has a very long way to go in order to be as good as it is for Objective-C in terms of refactoring etc.

To sum it up

I guess things are not that bad, because Swift is a very neat language and has some really cool features I'm in love with, like tuples, better enums, cooler closures, generics, property observers, and many many more. 
I assume my complaints are either going to be solved soon, or become less of an issue - many of them are mix&match issues, which only affect apps with existing Objective-C/C++ code. Hopefully

Anyhow, at least for the time being, I guess I'll stick with Objective-C as the main iOS development language.

Further reading:
Developing a Bidding Kiosk for iOS in Swift - a great retrospective with some more reasons of why Swift isn't ready yet.

Mar 13, 2014

8 Tips for working effectively with Interface Builder

While working at JoyTunes on the latest versions of our top-notch piano app, we've done a great deal of UI redesign and therefore had to spend long hours with the notorious Interface Builder, resizing images and views, which can be a lot of frustrating work.
During this work, we actually discovered a great deal of neat IB tricks, and I decided I must share them with the world.
So I conducted this little post of my 8 greatest tips for working effectively with IB.

A little disclaimer:
In JoyTunes, we work with .xib files (not storyboards), and without Auto Layout. This is mostly for historical reasons. As a result, some of the tips I'm mentioning might react a little differently for you if you're using storyboards or Auto Layout, but most should apply just the same.

1. Size to Fit Content
I'm so ashamed I only took the effort to look this up only recently. A HUGE time saver.
Select any view and then go to Editor->Size to Fit Content or simply press ⌘=
Will resize the selected view to fit its content in the following manner:

  • An ImageView/Button will be resized to the image's original size (most common usage):

  • A Label/Button will be resized to fit the text it's currently containing:

  • A parent container view to fit the frames of its subviews.

2. Holding option key to view distance from another view's edges
Holding option key after selecting a view, and then hovering over some other view, will show you cool distance indicators from that view's edges:

3. Editor -> Embed In View, Unembed:
Do you know that annoying feeling when you have some subviews all located and then you decide they need to have a different parent view, or even a different .xib, and then they are all automatically get located in the center of the new view?
Well - here's the solution:

4. Adding padding to freeform sized view without affecting subview's positions
When trying to add padding to a view, the x's and y's of the subview is retained, which isn't always what you want.
The best way I found to get past this, is to drag the edges while holding ⌘:

5. Move a view that isn't in the front:
One thing that bugs me when working with IB, is the inability to move views that aren't in front easily.
One way to do so will be to temporary bring it to front and then bring it back, but this is annoying.
Another way will be to use the size inspector on the right panel, which is fine but could take some guessing and adjustments until you get it right.

A way I found of moving a view with the arrow keys without bringing it to front is this:
1. Choose the view on the document outline.
2. Click on the frame of the root view in order to gain focus.
3. Move with arrow keys.
Some other (better) way of gaining focus, taken from the comments to this post:

  • If you double-click a view in the outline, it will switch focus to the layout pane with that view selected.
  • Shift+Right(Ctrl) Click over the view in IB and you'll see a menu of all the views that exist where you've clicked.

6. IBOutletCollection ordering
Often, the order of IBOutletColletions is important to us, and we want to iterate on them in code in that order.
One way of doing it is sorting them in code by their x/y/tag, and then iterate.
This is unnecessary. The order of connection is determined by the order in which we dragged the connection, and we can see the current order if by ⌃+Clicking the File's Owner:

7. Use custom properties 
I think a relatively underused feature of IB is the ability to set custom properties on views, using the "User Defined Runtime Attributes" of the Identity inspector:

As you can probably guess, we defined a JTLabel class that you can set stroke color and width to, and this is our way of immediately setting this without having to write any code.
Another common hack that can be applied this way is setting "layer.cornerRadius" of any view, to make it have rounded corners.

A relevant tool that uses this mechanism is Canvas, which will let you define animations for views without writing code.

Important warning regarding this that saved my life on one occasion:
In your implementation class you cannot use initWithCoder: otherwise your key-value path setting will not be picked up. You will have to do all your implementation within awakeFromNib.

8. MoarFonts - custom fonts WYSIWYG
Custom fonts have always been a big issue when working with Interface Builder.
There's no built-in support for those, and the best solutions I have seen were either using custom properties solutions like Canvas, or using font replacing techniques like IBCustomFonts. These are valid solutions, but they have a great downside - they don't give you a full WYSIWYG experience, which is exactly why you are using Interface Builder in the first place.
It can be very annoying to place a label with a placeholder font and then having to run the app in order to discover that the width you gave it doesn't fit well with your replacing custom font.

Lately I stumbled upon this solution called MoarFonts. It costs 10$, and there's no demo or trial - but trust me, it's totally worth it!
It's a pretty simple setup: you'll have to build your app with MoarFonts as a script build phase, restart Xcode, and voila! You can see custom fonts in InterfaceBuilder:

That's it!
I hope at least some of the tips were something you didn't know.
If you have any other tips you think I should add to the list, or if you have a better way to achieve some of the stuff I was trying to solve, please send me a tweet (@theyonibomber).

Also, you should listen to my podcast about mobile development & beer (in Hebrew).