Our Swift Experience at SlideShare
May 20, 2015
Last fall, SlideShare released its 1.0 of the SlideShare iOS app - one of the few apps in the App Store built entirely with Swift. This was partially due to the coincidental release date for the 1.0 of Swift, but using Swift made sense for us. As soon as the language was announced, we spent every moment reading all of the material available and experimenting with the language. When the time came to decide if we would use Swift for our iOS app, the new language features and much more succinct syntax was very appealing, so we decided to take a chance.
We already shared our general experience with Swift and iOS features in a previous article. This post is a more technical and in-depth look at the details of the Swift language and our experience using it during the development of the SlideShare iOS app.
We decided to use Swift because of its expressive syntax, functional programming features, and focus on safety. Objective-C interoperability was important for our decision as we knew we would want to leverage both internal LinkedIn frameworks and 3rd party open source code that had been written in Objective-C. After reading through Apple’s book: Using Swift With Cocoa and Objective-C, we got the feeling that using these libraries would be a breeze.
At SlideShare, we iterate quickly, so a rapidly changing language was not an issue for us. For a two-week period right before launch, we even brought on members of our Android team to help out. SlideShare promotes every developer being a generalist, therefore all developers have experience with the Ruby web applications. With their experience in both building mobile apps and Ruby web applications, they were able to code features in Swift with only days of experience with the platform. Because of Swift’s similarities to scripting languages like Ruby, it was much easier for them to get up to speed.
How Much Was Actually Swift?
We wrote the entire app in Swift, with just a few exceptions.
- We wrote a custom
UICollectionViewFlowLayoutsubclass in Objective-C, because it had to be written in Objective-C in order to be used in Storyboards or XIBs at the time.
- Open source libraries that we pulled in using CocoaPods
- LinkedIn Internal Libraries
- An internal Core Data interface in Objective-C that we are using for efficient threading with Core Data. We plan on converting it to Swift when we have the time.
Interoperating with Objective-C
Interoperating with Objective-C was fairly easy. Just add a new header file to your bridging header and you’re mostly all set to go. There were two main pain points we had when interacting with Objective-C code. First, you lose the benefits of Swift’s static typing when working with
NSDictionary objects. Even if your dictionary is simply a dictionary of keys that are strings and values that are strings, you still need to work with it as a Swift dictionary of either
[String : AnyObject] or
[NSObject : AnyObject] 1.
The second potential pain point was working with enumerations that were created in Objective-C or C. If the code does not use the
NS_OPTIONS macro that Apple has provided, Swift will not convert it into a proper Swift enumeration. Though, if you use the macros provided, Swift will convert them into proper Swift enumerations with all the syntactic sugar we love so much. Because of these issues, we needed to fork the Facebook SDK and change their code to use
NS_ENUM for every enumeration, so that we could use it properly in Swift.
Problems with Early Adoption
As I said, we started with Swift on day 1 with beta 1. Needless to say, it was pretty painful dealing with the constant crashing of SourceKit, but here are some more specific problems we encountered. Some of the problems still exist.
- The compiler is slow and CPU intensive. On my 13” Macbook Pro Retina, the dual Core i7 is just not powerful enough to compile quickly. My co-workers who have 15” models with quad-core i7s have a much better time. Either way, the fans are quite loud when compiling.
- Syntax highlighting would break quite often when SourceKit crashed. With each beta, this issue became better and better, but seemed to regress again in 6.1.1. This seemed to happen frequently when working with lazy properties 2.
- Real-time syntax checking takes a while, so you have to rely more on building more frequently, which is slow.
- Each time a new version of Xcode came out, we had to refactor portions of our code to updated syntax. The time to refactor was usually pretty trivial: about half a day. Unfortunately, the refactor options in Xcode don’t currently support Swift. However, for version 1.2 there was a refactor to Swift 1.2 option that worked reasonably well.
- There were a few times you would write something that couldn’t compile and it would just crash the entire IDE, sometimes even your whole computer. This occurred mostly with the early betas, but it was quite frustrating, because we were still new to the language and had to figure out what was wrong without any error messages.
Did you ever have an blocking issues because of Swift?
No. We never hit any issues or bugs that we spent more than half a day working on that turned out to be an issue with Swift. If there was a bug in Swift, the code would either not compile or crash the compiler or Xcode. There was only one crash we encountered after having the app in production that seems to have been a bug in Swift, and it doesn’t happen very often. I posted this question on the Apple Developer Forums.
Swift Syntax & Features
Swift syntax is very approachable for developers who are familiar with scripting languages like Ruby or Python. Even our designer felt comfortable altering parameters in Swift 3. The syntax is very simple, and it gets rid of all the cruft seen in languages like Objective-C or C++. A great example of this is Objective-C block syntax v.s. Swift closures:
When writing Swift, I never really find myself looking up syntax for different language features, because the syntax is very natural and easy to understand. Dealing with numerous nested optionals can be a bit messy sometimes, however the code is still very readable.
Generics are a useful feature that we have used when creating utility methods that could work with numerous types. We have found them to be helpful in making type-safe generic code that we could reuse in numerous situations. Though we have used generics quite a bit, I still feel we haven’t used them to their full potential. We will be investigating more ways we can use generics to refactor our code in the future.
Operator overloading is a hotly debated topic. We have not yet overloaded an operator or created our own. However, I have a C++ background, so I definitely see the merit of having operator overloading, and I think it could become useful at some point for us. Though, I don’t see us creating many custom operators because of issues with code readability. There were times I wished I had operator overloading when writing Objective-C code, so I'm excited to now have that ability. Operator overloading should allow for more consistent, concise code when working with custom types.
Using Optionals correctly and safely has enabled us to eliminate an entire class of crashes in our app.
Braces Required on If Statements
This is a simple one, but we found this to be a very important feature that eliminated a whole class of potential bugs. Many developers have a habit of trying to make code compact, and this feature makes sure that a bug will not be introduced due to this habit.
There is a sizable portion of the Swift book (by Apple) that goes over initialization. You should definitely take the time to read through that portion of the book a few times to understand it clearly. We had some disagreements internally on whether initialization should be that concrete and complex, but we have found that it prevents bugs that can occur with improper initialization or other developers trying to add initializers to classes that someone else has written.
The addition of failable initializers is very important and was what was missing from initialization in Swift 1.0. Our experience has been limited with these thus far as the feature was released recently. Unfortunately, it seems like it is not completely flushed out. We will be experimenting with them more and will try to use them to completely replace class methods that were needed, because the initialization could fail based on input.
Switch statements in Swift are much more powerful than other languages like C++ or Java, and they are much safer. Defaulting to not falling through cases in a switch statement is one of the features that is simple and safer. Swift also requires all cases to be caught by either a specific case or a default. You will run into compilation errors if you don't handle every case. This is another great change to help developers make sure that there is no undefined behavior. Because of these changes and some features like pattern matching, we have preferred using switch statements in most cases instead of if/else statements. You can see more in our Swift Style Guide.
Having everything statically typed eliminates issues due to assuming a certain data type is within a collection.
When casting in Swift, the developer has 2 options: the developer can force-cast to a specific type using the
as keyword. If the force-cast fails, because the type specified is not a descendant of the type being cast, the app will crash. The developer can choose to add a
? to as in order to have the statement return an
Optional that will be
nil if the cast is not possible. We require the use of
as? in our code in order to avoid these crashes.
It was possible in Objective-C to, essentially, let the developer know what was private using techniques, such as declaring private methods and variables in a class extension in the implementation file. On the other hand, Swift makes access control actually enforceable using the
internal keywords. This is a nice feature for safety, because it disallows improper use or subclassing of your class. Similarly, the
final keyword can be used to disallow subclassing of certain methods.
Cons of the Language
Single-line Return Statements in Closures
This feature is nice when you have an actual single-line closure, but it causes issues when you have something that returns a value that you don’t care about. In order to workaround the issue that this causes, you need to add a blank return or workaround it another way. We have found that it would be nicer and more consistent to always require the return keyword. An example of this issue in practice is the following:
Missing Some Useful Dynamic Features
We have not found that we miss too many of the dynamic features of Objective-C, but one in particular has been more apparent. When writing some helper methods for Core Data
NSManagedObject subclasses, we could not find a way to define a method in a class with a return type of itself that would return the subclass’s type if called in the context of the subclass. This was inspired by some of the categories that are implemented in Magical Record. In actuality, this only saves you from having to cast to the correct subclass when calling the method, which isn’t really that big of a deal. My attempt at coding this is here:
The way parameter names are used in functions, methods, and initializers is confusing and non-uniform. You can have external names for your parameters in a method or have it just be implied from the order of the arguments (as in C/C++). It would be nice if they made it very consistent, but for now, you kind of just get used to it. If it doesn’t do what you want the first time, you add an external name or a # in front. See my gist for more info on this:
We have found the experience writing our new SlideShare iOS app in Swift has been a great success overall. It is great that LinkedIn and SlideShare were comfortable taking the risk of developing in a brand new language, and we believe the benefits have greatly outweighed the costs. Please feel free to reach out to me directly on Twitter or LinkedIn and check out the SlideShare iOS app today!
Check out our Swift Style Guides
Download the SlideShare iOS app
1 The reason you must use either
[String : AnyObject] or
[NSObject : AnyObject] for an
NSDictionary is that the key must be hashable. A Swift
NSObject both implement the
Hashable protocol. If you create your own type or extend another type and want to use it as a key in a dictionary, you have to implement the
2 If your syntax highlighting breaks and won’t come back on a particular file, close the file from any tabs you have opened and re-open.
3 We will be publishing another article all about developers working with the designer.