Back to blog

Objective-C for Swift Developers


Aasif Khan
By Aasif Khan | December 29, 2021 2:11 pm  | 5-min read

Is Objective-C officially dead? If you look at the rise in popularity of programming language Swift since 2014, you’d most certainly think so.

Swift is easier to learn than Objective-C, it has a powerful syntax and it’s widely used for iOS, macOS, tvOS, watchOS and iPadOS apps. Swift is open source and supported on Linux and Windows (5.3-6), which means it has the power to break free from the Apple ecosystem.

Has Swift replaced Objective-C? Absolutely not! A beginner app developer’s language of choice is Swift for sure, but it pays dividends to get smart in Objective-C as well.

Objective-C is still widespread in:

Many important frameworks in the Cocoa and Cocoa Touch SDKs, ranging from UIKit to Auto Layout. They’re written in Objective-C.
Plenty of open source libraries, including AFNetworking, MBProgressHUD and ParseUI. Many of them have Swift-based alternatives.
Many apps are still coded in Objective-C, simply because they were built before Swift. It’s tough rewriting an entire app into a new language.
You can bridge Objective-C with Swift, of course. With a Bridging Header you can tap into any Objective-C codebase, and code Swift without ever looking at its Objective-C origins.

An example of where that could go wrong is the ParseUI framework, a wrapper around the Parse Server for iOS SDK that couples nicely with UI components such as table views and collection views.

What if you get stuck and need to inspect the original source of ParseUI, in Objective-C? You’d stay stuck if you don’t know Objective-C!

It’s smart to code in Swift, and know your way around Objective-C, enough to get around. That’s why I wrote this tutorial: to help you, as a Swift developer, to get to know Objective-C better.

Objective-C’s Origin Story

Objective-C was created in the early 1980s, and became part of Apple’s Mac and iOS platforms when Apple acquired NeXT, the company Steve Jobs founded when he left Apple in 1985. NeXT had licensed Objective-C from its creators, Brad Cox and Tom Love, from the company Stepstone.

When coding apps you’re still occasionally reminded of Apple’s past. The “NS” prefix some Cocoa and Cocoa Touch classes have is an abbreviation of “NeXTSTEP,” the operating system NeXT built. In Swift, most of these prefixes are gone. Behind the scenes, they’re still there though!

It’s also fascinating that Interface Builder’s inception, now part of Xcode, coincided with the creation of NeXTSTEP and Objective-C. Xcode itself was created much later, in 2003, and it was based on Project Builder – again, made for NeXTSTEP.

Just like in our age, where Instagram is now part of Facebook, Steve Job’s NeXTSTEP acquired many of its surrounding technologies. The same is true for Apple and even for Swift, which has moved into verticals like machine learning and computer vision.

Author’s Note: I started coding iOS apps in 2009-2010, with Xcode and Objective-C. Back then, iOS was called iPhone OS. I learned how to code for the first time long before that, as a teenager, with BASIC and Visual Basic. It’s a reminder to learn first principles, because technologies constantly change. Don’t worry too much about what’s popular, and invest in helpful learning techniques.

Crash-Course Objective-C Programming

First things first. What is programming? It’s telling a computer what to do. It’s an ordered list of commands – the code lines that make up a software program. The computer, or iPhone, will execute all lines of code after one another.

Programs used to be procedural, for which lines of code are executed one by one. A typical statement for procedural programming languages is GOTO, which took the program to an arbitrary line of code, or gosub, which would take your code into a subroutine. Subroutines are like functions: individual blocks of code that take input and make output.

Procedural programming has no other way of reusing a piece of code multiple times, and no way of organizing code more sensibly. A better way of organizing your code is called Object-Oriented Programming or OOP.

OOP is a way of programming which divides programming code in classes and methods. Classes are structures for your code, as you’ll see next.

Objective-C is an Object-Oriented language, and so is Swift. Objective-C adds an object-oriented extension to the C language, and uses Smalltalk’s bracket-style messaging syntax.

What’s interesting here is that programming languages borrow features from each other. There’s a parallel with Swift here, because Swift was partly inspired by functional programming languages like Haskell. In fact, Swift is slowly crossing the chasm into Linux and Windows environments – like Objective-C from NeXTSTEP to Mac, back in their day.

