Converting Objective-C to Swift

Soon after Swift came out David Kobilnyk set out on an interesting quest: to convert all the Objective-C code samples in the seminal book “iOS Programming: The Big Nerd Ranch Guide” to Swift. In his SLUG presentation at ThoughtWorks, he covered a range of topics, including Swift data types and mutability of types, and converting Objective-C code into Swift.

As usual, video & slides are synchronized. The video is also subtitled. You can find a blog version of the talk (with code samples) below.


Swift Types (0:08)

Structs in Swift include arrays, dictionaries, and strings. In Objective-C, these structures were classes. This affects how these data structures are used in Swift.

Value Types

Using Enums (0:57)

Enums in Objective-C tend to have very long enum names, one example being UIControlStateNormal.

[self.tableView insertRowsAtIndexPaths:
 @[indexPath] withRowAnimation:
 UITableViewRowAnimationAutomatic];

In Swift, you can add a dot before the last word. In this case, you could have an enum of type UIControlState with highlighted, normal, and different values associated with it. Swift allows you to shorten the name to “.Normal” when you pass that value into a parameter. It infers the type because of the function signature.

self.tableView.insertRowsAtIndexPaths(
 [indexPath], withRowAnimation: .Automatic)

Initializing Structs (1:53)

Structs are also a little different for initialization. Obj-C used a lot of constructors to initialize structs.

CGPoint p = CGPointMake(12, 64);

Swift still uses those types, but the CGPoint constructor is more like a class.

let p = CGPoint(x: 12, y: 64)

No Implicit Casting (2:21)

When it comes to numerical calculations with value types, you’ll find that unlike in Objective-C, there is no implicit casting in Swift. For example, if you wanted to take a mod 100 of a random integer and divide by a float, Objective-C would allow it.

float red = (arc4random() % 100) / 100.0;

Swift does not allow the same implicit casting. You would need to explicitly cast the integer so you have two float values, otherwise the compiler will give you an error.

let red = (arc4random() % 100) / 100.0
// 'UInt32' is not convertible to 'Int'

let red = Float(arc4random() % 100) / 100.0 // Ok

Arrays (2:53)

The initialization of arrays has changed through the development of Swift. The way to declare and modify a mutable array in Objective-C looks like such:

NSArray *a = @[@"pale ale", @"stella"];

NSMutableArray *a = @[];
[a addObject:@"hefeweizen"];

At this point, the way to do this in Swift is by declaring the array element’s data type in square brackets. All of the elements are then forced to be of the same type. If you want to change the values of the array, then you would want to declare it with the keyword var. To add elements, either the append method or the plus equals operator work. The += operator actually requires you to concatenate another array rather than just adding an element.

let a: [String] = ["pale ale", "stella"]
// or
let a = ["pale ale", "stella"]

var a = [String]()
a.append("hefeweizen")
a += ["hefeweizen"]

Dictionaries (4:05)

Dictionaries have a similar syntactic structure in Swift for declaring them. In Objective-C, you store objects in dictionaries. To store values, they must be wrapped, like below with NSNumber classes.

NSMutableDictionary *ages = [NSMutableDictionary new];

ages[@"Al"] = [NSNumber numberWithInt:35];
ages[@"Liz"] = [NSNumber numberWithInt:26];

for (NSString *name in ages) {
  NSLog(@"%@ is %@", name, [ages objectForKey:name]);
}

In Swift, you declare a dictionary with key:value. Swift allows you to store not only objects, but also any value type as well. Thus, you can directly store values like 35 or 26. To iterate through the values of the dictionary, you can use a tuple made of the key:value pairs. There is also an example of string interpolation in the println statement below.

var ages: [String: Int] = [String: Int]()
var ages = [String: Int]()

ages["Al"] = 35
ages["Liz"] = 26

for (name, age) in ages {
  println("\(name) is \(age)")
}

Value Type Mutability (5:13)

Mutability of value types corresponds to using the keyword let versus the keyword var when you declare the variable. By declaring an array with let, the array is entirely immutable.

let a = [1, 2]
a[0] = 5
// '@lvalue $T5' is not identical to 'Int'

var a = [1, 2]
a[0] = 5 // no error

Using Classes

Mutability of Classes (5:50)

