Advertisement
  1. Code
  2. iOS
Code

iOS From Scratch With Swift: Exploring the Foundation Framework

by
Difficulty:BeginnerLength:LongLanguages:
This post is part of a series called iOS From Scratch With Swift.
iOS From Scratch With Swift: Exploring the iOS SDK
iOS From Scratch With Swift: First Steps With UIKit

The Foundation framework is the bread and butter in the toolbox of an iOS developer. It provides the NSObject root class and a large number of fundamental building blocks for iOS development, from classes for numbers and strings, to arrays and dictionaries. The Foundation framework might seem a bit dull at first, but it harnesses a lot of power and is indispensable for developing iOS applications.

A Word About Core Foundation

In the previous article, I briefly mentioned Core Foundation and its relation to Foundation. Even though we won't explicitly use the Core Foundation framework in the rest of this series, it's useful to be familiar with the framework and to know how it differs from its object-oriented sibling, Foundation, which you'll use extensively.

While the Foundation framework is implemented in Objective-C, the Core Foundation framework is C-based. Despite this difference, the Core Foundation framework does implement a limited object model. This object model allows for the definition of a collection of opaque types that are often referred to as objects—despite the fact that they are, strictly speaking, not objects.

The primary goal of both frameworks is similar, enabling sharing of data and code between the various libraries and frameworks. Core Foundation also includes support for internationalization. A key component of this support is provided through the CFString opaque type, which efficiently manages an array of Unicode characters.

As I mentioned previously, toll-free bridging literally bridges the gap between both frameworks by enabling the substitution of Cocoa objects for Core Foundation objects in function parameters and vice versa.

It's important to note that Automatic Reference Counting (ARC) doesn't manage Core Foundation "objects". This means that you are responsible for managing memory when working with Core Foundation. Mike Ash wrote a great article about Automatic Reference Counting and how to use ARC with Core Foundation and toll-free bridging.

Visit Apple's Core Foundation framework reference if you want to learn more about the framework and for a complete list of the framework's opaque types.

Practice, Practice, Practice

Learning a new skill is best done through practice. Let's get started by creating a new playground in Xcode. Name the playground Foundation and set Platform to iOS. Click Next to continue. Tell Xcode where you want to save the playground and click Create.

Creating a New Playground

This is what the contents of the playground should look like.

The playground contains a comment at the top, an import statement for the UIKit framework, and a variable declaration. On the right, you can see the output of the playground.

Since we'll be working with the Foundation framework, we need to replace the import statement for UIKit with an import statement for Foundation. Remove the comment at the top, update the import statement, and remove the variable declaration.

Foundation

The Foundation framework is much more than a collection of classes for working with numbers, strings, and collections (arrays, dictionaries, and sets). It also defines dozens of protocols, functions, data types, and constants.

In the rest of this article, I will primarily focus on the classes that you'll use most often when developing iOS applications. However, I will also briefly talk about three key protocols defined by the Foundation framework, NSObjectNSCoding, and NSCopying.

Protocols

Several languages, such as Perl, Python, and C++, provide support for multiple inheritance. This means that a class can descend from—be a subclass of—more than one class.

Even though Swift and Objective-C don't provide support for multiple inheritance, they do support multiple inheritance through specification in the form of protocols. We already covered protocols earlier in this series, but there is an important detail we haven't discussed yet.

Optional and Required Methods

Remember that every method of a Swift protocol is required. If a type conforming to a Swift protocol doesn't implement every property and method defined by the protocol, the compiler will throw an error. This isn't true for Objective-C protocols. In Objective-C, methods of a protocol can be marked as optional. If a protocol method is optional, the type conforming to the protocol isn't required to implement the method.

Because the vast majority of the frameworks of the iOS SDK are written in Objective-C, we will meet a number of Objective-C protocols that define optional methods. What's important to remember is that every property and method of a Swift protocol is required.

Benefits

The benefits of protocols are manifold. When a class adopts or conforms to a protocol, the class is expected to implement the methods declared by the protocol. For some of you, protocols may be reminiscent of interfaces in Java. This means that a protocol can be used to declare the interface to an object without revealing the type of the object.

Multiple inheritance has its benefits, but it most certainly has its downsides. The advantage of protocols is that unrelated types, enums, classes, and structures, can still share similar behavior through the use of protocols.

NSObject

In addition to the NSObject root class, the Foundation framework also defines the NSObject protocol. In Swift, however, classes and protocols cannot have the same name. As a result, the NSObject protocol is known as the NSObjectProtocol in Swift.