It’s all a mashup anyway!

The Valet Program in Objective-C

Did you see The Matrix, the movie from 1999? (If you didn’t, go watch it!)

In it, Neo (Keanu Reeves) “exits” our physical world to be reborn in the actual “real” world. The premise of The Matrix is that we all live in a computer simulation, and that people, events and physical things are controlled by computer programs.

Take for instance The Valet Program, that’s out here in our computer world. Whenever you go up to The Valet, he takes your keys and parks your car. When you return, you get back your car and drive away.

We’re going to write a small program that makes up the actions The Valet Program does. We’ll be working with two different files, called AppDelegate and Valet.

The AppDelegate is our “starting point”, it’s what the iPhone loads into it’s memory when we start The Valet Program. The Valet holds the actions, memory and logic for the program.

Fun fact: SwiftUI has the potential to retire the “App Delegate”, with its SwiftUI App and App protocol.

Declaration, Allocation and Initialization

The code that creates the valet, and makes it run, is this:

Valet *valet = [[Valet alloc] init];
[valet run];

In the code, this happens:

  1. Create a new object of the type Valet. This particular object is a so-called instance of the class Valet. First, we tell the Valet class to allocate a piece of memory for this instance with alloc, and then we tell it to initialize the Valet instance with init.
  2. Then we give our valet object a command: run.

In Swift, the same code is this:

let valet = Valet()
valet.run()

Can’t get your head around an instance and a class?

Think of it like architecture. You have a blueprint of a house, that’s the class, and a house that the workers build: an instance.

The blueprint defines where the walls, doors and windows of the house are, but you can’t live in it! That’s what you need the actual house for, the instance of the class.

In the Objective-C example, the way of coding (called syntax), with the square brackets, is very unique. It’s called a messaging system. It’s the Objective-C way of telling objects what they should do.

Objective-C’s Messaging Syntax

Let’s break down the above code sample a bit further. The following line …

Valet *valet = [[Valet alloc] init];

… is identical to this:

Valet *valet = [Valet alloc];
valet = [valet init];

Do you see that instead of writing alloc and init on one line, they’re now separated on two lines of code?

This is what it does:

  • Valet *valet means: create a new object with the name valet. It must be of class Valet. That asterisk * is a pointer, Objective-C’s syntax for indicating a reference type (as opposed to a value type).
  • The = sign is the assignment operator. It says, whatever is left of the = sign, is assigned to what’s right of it. You store the result of [Valet alloc] into variable valet.
  • The command we’re executing first is called alloc, it allocates memory for the object valet. Whatever you store on an iPhone needs memory! This alloc command is called a method.
  • There’s a catch: the Valet inside the brackets (near alloc) is written with a capital character! Any word that starts with a capital character, like this one, is a class. alloc in this sense is then a class method.
  • Next line: valet = [valet init]. Another method, named init. It is called on the object valet, and stored inside the object valet with the equals sign. Note that it’s written without a capital first character, because it’s an object (and not a class). Then, the method init is called an instance method. Why? Remember this: object valet is an instance of the class Valet. The init is therefore an instance method, because it is called on the instance of the class, not on the class itself.

So what about that pointer?

Picture the iPhone’s computer memory in your head like a city. Each memory block has an address, like a house. In each house you can store 1 object.

We’ve just stored an instance of class Valet in variable valet. We can retrieve our object later, because we know it’s name valet. The iPhone memory however stores it under a different address, like 0x00ad6f88.

We can’t write our program with names like that! It’s too complicated. In order to work with readable names, we create a pointer. Our name valet points to 0x00ad6f88 for the computer, which in turn is the address for the actual contents of the object. The contents can include the valet’s name, how many cars he’s stored and the time he can go home. So, a pointer is a reference, a readable reference for us, and a memory address for the iPhone computer.

So, what’s the big difference between the two Objective-C code samples above?

No difference at all. But why do we write them differently and which is better? The first one, because it’s shorter. We utilize the messaging system of Objective-C to “chain” the results of methods to one another.