The mutability of classes does change with the keywords let and var, depending on the pointer. The pointer is actually what’s being mutated when you use let or var, so the object being pointed to can be mutated. You can declare a mutatable object with let and still mutate it, but if you declare with var an immutable object type, appendAttributedString will not work because it isn’t defined on that class. Whether or not you can assign a new value to a variable does still depend on whether you declare with let or var.

let str1 = NSMutableAttributedString(string:"yo")
str1.appendAttributedString(str1) // no error
var str2 = NSAttributedString(string: "yo")
str2.appendAttributedString(str2)
// 'NSAttributedString' does not have a member named 'appendAttributedString'

let str1 =  NSMutableAttributedString(string: "yo")
var str2 = NSAttributedString(string: "yo")
str2 = str1 // no error
str1 = str2
// 'Cannot assign to 'let' value 'str1'

Class Initialization (6:58)

In Objective-C, there are a lot of situations where you have something like factory initializers like colorWithRed on UIColor.

[UIColor colorWithRed:0.1 green:0.6 blue:0.3 alpha:1.0];
[[UIColor alloc] initWithRed:0.1 green:0.6 blue:0.3 alpha:1.0];

In Swift, you simply use initWithRed, which has been simplified to just “red”. You then list the parameters of the initializer.

UIColor(red: 0.1, green: 0.6, blue: 0.3, alpha: 1.0)

Optionals (8:08)

In Swift, there’s a new concept of Optionals. In Objective-C, you could assign “nil” to any reference type but not value types. In Swift, you can’t assign nil to any data type value unless you declare it as Optional, at which point you can do that for either a value or reference type. By appending a ‘?’ to the data type, you have declared a variable as Optional.

let a: Int = nil
// Type 'Int' does not conform to protocol 'NilLiteralConvertible'

let b: Int? = nil // No error

Unwrapping Optionals (8:47)

When you declare a variable as Optional, you need to unrwap the Optional before using the value. To unwrap a value, use ‘!’ after the variable name. However, this force unwraps the variable, so if the Optional value is nil, Swift will crash. Therefore, you might checkbeforehand to make sure there isn’t a nil value. A safer way to unwrap an Optional is by using the “if let” syntax. This lets you unwrap the variable into another variable name of your choosing. If the value is nil, you can instead execute some other code in the “else” portion of code.

let a: Int? = 10
println(a) // prints "Optional(10)"
println(a!) // prints "10"
let b: Int? = nil
println(b!) // fatal error: unexpectedly found nil while unwrapping an Optional value

 var name: String? = "Al"
if let unwrappedName = name {
  println(unwrappedName)
} else {
  println("name is nil")
}

var name: String? = "Al"
if let name = name { // This is ok
  println(name) // Using unwrapped 'name'
}

Mutating Optional Value Types (10:27)

If you try to modify the value that you’ve unwrapped the original Optional value does not change. Only the unwrapped value is modified. In order to modify the initial Optional, you use the syntax [variable]!.write to force unwrap the value and mutate the Optional. If you instead use [variable]?.write, you safe unwrap the value by checking if the value is nil before executing the write.

if var unwrappedName = name {
  unwrappedName.write("bert")
  // unwrappedName changes to "Albert"
  // name remains "Al"
}

var unwrappedName = name!
unwrappedName.write("bert")
// name is still "Al"
name!.write("bert") // force unwrap
// name is now "Albert"
name?.write("bert") // safe unwrap
// name is now "Albertbert"

Mutating Optional Class Types (11:31)

For Optional class types, updating the unwrapped value does in fact update the original Optional as well. Both of the following examples of code updates the Optional through the unwrapped value.

let v: UIView? = UIView()
if let u = v {
  u.hidden = true
} // v is updated as well

let v: UIView? = UIView()
let u = v!
u.hidden = true
// v is updated as well

Checking for Nil (12:15)

In order to check for nil in Swift, you need to explicitly check for nil, and then if name isn’t nil, you can execute some code without bothering to unwrap the variable.

var name: String? = "Al"
if name { // causes error
  // …
}
// Type 'String?' does not conform to protocol 'BooleanType.Protocol'

var name: String? = "Al"
if name != nil {
  // …
}
// OR
if let _ = name {
  // …
}
// No error

