Creating iOS Frameworks

Have you ever wanted to share a chunk of code between two or more of your apps, or wanted to share a part of your program with other developers?

Maybe you wanted to modularize your code similarly to how the iOS SDK separates its API by functionality, or perhaps you want to distribute your code in the same way as popular 3rd parties do.

In this iOS frameworks tutorial you’ll learn how to do all of the above!

In iOS 8 and Xcode 6, Apple provided a new template, Cocoa Touch Framework. As you’ll see, it makes creating custom frameworks much easier than before.

Frameworks have three major purposes:

  • Code encapsulation
  • Code modularity
  • Code reuse

You can share your framework with your other apps, team members, or the iOS community. When combined with Swift’s access control, frameworks help define strong, testable interfaces between code modules.

In Swift parlance, a module is a compiled group of code that is distributed together. A framework is one type of module, and an app is another example.

In this iOS frameworks tutorial, you’ll extract a piece of an existing app and set it free, and by doing so, you’ll learn the ins and outs of frameworks by:

  • Creating a new framework for the rings widget
  • Migrating the existing code and tests
  • Importing the whole thing back into the app.
  • Packing it up as an uber-portable CocoaPods
  • Bonus: Setting up a repository for your framework

By the time you’re done, the app will behave exactly as it did before, but it will use the portable framework you developed! :]

Getting Started

Download the Phonercise Starter Project.

Phonercise is a simple application that replicates the Apple Watch Activity app, except it measures your phone’s physical activity. The three rings on the main view represent movement, standing and exercise.

To get the most out of the project, you’ll need to build and run on an actual iOS device, and turn on the volume. Go ahead now, build and run!

Move the phone around to get credit for movement, and “exercise” the phone by shaking it vigorously — that’ll get its heartrate up. To get credit for standing up, just hold the phone upright.

The logic for the app is pretty simple:

  • ActionViewController.swift contains the view lifecycle and motion logic.
  • All the view logic is in the files in the Three Ring View folder, where you’ll find ThreeRingView.swift, which handles the view, and Fanfare.swift, which handles the audio. The other files handle the custom drawing of the shading, gradient and shapes.

The ring controls are pretty sweet. They’ve got an addictive quality and they’re easy to understand. Wouldn’t it be nice to use them in a number of applications beyond this fun, but completely silly app? Frameworks to the rescue!

Creating a Framework

Frameworks are self-contained, reusable chunks of code and resources that you can import into any number of apps and even share across iOS, tvOS, watchOS, and macOS apps.

If you’ve programmed in other languages, you may have heard of node modules, packages, gems, jars, etc. Frameworks are the Xcode version of these. Some examples of common frameworks in the iOS SDK are: Foundation, UIKit, AVFoundation, CloudKit, etc.

Framework Set Up

In Xcode 6, Apple introduced the Cocoa Touch Framework template along with access control, so creating frameworks has never been easier. The first thing to do is to create the project for the framework.

    1. Create a new project. In Xcode, go to File/New/Project.
    2. Choose iOS/Framework & Library/Cocoa Touch Framework to create a new framework.
  1. Click Next.
  2. Set the Product Name to ThreeRingControl. Use your own Organization Name and Organization Identifier. Check Include Unit Tests. That’s right! You’re going to have automated tests ensure your framework is bug free
  3. Click Next.
  4. In the file chooser, choose to create the project at the same level as the Phonercise project.
  5. Click Create.

Now you have a project (albeit a boring one) that creates a framework!

Add Code and Resources

Your current state is a framework without code, and that is about as appealing as straight chocolate without sugar. In this section, you’ll put the pod in CocoaPods by adding the existing files to the framework.

From the Phonercise source directory, drag the following eight files into the ThreeRingControl project in Xcode:

  • CircularGradient.swift
  • coin07.mp3
  • Fanfare.swift
  • RingLayer.swift
  • RingTip.swift
  • ThreeRingView.swift
  • Utilities.swift
  • winning.mp3

Make sure to check Copy items if needed, so that the files actually copy into the new project instead of just adding a reference. Frameworks need their own code, not references, to be independent.

Double-check that each of the files has Target Membership in ThreeRingControl to make sure they appear in the final framework. You can see this in the File Inspector for each file.

Build the framework project to make sure that you get Build Succeeded with no build warnings or errors.

Add the Framework to the Project

Close the ThreeRingControl project, and go back to the Phonercise project. Delete the six files under the Three Ring View group as well as the two MP3 files in Helper Files. Select Move to Trash in the confirmation dialog.

Build the project, and you’ll see several predictable errors where Xcode complains about not knowing what the heck a ThreeRingView is. Well, you’ll actually see messages along the lines of “Use of undeclared type 'ThreeRingView'“, among others.