Picture this: you’re waking up and then making breakfast. First we create an object called you, and then we do this:

you = [you wakeUp];
you = [you goDownStairsToKitchen];
you = [you getKnifeBreadButter];
you = [you makeSandwich];
you = [you eatSandwich];

As you can see, we’ve used several instance methods to get from bed to a full stomach. We assign to the variable you temporarily, so we can save the results of each of your actions. Each time we write = and perform an action, it overwrites the previous action.

We can also write it like this:

[[[[[you wakeUp] goDownStairsToKitchen] getKnifeBreadButter] makeSandwich] eatSandwich];

This doesn’t mean you did all of that at the same time. No, all methods are still executed in after one another. However, we’ve chained all the results.

You can’t get the knife, bread and butter before getting down to the kitchen. The result of getting the knife etc. is chained to the result of going down to the kitchen.

In the first code sample we’ve stored the result of each of the commands inside you with the assignment operator. In the second sample we’ve omitted the equal sign and put it all in one line, all held together by square brackets. The result of a method is used as the basis for the next, without storing it in a temporary object (like you = …).

In Swift, the first code sample would look like this:

you = you.wakeUp()
you = you.goDownStairsToKitchen()
you = you.getKnifeBreadButter()
you = you.makeSandwich()
you = you.eatSandwich()

The more condensed way of coding, in Swift, is this:

you.wakeUp().goDownStairsToKitchen().getKnifeBreadButter().makeSandwich().eatSandwich()

Interestingly, that’s not much different from the functional programming style used nowadays in Swift and SwiftUI:

Text(“Hello World”)
.font(.title)
.color(.blue)

You can clearly see the differences between Objective-C syntax and Swift syntax. Objective-C calls methods and properties with those square brackets [ ], whereas Swift uses dot-syntax.

The most significant difference between Objective-C and many other languages, including Swift, is this []-style messaging syntax. Of course, the syntaxes differ in many more ways, but it’s a relief to know that the worst is behind you now!

Coding The Objective-C Valet

Alright, let’s put all that Objective-C to good use. We’re going to write some hypothetical, working Objective-C code.

File: Valet.h (Header)

@interface Valet : NSObject
{

}

@property (copy) NSString *name;

– (id) initWithName:(NSString *)aName;
– (void) run;

@end

File: Valet.m (Implementation)

#import “Valet.h”

@implementation Valet;

@synthesize name;

– (id) initWithName:(NSString *)aName
{
self = [super init];
if (self)
{
self.name = aName;
}

return self;
}

– (void) run
{
NSLog(@”Running valet with name %@”, self.name);
}

@end;

First, we used two files: a header file and an implementation file. The header file is used to declare our class, which means that we tell the iPhone which objects, methods and so on we’re going to use inside our class Valet.

As you can imagine, an object can store objects (kind of like sub-objects) inside itself. That’s the whole deal with Object-Oriented Programming!

The implementation file is used to write out the things we declared in the header, so the contents and logic of our code.

If you compare it to a cooking recipe, the header would be the ingredients list and the implementation the steps to cook the meal.

A header file has the filename extension .h, an implementation file the extension .m. If you want to use a class you’ve coded in another file you have to import that class at the top of the file, with the #import keyword.

Moreover, you have to “link” a header and implementation of the same class by including an import for the header file in the implementation file, like #import “Valet.h” in Valet.m.

In Swift you don’t have separate header and implementation files. Any Swift class is just saved in a .swift file. Moreover, you typically only have to import external frameworks and libraries with the import statement. In Objective-C you have to import pretty much every file or class you want to use.

Breaking Down The Code