Building Classes (13:05)

A very simple class declaration in Swift looks like the following:

class Food {
}

let food = Food()

You don’t need a header file or implementation file in Swift, or as in Objective-C, the @interface of @implementation.

Stored Properties (13:34)

In order to properly initialize store properties in a class, you need to write an initializer like so:

class Food {
  var name: String
  init(name: String) {
    self.name = name
  }
}

var food = Food(name: "hard-boiled egg")

If you didn’t want to write an initializer, you could handle this a few other others. You can include a default value on the property or make it Optional. ```swift class Food { var name: String = “[Unnamed]” } // No error

class Food { var name: String? } // No error ```

Designated Inits (13:58)

If you go with the initializer, it’s called the designated initializer in both Swift and Objective-C. It becomes responsible for initializing all of the properties and making sure that that’s complete.

class Food {
  var name: String
  init(name: String) { self.name = name }
}
var food = Food()
  // Missing argument for parameter 'name' in call

However, if you pass a name property, you have to use the convenience keyword, otherwise the compiler throws an error. The convenience keyword creates what is known as a convenience init. This was part of a design pattern in Objective-C that wasn’t actually enforced, but now is by the compiler within the Swift language.

class Food {
  var name: String
  init(name: String) { self.name = name }
  convenience init() {
    self.init(name: "[Unnamed]")
  }
}

Inits & Inheritance (15:55)

If you want a subclass and try to add a new property, you again run into the problem of not having an initializer. The initializer written has all the properties of the subclass and superclass, and it delegates initialization up to the superclass. If you want to continue with this sort of initialization, you need to include the override keyword because you’re overriding the init from the superclass.

class RecipeIngredient: Food {
  var quantity: Int
  init(name: String, quantity: Int) {
    self.quantity = quantity
  super.init(name: name)
  }
} // No error

class RecipeIngredient: Food {
  var quantity: Int
  init(name: String, quantity: Int) {  }
  override convenience init(name: String) {
    self.init(name: name, quantity: 1)
  }
}

Init Code Conversion Example (17:38)

In this Objective-C code example, this DrawView subclasses from UIView and has a moveRecognizer property.

@interface BNRDrawView () <UIGestureRecognizerDelegate>
@property (nonatomic, strong) UIPanGestureRecognizer *moveRecognizer;
@end

@implementation BNRDrawView
- (instancetype)initWithFrame:(CGRect)r {
  self = [super initWithFrame:r];
  if (self) {
    self.moveRecognizer = [[UIPanGestureRecognizer alloc]
    initWithTarget:self action:@selector(moveLine:)];
    [self addGestureRecognizer:self.moveRecognizer];
  } return self;
}

In Swift, you can put the protocols and superclass together, making sure that your superclass is listed first. The new regognizer property is made private because it was declared in the implementation file. There are three accessibility modifiers: private, public, and internal. Private means the property is accessible only within the file that you declare the property in. Internal, which is the default, means that the property is accessible throughout your entire app or framework. Public means that it’s accessible outside of your framework. We use the override keyword because the initWithFrame is an initializer of the superclass UIView, and the moveRecognizer is before the super.init call because all of the properties should be initialized before then. By making the moveRecognizer an Optional using the ‘!’, it is implicitly unwrapped so that every time it needs to be used, you don’t have to continue force unwrapping it.

class BNRDrawView: UIView, 
  UIGestureRecognizerDelegate {
    private let moveRecognizer: 
    UIPanGestureRecognizer
  }

override init(frame: CGRect) {
  super.init(frame: frame)
  moveRecognizer = 
  UIPanGestureRecognizer(target: self, 
    action: "moveLine:")
  addGestureRecognizer(moveRecognizer)
} // No error

IBOutlets (21:00)

IBOutlets in Swift are required to be Optional. In Objective-C there was no need to specify them as such.

@property (nonatomic, weak) IBOutlet
  UIDatePicker *datePicker;

In Swift, you want to go ahead and use either the ‘?’ or the ‘!’ to declare them as Optional.

@IBOutlet weak var datePicker: 
  UIDatePicker?
@IBOutlet weak var datePicker: 
  UIDatePicker!

Custom Setters (21:30)

