Thomas Denney

SQLite in Swift frameworks

N.B. If you are using Xcode 6.1+ these instructions DO NOT WORK. In the mean time I would recommend looking at Stephen Celis’ SQLite.swift, which makes working with SQLite in Swift really easy.

I have an unusually specific use case for Swift: I want to use it to replace the model layer in one of my apps, and to do so I wanted it in a separate framework (given that these are so easy in Xcode 6). My model layer works off of on top of FMDB + SQLite, so that was a must have. However, the latest beta of Xcode 6 (b4) removed bridging headers from frameworks - instead you have to add Objective-C imports to the ‘umbrella header’ of the framework. Unfortunately ‘sqlite3 is a non-modular header import’, which meant that I couldn’t import FMDB into the Swift framework at all:

Lots of errors…

This was very frustrating because the Objective-C version of the framework would build perfectly! The solution, however, is to use module maps. These are part of the LLVM compiler that allow you to map non-modular libraries and frameworks such as sqlite3, which means that they can be used by Swift.

Here’s what I did to setup FMDB:

  1. Create a new Objective-C framework in an Xcode workspace for FMDB. I then added all of the FMDB headers and source files
  2. Ensured that all FMDB headers were made public and that they were included in FMDB.h
  3. Linked libsqlite3.dylib
  4. Ensured that the ‘Defines Module’ was ‘Yes’ in the FMDB build settings

Then I created Swift framework that used FMDB:

  1. Create a new Swift framework (I called it ModelSwift)
  2. Link it with FMDB
  3. Add #import <FMDB/FMDB.h> to the umbrella header (ModelSwift.h)
  4. Create a module map (sqlite3.modulemap) and add it to the project (you could place it anywhere, however):
module sqlite3 [system] {
    header "/Applications/Xcode6-Beta4.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.0.sdk/usr/include/sqlite3.h"
    link "sqlite3"
    export *
}

module sqlite3simulator [system] {
    header "/Applications/Xcode6-Beta4.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.0.sdk/usr/include/sqlite3.h"
    link "sqlite3"
    export *
}

You then have to add the path to the directory that this the module map is stored in to the ‘Import Settings’ of the Swift framework:

Swift framework

Once you’ve done this you’ll be able to freely use FMDB in your Swift framework. Once you’ve built the framework and you’re importing it into an app you will also need to add the import path to your app’s build settings to ensure that it picks up the module map as well (I’m not quite sure why this is). I found that I also need to add an empty Swift file to my Objective-C app so that it would allow me to set the import paths. You may also need to enable non-modular headers in the build settings of the app.

Hopefully a future release of the beta will fix all of this, but this definitely works for now.