Adding the Three Ring Control framework project to the workspace is the solution to these problems.

Add the Framework to the Project

Right-click on the root Phonercise node in the project navigator. Click Add Files to “Phonercise”. In the file chooser, navigate to and select ThreeRingControl.xcodeproj. This will add ThreeRingControl.xcodeproj as a sub-project.

Note: It isn’t strictly necessary to add the framework project to the app project; you could just add the ThreeRingControl.framework output.

However, combining the projects makes it easier to develop both the framework and app simultaneously. Any changes you make to the framework project are automatically propagated up to the app. It also makes it easier for Xcode to resolve the paths and know when to rebuild the project.

Even though the two projects are now together in the workspace, Phonercise still doesn’t get ThreeRingControl. It’s like they’re sitting in the same room, but Phonercise can’t see the new framework.

Try linking the framework to the app’s target to fix this problem. First, expand the ThreeRingControl project to see the Products folder, and then look for for ThreeRingControl.framework beneath it. This file is the output of the framework project that packages up the binary code, headers, resources and metadata.

Select the top level Phonercise node to open the project editor. Click the Phonercise target, and then go to the General tab.

Scroll down to the Embedded Binaries section. Drag ThreeRingControl.framework from the Products folder of ThreeRingControl.xcodeproj onto this section.

You just added an entry for the framework in both Embedded Binaries and Linked Frameworks and Binaries.

Now the app knows about the framework and where to find it, so that should be enough, right?

Build the Phonercise project. More of the same errors.

Access Control

Your problem is that although the framework is part of the project, the project’s code doesn’t know about it — out of sight, out of mind.

Go to ActionViewController.swift, and add the following line to the list of imports at the top of the file.

import ThreeRingControl

It’s critical, but this inclusion won’t fix the build errors. This is because Swift uses access control to let you determine whether constructs are visible to other files or modules.

By default, Swift makes everything internal or visible only within its own module.

To restore functionality to the app, you have to update the access control on two Phonercise classes.

Although it’s a bit tedious, the process of updating access control improves modularity by hiding code not meant to appear outside the framework. You do this by leaving certain functions with no access modifier, or by explicitly declaring them internal.

Swift has three levels of access control. Use the following rules of thumb when creating your own frameworks:

  • Public: for code called by the app or other frameworks, e.g., a custom view.
  • Internal: for code used between functions and classes within the framework, e.g., custom layers in that view.
  • Fileprivate: for code used within a single file, e.g., a helper function that computes layout heights.
  • Private: for code used within an enclosing declaration, such as a single class block. Private code will not be visible to other blocks, such as extensions of that class, even in the same file, e.g., private variables, setters, or helper sub-functions.

Ultimately, making frameworks is so much easier than in the past, thanks to the new Cocoa Touch Framework template. Apple provided both of these in Xcode 6 and iOS 8.

Update the Code

When ThreeRingView.swift was part of the Phonercise app, internal access wasn’t a problem. Now that it’s in a separate module, it must be made public for the app to use it. The same is true of the code in Fanfare.swift.

Open ThreeRingView.swift inside of ThreeRingControlProject.

Make the class public by adding the public keyword to the class definition, like so:

public class ThreeRingView : UIView {

ThreeRingView will now be visible to any app file that imports the ThreeRingControl framework.

Add the public keyword to:

  • Both init functions
  • The variables RingCompletedNotification, AllRingsCompletedNotification, layoutSubviews()
  • Everything marked @IBInspectable — there will be nine of these

Note: You might wonder why you have to declare inits as public. Apple explains this and other finer points of access control in their Access Control Documentation.

The next step is to do essentially the same thing as you did for ThreeRingView.swift, and add the public keyword to the appropriate parts of Fanfare.swift. For your convenience, this is already done.

Note that the following variables are public: ringSound, allRingSound, and sharedInstance. The function playSoundsWhenReady() is public as well.

Now build and run. The good news is that the errors are gone, and the bad news is that you’ve got a big, white square. Not a ring in sight. Oh no! What’s going on?

Update the Storyboard

When using storyboards, references to custom classes need to have both the class name and module set in the Identity Inspector. At the time of this storyboard’s creation, ThreeRingView was in the app’s module, but now it’s in the framework.

Update the storyboard, as explained below, by telling it where to find the custom view; this will get rid of the white square.

  1. Open Main.Storyboard in the Phonercise project.
  2. Select the Ring Control in the view hierarchy.
  3. In the Identity Inspector, under Custom Class, change the Module to ThreeRingControl.

Once you set the module, Interface Builder should update and show the control in the editor area.

Build and run. Now you’ll get some rings.

Creating iOS Frameworks

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s