Objects conforming to the NSObjectProtocol protocol can be asked about their class and superclass, can be compared with other objects, and respond to self. This is only a small subset of the behavior added to objects conforming to the NSObjectProtocol protocol.

NSCoding

Objects conforming to the NSCoding protocol can be encoded and decoded. This is necessary for objects that need to be archived or distributed. Object archival, for example, takes place when an object or object graph is stored to disk.

NSCopying

The NSCopying protocol declares only one method, copyWithZone(_:). If a class is to support copying objects, it needs to conform to the NSCopying protocol. Copying an object is done by sending it a message of copy() or copyWithZone(_:).

Classes

NSObject

The NSObject class is the root class of the vast majority of the Objective-C class hierarchies. Why is that important if we're interested in Swift? Remember that most frameworks of the iOS SDK are powered by Objective-C. Even if you decide to develop an application in Swift, you'll still be using Objective-C under the hood.

By inheriting from the NSObject root class, objects know how to behave as Objective-C objects and how to interface with the Objective-C runtime. It shouldn't be a surprise that NSObject conforms to the NSObject/NSObjectProtocol protocol, which we discussed a few moments ago.

Let's see what we get for free if a class inherits from the NSObject class. Add the following code snippet to your playground. This should look familiar if you've read the previous articles of this series. We declare a class, Book, that inherits from NSObject. The Book class implements two instance methods, chapters() and pages().

Because Book inherits from NSObject and NSObject conforms to the NSObjectProtocol protocol, we can ask the Book class about its behavior and inheritance tree. Take a look at the following example.

Inspecting the Book Class

We first ask the Book class about itself by invoking classForCoder() on the Book class. The classForCoder() method is a class method, which means we can invoke this method on the Book class itself. Next, we ask the Book class about its superclass or parent class, the class it directly inherits from. This returns an optional since not every class has a parent class. Unsurprisingly, the superclass of Book is NSObject.

The next line tells us that Book conforms to the NSObjectProtocol protocol, which isn't unexpected since it inherits this trait from the NSObject class. We also learn that Book doesn't conform to the NSCoding protocol.

It appears that the Book class doesn't respond to the chapters() method. This isn't that odd since chapters() is an instance method, not a class method. This is proven by the last two lines of our example in which we instantiate a Book instance and ask the instance the same question.

In the above example we use the respondsToSelector(_:) method, but you may be wondering what a selector is? What does "responding to a selector" mean? A selector is a fancy word for the name a function or method. A selector is used by the Objective-C runtime to send messages to objects. Long story short, if you read the word selector, then think function or method. The finer details are beyond the scope of this tutorial.

NSNumber

The NSNumber class is a utility class that manages any of the basic numeric data types. It is a subclass of the NSValue class, which provides an object-oriented wrapper for scalar types, pointers, structures, and object ids. The NSNumber class defines methods for retrieving the value it stores, for comparing values, and also for returning a string representation of the stored value.

Keep in mind that the value retrieved from an NSNumber instance needs to be consistent with the value that is being stored in it. The NSNumber class will attempt to dynamically convert the stored value to the requested type, but it goes without saying that there are limitations inherent to the data types that NSNumber can manage.

Let me illustrate this with an example. Add the following code snippet to your playground.

Working With the NSNumber Class

We create a new NSNumber instance by passing a Double value to init(double:). Next, we request the stored value using three different NSNumber methods. As you can see, the results don't always reflect the value stored in myNumber.

The lesson is simple, be consistent when using NSNumber by keeping track of the type that is stored in the NSNumber instance.

NSString

Instances of the NSString class manage an array of unichar characters forming a string of text. The subtle but important difference with a regular C string, which manages char characters, is that a unichar character is a multibyte character. This is similar to Swift's String type.

As its name implies, a unichar character is ideally suited for handling Unicode characters. Due to this implementation, the NSString class provides out-of-the-box support for internationalization.

I want to emphasize that the string managed by an NSString instance is immutable. This means that, once the string is created, it cannot be modified. Developers coming from other languages, such as PHP, Ruby, or JavaScript, might be confused by this behavior.

The Foundation framework also defines a mutable subclass of NSStringNSMutableString, that can be modified after initialization.

There are various ways to create string objects. The simplest way to create a string object is by calling the string() method on the NSString class. This returns an empty string object. Take a look at the class reference of NSString for a complete list of initializers.

Another common path for creating string objects is through string literals. In the next example, a string literal is assigned to stringA. At compile time, the compiler will replace the string literal with an instance of NSString.

