/ Swift

How to make a Swift framework?

Creating a Swift framework shouldn't be hard. This tutorial will help you making a universal framework for complex projects.

TL;DR

I made a github repository that contains all the code examples explained below, so grab that and start using all the Swift framework templates.

Frameworks

What is a framework?

A framework is a hierarchical directory that encapsulates shared resources, such as a dynamic shared library, nib files, image files, localized strings, header files, and reference documentation in a single package.

So in a nutshell, a framework is a highly reusable component for your apps.

How to make it?

There is an article about Xcode conventions which will help you organize your projects, you should check that too if you haven't before.

Traditional way

There is a traditional way to make a framework in Xcode. I'm going to create a shared framework for all the apple platforms (iOS, macOS, tvOS, watchOS), which is going to be capable of logging things to the standard console.

Let's make the project:

  • Create a new project using one of the framework targets
  • Follow the instructions fill & name all the fields
  • Add all the other platform framework targets
  • Rename all targets according to the platform names

Frameworks

Now your project should look something like this,
note that there are no tests for the watchOS framework target.

Now in Finder:

  • Create a Sources folder and move all the Swift and header files there
  • Create an Assets folder with platforms subfolders
  • Move all the Info.plist files into the correct platfrom subdirectory
  • Create a Tests folder and move test files there

Back to Xcode:

  • Remove every group and add the new Folders from Finder
  • Check that every target has the correct files (framework & tests)
  • Inside the header file, replace UIKit depencency with Foundation

Framework-Groups

The goal is to achieve a structure like this.

Project settings:

  • Select the correct plist files for the targets
  • Set your bundle identifiers (use my conventions)
  • Setup platform versions (advice: support 1 older version too)
  • Setup the plist files for the tests from the build settings pane
  • Set the product name (Console) in your framework build settings
  • Check your build phases and add the public header file.

Framework-Settings

Scheme settings:

  • Go to the scheme settings and setup shared schemes for the frameworks
  • Gather coverage data if you need it

Framework-Scheme

  • Write your framework you can use Swift "macros" to detect platforms

NOTE: There is a flag in Xcode to allow app extension API only, if you are embedding your framework inside an application extension it should be enabled!

Congratulations, now you have your brand new Swift 4 framework made in the traditional way. Let's continue with a neat trick.

Universal cross platform framework

It is possible to create a multiplatform single scheme Xcode project with cross platform support for every platform, but it's not recommended because it's a hack. However multiple open source libraries do the same way, so why shouldn't we.

Framework-Universal

  • Delete all the targets, schemes, except macOS!!!
  • Rename the remaining target, scheme (we don't need platform names)
  • Use the project configuration file, set the xcconfig on the project

Project-Config

  • Delete Info.plist files, use one for the framework and one for the tests
  • Rename bundle identifier (we don't need platform names there too)

NOTE: States can be mixed up if you are building for multiple platforms, however this is a nice clean way to support every platforms, without duplications.

How to use it?

Embedding

Embedding your framework is the most straightforward thing to do. You can simply drag the framework project to another Xcode project, the only thing left to do is to the embedded the framework into the application. You can go to the embedded binaries section inside the general project info tab and add the framework as a dependency.

Framework-Usage

Carthage

First you need to install carthage, but that's really easy with homebrew. Next, you can simply Create a cartfile like this below and run carthage update from Terminal.

github "corekit/corekit" "master"

Cartfile example dependency from a github repo's master branch

You still have to play with build phases a little bit (read me), but carthage is a nice way to have dependencies inside your project files with so little overhead.

CocoaPods

I really hate CocoaPods, because:

  • it's a huge centralized beast
  • generates a workspace for your project
  • messes up your project configs
  • adds new build phase scripts
  • master repo update is insanely slow

If you want to make a pod from your framework it's up to you. I also made a few pods in the past, but it was a real pain in the ass to maintain it, so I'm would stay away from CocoaPods forever if it would be possible. Anyway, there are a few links in the end of the article that might help you if you want to make a pod.

Swift Package Manager

With SPM, you have to make a Package.swift file and you'll be able to build your targets with the swift build command. I would not recommend this method until a full Xcode support arrives for Swift packages, right now it's the best for server side apps, or other command line applications, but not for appleOS apps.

External sources