KPCC API Client: Creating a Framework

Jeff Campbell
KPCC Labs
Published in
8 min readMar 6, 2018

It is perhaps the worst-kept secret that KPCC is working on a new version of our iPhone app, least of all to me — the guy tasked with writing it.

The design goals of that project are many (and will result in at least one dedicated blog post, I promise!), but one of the more compelling opportunities is that of creating a more modular codebase. And to do that, one of our early ideas was to break all interaction with the KPCC API out into a separate framework.

The KPCC API is the conduit through which our iPhone app — and some of our other projects — speak to our servers. This is how we retrieve lists of programs, news headlines, and episodes to display to the user. A non-trivial portion of our app involves making various network calls to our JSON-based API and building a model (using Program, Article, and Episode struct instances, among others) to be used by our app.

Wouldn’t it be nice if that were split out from the rest of the app? I agree!

Here’s why:

  • Modularization encourages programmers to enforce a strict separation of concerns between the API client code and the rest of the project. This improves maintainability.
  • The API client can be reused in other Swift (and Objective-C!) projects, even on other platforms like macOS, tvOS, watchOS, or fancy server-side Swift frameworks like Vapor. While we have nothing to announce at this time, this does open the door to future experimentation as almost anything we do will interact with the API.
  • Code reuse means that there is simply less code to debug, and what code there is is used more often so it tends to become more solid as any seams are found and smoothed over.

Of course, in the heady early days of any project it’s easy to write an awful lot of code to bootstrap it without due consideration for keeping it modular, and as such our API client code was initially integrated directly into the project. But now that we are on the cusp of releasing our new iPhone app, I’ve had some time to revisit the idea of a separate KPCC API client project in the form of a framework.

Creating a Framework

Frameworks are collections of related code that can be embedded within your Xcode projects to provide common functionality. Every iOS app depends on a bunch of system-provided frameworks (hello, UIKit!), and most non-trivial ones include at least a couple 3rd party frameworks.

Creating our own framework wasn’t hard, but there were a few gotchas. I’ll detail the steps below, and provide some guidance to help you create your own.

To create a framework in Xcode, select File > New > Project from the menu bar, then make sure you’ve selected iOS for your project template type. Select the Cocoa Touch Framework option and hit Next.

Where It All Begins

You’ll be prompted to supply a Product Name. This is the name of your framework, and should be short and descriptive without spaces or hyphens (I used “KPCCAPIClient”). Hit Next and save the project wherever you like.

Making the Cut

Once I created our project, I started moving API-related source files over from our main iOS app project. For me, this was a moment of reckoning as I quickly realized that the API-related code commingled with bits of non-API-related code and would have to be separated. While this resulted in a fair bit of tedious pruning, it also validated the decision to make an external framework.

How does one know what to add and what to leave in your existing project? Well, it depends on the project. I’d decided that this project should be limited to handling API calls and defining the model structs they generate. Nice and tidy, like any framework should be.

Also, needless to say, anything dealing with views — representations of data — were kept out. Projects using our framework would be best served by handling that task itself in a way that best fits their purpose.

Once you’ve moved everything over and excised every bit of ill-fitting code you can find, you should be able to build your framework project without any warnings or errors (unless it already had them, in which case you should address those posthaste!).

Embedding

Embedding your framework in another project is pretty simple. Simply open your app project and drag the framework’s Xcode project file (ex. “KPCCAPIClient.xcodeproj”) from the Finder into the top level of the file navigator hierarchy.

What a Drag

Now, expand the framework project and then its Products group. You should see your framework file (ex. “KPCCAPIClient.framework”) there. Next, select your main project in the file navigator so that you can see its associated targets, then select the target you wish to use your framework with.

Make sure that the General section is visible, then drag your framework file into the Embedded Binaries list. This will make it available to your app.

The Big Drop

See the multiple frameworks listed under “Products” in the screenshot above? Right now, you’ll just have one. More on this in a moment.

Once you’ve done that, you should be able to import the framework in your project wherever you use it, like this:

import UIKit
import KPCCAPIClient
class ViewController: UIViewController {
override func viewDidLoad() {
// ...
}
}

Access Denied!

You may be surprised to find that perfectly good code moved to a framework fails to compile once embedded in an app, rendering it positively useless. If you receive any errors, it is likely that the access control levels you’ve set for your classes and structs do not allow for access outside of their defining module (the framework).

This is a perfect opportunity to revisit the encapsulation of your classes, structs, methods, and properties! Set anything intended for use by embedding projects to public and mark the rest as internal, private, or fileprivate. Apple’s documentation explains Swift access control in greater detail, but in general it is recommended that you expose as little outside the framework as possible.

Stay on Target

Remember, one of the goals of this framework is to create a platform-agnostic tool with which to interact with our server API, but so far we’ve only created a Cocoa Touch (iOS) framework. To bring this functionality to other platforms we’re going to need to add some targets.

To do this, select your framework project in the Xcode file navigator and then, below the list of targets, hit the + button. This will display a template selector (much like the one you used initially to create the project), from which you should select the associated platform’s framework template. I started with macOS, then repeated this step with watchOS and tvOS. A full set!

iOS, macOS, watchOS, and tvOS Frameworks

You will likely need to go through all of your source files and ensure that they are exposed to each target you’ve created. This can get pretty tedious, but it helps to know that you can shift-click multiple files and do a bunch of them all at once if needed. With the file(s) selected, show the Utilities pane and display the File Inspector section. Ensure that each target is checked.

Check All Targets

Once you’ve created all the targets you need, you’ll likely want to do some cleanup for each target:

  1. First, with a target selected, select Build Settings and change the Product Name value to whatever you’d like to import the resulting framework as. For this project I’ve named each the same (“KPCCAPIClient”) for consistency (this is why you saw multiple framework products with the same name earlier).
  2. You may also want to change the target’s Deployment Target value so that it can be used with older OS versions, as Xcode will default this value to the latest and greatest and you may want to expand your support a bit.
  3. You can use a single Info.plist file for all targets, if you wish. If so, delete all but the one you wish to share (you may want to rename it or move where it is located in the project to reflect that it is shared) and change the paths for each target to point to it. Once you’ve done this you can delete the platform-specific Info.plist files.
  4. You can also use a single umbrella header for each target. First, delete all but the one you wish to use (ex. “KPCCAPIClient.h”). Then, as you did with your source files, select that umbrella header in the file navigator and within the Utilities pane (within the File Inspector section) ensure that the Target Membership for that file is checked for each framework. Also mark each file as being Public. You can then delete any unused umbrella headers.
This Looks Familiar

Cross-Platform World

Writing cross-platform code is a bit beyond the scope of this post, but here is a useful trick for dealing with frameworks that reference platform-specific code. You can conditionally expose code to the compiler based on platform easily, like this:

Platform-Specific Code Blocks

This is useful if you need platform-specific imports, as above, or reference things like UIColor and NSColor (for iOS/tvOS and macOS, respectively). You can learn more about conditional compiler blocks in the Swift language reference.

That’s It!

This is, in a nutshell, how we created a cross-platform KPCC API Client framework.

There was one other really good reason for creating this framework that I neglected to mention above, but that will have to wait until my next blog post. Until then, good luck with modularizing your code!

--

--