Another common occurrence when converting code from Objective-C to Swift is that you want to override a setter of a property. In this example, you have containedItem that’s within a container, and you want to make sure its container property is updated to be self.

@property (nonatomic, strong) BNRItem  containedItem;
// …
- (void)setContainedItem:(BNRItem *)containedItem
{
  _containedItem = containedItem;
  self.containedItem.container = self;
}

In Swift, you can’t override the setter of the property in Swift. On the stored property, there is something called Computer Properties which enable you to define the getter and setter of your property. However, this limits your ability to refer to the contained item itself within the getter or setter. The didSet and willSet events allow you to declare on a stored property in Swift and so you can execute a bit of code when the value is set on the contained item.

var containedItem: BNRItem? {
  didSet {
    if let containedItem = containedItem {
      containedItem.container = self
    }
  }
}

Privately Mutable Public Properties (23:04)

Sometimes you want to have a read-only version that publicly available outside of the class and a private type available within the class. In this last example of Objective-C code, you declare two different types, an NSArray and an NSMutableArray.

// .h file
@interface BNRItemStore ()
@property (nonatomic, readonly) NSArray *allItems;
@end

// .m file
@interface BNRItemStore ()
@property (nonatomic) NSMutableArray *privateItems;
@end

In Swift, you don’t need to deal with these different types and can instead just have one array type, with a private setter to replace all that code.

class BNRItemStore {
  private(set) var allItems = [BNRItem]()
}

Q&A (24:05)

Q: If you have a lot of initializers, the first one doesn’t need any special keywords but subsequent ones require the keyword “convenience”?
David: You need an initializer that makes sure that all your stored non-optional properties are initialized.

Q: Could you tell us more about your github project on translating everything? How far are you into the peoject and are there any kind of overarching lessons you’ve learned?
David: I’m about halfway through the project, about 13 chapters so far. There are a lot of different quirks in Swift and I think it prevents a lot of developer errors that can be fairly common in Objective-C. Overall it just ends up being cleaner code.

Q: Have you seen any big advantages of using Swift?
David: I wouldn’t say there have been massive gains in terms of shorter code. However, I think it’s much less likely to run into as many programmer bugs and it’s probably a little bit faster to code. I prefer the structures you work with in Swift.

Q: How do you place key-value observing?
David: I haven’t worked with that much in OBjective-C or Swift, so I couldn’t say for sure. Person 2: I’ve worked that a little, I think at the moment you declare your property to be dynamic and use Objective-C stuff to do key-value observing in Swift.

Q: When you want to take an app and convert it to Swift, where is a good place to start based on your BNR guide?
David: I haven’t incrementally converted a large app, just smaller projects. I think I might start with some of the data part so you don’t have two separate files managing the data of something.

Q: About public, private, and internal - how do you define internal? What are the boundaries for that?
David: Internal means that your property can be used with a class-type .[property name]. This can be used anywhere within your application. If you were declare something instead as private, you can’t access the property of that class anywhere else in your app.

Q: Does having the convenience keyword make anything easier to decipher? Because I found it strange that in my project I had to go through my class heirarchy to find every init and redefine them in my class.
David: Initializers are not by default inherited in Swift. There are special circumstances where they do get inherited, for example when you have no store properties, but once you decalre a non-Optional property, you have to take care of the initializers.

Q: What if you don’t have an init in a class that just has methods? I had an SKView class deriving from base classes and kept having a strange pilot error.
David: Another thing to keep in mind is that if it’s implementing a protocol that has a required keyword on an init, then you need to define those required inits that don’t get inherited.

Q: Does Swift have the ID type? How can you declare a variable of the protocol type?
David: In Swift there is no ID, but there is the type AnyObject which can contani any reference type. AnyType can contain any value type or reference type.

Q: How helpful is XCode with refactoring or things like debugging?
David: I think the error messages I’ve gotten in Swift so far are a little bit cryptic, but there have been updates on Swift debugging. I haven’t worked with the debugger or the refactoring option. There are a couple talks on that from WWDC which would be a great resource to learn more.



David Kobilnyk

David Kobilnyk

Entrepreneur in Residence at Samsung Accelerator. Victor of an interesting quest: to convert all the Objective-C code samples in the seminal book “iOS Programming: The Big Nerd Ranch Guide” to Swift.