Let’s start with the header file Valet.h

  • We start with @interface Valet : NSObject {}.
    You use an interface to declare a class. An interface is like a human face, you say: this is a face with eyes and a nose and a mouth. You use the word @interface to tell the iPhone you’re declaring a class. The colon (double point) is used to inherit NSObject, we’re telling the iPhone that we want to make a class based on the properties of NSObject – another parent class. Then, the curly braces are required, but today we won’t use them. (Normally, you define instance variables inside the curly braces.)
  • Second, we write @property (copy) NSString *name;.
    This is an easy one: we declare an object with name name, of class NSString. We want to use this object, we tell the iPhone. To be able to use it later, we give it a name: name. You see that once more we use a pointer with *, a reference address.
    We end this line with a semi-colon (dot comma) ;, it’s to tell the program that this line has ended. Now, there’s something interesting here: @property (copy). We declare this object as a property, we attach this object to the class we’re writing (Valet class). You can literally see that a name is a property of a real-world valet. Forget the (copy) for now.
  • Then, – (void) run;.
    This is a declaration of a method, named run. It has no result (methods can return results), indicated by the (void). The minus – indicates that we’re writing an instance method, and not a class method (indicated by +). Again, the ; for the line ending.
  • We close the file with @end, to indicate this is the end of the file. All files need that. No, no ; here!

Got it? We’ve declared one property called name and one method called run.

Now, for the implementation file Valet.m.

  • We start with telling the iPhone it should import our header file, with #import “Valet.h”. This way the iPhone knows where to look for the header file of this implementation.
  • Second, we write @implementation Valet;. This is an indication that the code that follows is the implementation of the Valet class.
  • Then, we write @synthesize name;. It’s for telling the iPhone we want to use the name property in this implementation. It kind of connects the property from the header to the implementation. It also creates setter and getter methods for us. We can use those methods to set the name property (to your name for instance) and read out its contents. We could write those methods ourselves, but using @synthesize is easier.
  • Then, the entire – (id) initWithName:(NSString *)aName { … block. It is a convenience method for initializing this Valet class with a name, all in one. Remember when we wrote Valet *valet = [[Valet alloc] init];? Now we can write Valet *valet = [[Valet alloc] initWithName:@”Steve”]; instead of first initializing it, and then setting its name on 2 lines. The code block first runs self = [super init];, which calls the init method of NSObject. That’s because we inherited it in our header. Then, the if-statement is executed when self has correctly initialized. Only then it executes self.name = aName;. See aName in the start of this code block? The object aName is copied and stored into self.name. When we use Valet *valet = [[Valet alloc] initWithName:@”Arthur”];, the @”Arthur” is copied into self.name. Double check that! Do you understand why? Finally, the method gives a result back with return self. The result is self, the entire object itself. And when we write Valet *valet = [[Valet alloc] initWithName:@”Arthur”];, guess what the contents of valet is? It’s returned with return self, so it’s self!
  • Then there is the – (void) run { … block. This has the contents of the run method. It doesn’t return anything (void means “empty”). The contents of our run method is NSLog( …, it shows some debug output with the function NSLog to our debug screen in Xcode. The @”Running valet with name %@”, self.name inside the parentheses is called a format string, it inserts the contents of self.name at the position of %@.
  • And again, we end with @end.

Note that in Objective-C string literals like @”Steve” start with an @-sign. In Swift, you just write “Steve”.

Remember we started with the AppDelegate? The code we used was:

Valet *valet = [[Valet alloc] init];
[valet run];
As you can see, something’s missing, it should be:

Valet *valet = [[Valet alloc] initWithName:@”Arthur”];
[valet run];

The output of this code, when used in conjunction with the class Valet we wrote, is:

Running valet with name Arthur

Awesome!

The Valet Class in Swift

The Valet class in Swift is surprisingly simple…

class Valet
{
var name = “”

init(name:String) {
self.name = name
}

func run() {
print(“Running valet with name \(name)”)
}
}

Like before we’re declaring a class called Valet. It has one initializer init(name:String) that takes parameter name and assigns it to the property self.name. When the method run() is called, it prints out “Running valet with and the value of name.

You can call this Swift class like this:

let valet = Valet(name: “Arthur”)
valet.run().

Easy, right? Now you understand why it’s smarter to learn Swift compared to Objective-C, although it’s even smarter to know a bit of Objective-C next to Swift.

Conclusion

Hooray! These are the basics of Objective-C programming. Most Objective-C from here on out is based on what you’ve learned in this tutorial. It might seem like a lot to comprehend, but now you know that this is it.


Aasif Khan

Head of SEO at Appy Pie

App Builder

Most Popular Posts