The Techdegree Blog: Objective-C in a Swift world?

During my time at the Team Treehouse iOS Techdegree some students asked the question: Do I need to learn Objective-C to become a successful developer in the Apple ecosystem? Here’s my take on that question

Swift and Objective-C?

While Objective-C was the standard for Apple for a very long time, Swift has taken over the Apple community by storm. I don’t know the exact figures, so I’m not going to elaborate which one is the preferred one, but I would guess that the majority of companies/developers choose Swift over Objective-C. Does that mean that you’ll never need to learn Objective-C? Probably not. There are still companies that choose Objective-C over Swift because it is very stable, and compiling and building a project in Swift takes a lot longer than in Objective-C. And of course there’s a lot of legacy code out there that relies on Objective-C, especially if you’re considering adding a third-party framework to your project. But does that mean you absolutely need to know all about Objective-C to become a successful Apple developer? In short, I would say ‘no’, because Apple does a tremendous job of hiding Objective-C when working with Swift. The longer answer is, in fact you are already working with Objective-C all the time, because the types and methods we use in Apple’s frameworks are all bridged to Swift. What this means is that Apple has built a “wrapper” around all Objective-C type/method declarations that allows us to use the Swift version, which in turn use the underlying Objective-C version. A little example: Let’s say you’re using a UITableViewController and are working with the datasource method

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

What you’re doing here is actually calling the Objective-C method

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

Yes, there’s no func at the start of the method declaration and the return type UITableViewCell is at the beginning of the method. This is just one of the quirks of the old way of doing things. And those asterisks *? Those are indicators that you’re working with object pointers. A pointer is a type that points to the memory address of the object you’re working with. Sounds complicated? Well, it is. ;-)

But back to our original question: Do I even need to know this? It depends. Do you just want to learn the Swift way of doing things and build apps? Then no, you don’t. Do you want to learn all the underlying mechanics behind UIKit/AppKit/WatchKit/TVKit? Then yes, it might make perfect sense to learn all there is to know. Let’s compare some of the things that are or work differently in Objective-C, shall we?

Primitive Types

Basic types like String, Int, Bool etc. are considered primitive types. And there are alternatives for those in Objective-C as well. Here are just some examples:



At first sight this doesn’t seem like a big difference, in the end most Objective-C types are just prefixed with NS, but there’s a lot more going on:

  • First of all every Objective-C type has a Mutable variant, one that you can change later in your code. While we just differentiate those with var or let in Swift, this isn’t possible with Objective-C. Instead we use NSMutableString to declare an NSString type that can be manipulated later on. The same goes for arrays. While NSArray is the immutable version, NSMutableArray is used to create an array that can be changed.
  • Booleans in Objective-C work a little different, too. While the Swift version accepts true and false as values, we need to use YES and NO in Objective-C. Furthermore there are actually two boolean types in Objective-C, BOOL and bool, with the first one being used 99% of the time.
  • NSInteger is a very interesting one; at least I think so. ;-) There are in fact two integer types in Objective-C, NSInteger and int. The first one is usually used if you don’t know how large your integer might end up being, while int is the largest possible on 32-bit systems and long being the alternative on 64-bit systems. In the end NSInteger should be used, unless you need a specific size.


Another interesting topic are loops. In Swift those are very simple: for user in users {} With just those four words, we managed to set up a pretty complicated workload. Loop through the array of users and give us a local constant user which we can work with inside the declaration. This is how a traditional for loop looks like in Objective-C: for (int i = 0; i < [users count]; i++) {}

What the heck is going on here? Let’s break this apart:

  1. First you declare a variable i and initialize it with the value 0.
  2. The second argument determines how long your loop should run, in this case as long as i is less than the amount of users in the array.
  3. Lastly, you increment i by one every time the loop finishes execution.

I hope this example alone helps you appreciating the work Apple puts into Swift a little more. ;-)

For completeness sake I need to add another example. While the example above shows a traditional for loop, Apple also added a more “Swifty” approach that looks like this: for (User *user in users) {} Both are in fact identical, the last one just shows the recent attempt to make Objective-C a little more modern.

Swift has a pretty easy to use, yet very powerful console function named print(). With this simple function declaration you can print almost anything to the console, while even doing some heavy work, like mathematic operations or concatenating strings. The Objective-C equivalent is called NSLog() and works a little different. Here’s an example that shows the Swift and Objective-C variants respectively: print("User \(user.name) is \(user.age) old") NSLog(@"User %@ is %i old", user.name, user.age);

Confused? I sure was when I first saw this. While the Swift print statement is pretty straightforward in that it lets us interpolate strings, Objective-C is a little different. The %@ and %i are placeholders for NSString and NSInteger respectively, which allow us to add the values after the actual text you want to print to the console. Also, there’s an @ sign at the beginning of the text, which is an indicator for Objective-C that a text is about to start.

Semicolons, method calling and other differences

Lastly I would like to talk about some other differences between Swift and Objective-C. First of all you have to remember to end everything in Objective-C with a semicolon ;. Forget one and the compiler will give you a very hard time. Method calling is also very different: While Swift offers us the ability to call methods by dot notation, Objective-C wraps everything in square brackets like so:
[user call];

Type declarations are a little too complicated to cover in a single paragraph (I could actually write another blog post for this), but let me just tell you this: While they don’t look as good in Objective-C as they do in Swift, they are in fact a little easier to use. Objective-C types always come with two files, a header file and an implementation file. The header is responsible for exposing properties and methods that can be used across your project, as long as you create an instance of the type. The implementation file then takes those properties and methods and actually implements the functionality. Furthermore, every property and/or method you declare in the implementation file doesn’t get exposed to the outside world. In Swift we can achieve the same with the public, private and fileprivate keywords, but those also make our code a little harder to read. Let’s take a look at an example, first the Swift version:

struct User {
	let name: String	//public property

	private func call() {}	//private function

Now Objective-C, starting with the header file:

@interface User : NSObject

@property (nonatomic, strong) NSString *name; //public property


And the implementation:

@implementation User

-(void)call {} //private method


Object pointers

As described above objects (classes) in Objective-C aren’t used directly across your project, but instead the system wants us to use pointers to refer to them. Such a pointer is marked with an asterisk * and helps the system in finding this particular object. Other than variables, which live and die according to the scope they are used in, objects might need to live a little longer. Let’s take a look at an example:

- (void)anInt {
	int someInteger = 0;

The variable someInteger only lives as long as it’s in the scope of the method defined above. You can’t access it from outside the method declaration.
In the following example we’re going to use an object though, which is accessible from everywhere in your app:

-(void)sayHello {
	User *user = [[User alloc] init];

In this case the variable user is again limited to the scope of the method, but the object User can be used wherever you need it.
Why you need to know this? Well, this is exactly how reference types, e.g. classes, work in Swift, too. We don’t need to add any special character to initialize our class, but under the hood - whenever you write let user = User() - the system creates a pointer to that object in memory so it can find it. If you then call a method on that object - like user.call() - the method actually gets called on the pointer of that object.


Well, there won’t be one in this post. I just wanted to show you some of the things that you need to deal with when working with Objective-C, especially after learning Swift first. In the end it really depends on what you think you will need in the future. For writing apps learning Swift will more than suffice. While Swift is easy to read and work with, there’s still a ton to learn to really master it. Do you want to learn more, or are just curious, go ahead and give Objective-C a chance. Yes, it’s complicated and verbose, but it might help you appreciate Swift even more. And lastly, it even helps if you’re “just” able to read Objective-C code to make sense of Apple’s outdated code examples and StackOverflow’s older articles.