Creating Strings in Swift

Notice that we don't rely on Swift's type inference to determine the type of stringA. The second example, stringB, shows why that is. If we don't explicitly specify the type of stringA as NSString, Swift thinks we want to create a String instance. The example also illustrates that NSString inherits from NSObject while String doesn't. Remember that the String type in Swift is a structure and structures don't support inheritance.

The NSString class has a wide range of instance and class methods for creating and manipulating strings and you will rarely, if ever, feel the need to subclass NSString.

Let's explore NSString and its mutable subclass, NSMutableString, by adding the following snippet to your playground.

We start by creating two NSString instances, one using literal syntax and one using the init(string:) initializer. A mutable string is then created by passing the first string as an argument. To illustrate that mutable strings can be modified after creation, stringB is appended to the mutable string and its value printed.

NSArray and NSSet

The NSArray class manages an immutable, ordered list of objects. The Foundation framework also defines a mutable subclass of NSArrayNSMutableArray. The NSArray class behaves very much like a C or Swift array with the difference that an instance of NSArray manages objects. The NSArray class declares a wide range of methods that facilitate working with arrays, such as methods for finding and sorting objects in the array.

It's important to understand that instances of NSArrayNSSet, and NSDictionary can only store objects. This means that it's not possible to store scalar types, pointers, or structures in any of these collection classes—or their subclasses—the compiler will throw an error if you do. The solution is to wrap scalar types, pointers, and structures in an instance of NSValue or NSNumber like we saw earlier in this article.

Add the following code snippet to your playground to explore NSArray and its mutable counterpart, NSMutableArray.

We create an array by using the init(objects:) initializer. This method accepts a variable number of arguments. Next, we print the number of objects in the array and ask the array for the object at index 2.

Because NSMutableArray inherits from NSArray, it behaves in much the same way as NSArray. The main difference is that objects can be added and removed from the array after initialization.

Before moving on, I want to say a few words about NSSet. This class is similar to NSArray, but the key differences are that the collection of objects that a set manages is unordered and that a set cannot contain duplicates.

The advantage of NSSet is that querying its objects is faster if you only need to know if an object is contained in the set. The Foundation framework also defines NSOrderedSet. Instances of this class have the benefits of NSSet, but also keep track of the position of each object.

NSDictionary

Like arrays, dictionaries are a common concept in most programming languages. In Ruby, for example, they are referred to as hashes. The basic concept is easy, a dictionary manages a static collection of key-value pairs or entries.

As in Ruby hashes, the key of an entry doesn't need to be a string object per se. It can be any type of object that conforms to the NSCopying protocol as long as the key is unique in the dictionary. In most cases, though, it's recommended to use string objects as keys.

Like arrays, dictionaries cannot store a null value. If you want to represent a null value, then you can use NSNull. The NSNull class defines a singleton object that is used to symbolize null values in arrays, dictionaries, and sets.

The singleton pattern is an important pattern in many programming languages. It limits the instantiation of a class to one object. You will deal with singleton objects frequently when developing iOS applications.

Like NSArray, the Foundation framework defines a mutable subclass of NSDictionaryNSMutableDictionary. There are various ways to instantiate a dictionary. Take a look at the following code snippet.

We declare two separate NSString objects containing the same string. We then instantiate a dictionary by invoking init(object:forKey:). Next, we ask the dictionary for the object associated with the contents of keyB and print its value.

It's important to pay attention to the details. Even though we used keyA as the key of the key-value pair and keyB as the key to fetch the value or object of the key-value pair, the dictionary gave us the correct object (as an optional). The NSDictionary class is smart enough to know that we want the object associated with string "myKey". What does this mean? Even though the objects keyA and keyB are different objects, the string that they contain is the same and that is precisely what the NSDictionary class uses to reference the object.

Before we wrap up this tutorial, I'd like to show you an example of nested dictionaries and arrays and an example of NSMutableDictionary. The following code fragment shows that a dictionary can contain another dictionary—or array—and it also shows how to work with mutable dictionaries.

Conclusion

Even though we covered a lot of ground in this article, we barely scratched the surface of what the Foundation framework has to offer. It isn't necessary to know the details of every class or function defined in the Foundation framework to get started with iOS development, though. You'll learn more about the Foundation framework as you explore the iOS SDK.

In the next article, we explore the UIKit framework and I also discuss the ins and outs of an iOS application.

If you have any questions or comments, you can leave them in the comments below or reach out to me on Twitter.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.