Using CocoaPods to just build frameworks for use elsewhere
I am definitely not a fan of CocoaPods
, I use Carthage
in all of my projects. It is not ideal but I have a way of using it that works for me.
Recently I was faced with a problem that made me use CocoaPods
but in a quite different way, just to build some frameworks to be used elsewhere without CocoaPods
.
The problem
I use GRDB.swift to work with the database in iOS applications, especially because it support using SQLCipher to have the database encrypted. The current version 3.x
has some problem when used by Xcode 10.2 and Swift 5 so using the latest 4.0
is recommend.
GRDB.swift
never supported Carthage
but there was always a way to make it work. I usually just needed to delete some of the shared schemes and run carthage build
instead of carthage bootstrap
. I was not able to make Carthage
work with 4.0
, mainly because the targets changed. There is no GRDBCipher
target for use with SQLCipher
anymore, just a podspec
definition
s.subspec 'SQLCipher' do |ss|
ss.source_files = 'GRDB/**/*.swift', 'Support/*.h'
ss.framework = 'Foundation'
ss.dependency 'SQLCipher', '>= 3.4.0'
ss.xcconfig = {
'OTHER_SWIFT_FLAGS' => '$(inherited) -D SQLITE_HAS_CODEC -D GRDBCIPHER -D SQLITE_ENABLE_FTS5',
'OTHER_CFLAGS' => '$(inherited) -DSQLITE_HAS_CODEC -DGRDBCIPHER -DSQLITE_ENABLE_FTS5',
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_HAS_CODEC=1 GRDBCIPHER=1 SQLITE_ENABLE_FTS5=1'
}
end
It looks like only using CocoaPods
and SwiftPM
is now supported, there is not even an easy way to do manual installation.
I had to decide how to integrate 4.0
to my project in the best way possible.
Possible solutions
There were a few solution that came to mind
Using CocoaPods
This would be the easiest solution. I would just create a Podfile
with
# GRDB with SQLCipher 3
pod 'GRDB.swift/SQLCipher'
pod 'SQLCipher', '~> 3.4'
and make it integrate into the existing workspace of my application.
It would probably work but I really do not want to use CocoaPods
and it would be a bit strange having 2 libraries added via CocoaPods
and all the other libraries via Carthage
.
Creating projects for GRDB.swift and SQLCipher and integrating them
Another option would be to create separate projects for GRDB.swift
and SQLCipher
, add them to the workspace of the application and link everything properly. This is easier said than done.
Creating a project for GRDB.swift
and applying the special build flags from he podspec
shown earlier would not be a problem. The problem would be creating a project for SQLCipher
, it would have to be done just from reading their podspec
.
Not impossible but managing updates would be quite hard even when adding both libraries as git submodules.
Using CocoaPods just to build GRDB and SQLCipher and use the .framework
files directly
I came up with a different solution. The main idea was to let CocoaPods
generate and build a project with both libraries and then use the resulting .framework
files in my main project directly. This solution needed a bit of knowledge and tinkering but I decided to go for it.
Creating a separate project
I created a new iOS framework project called GRDBCipher
. The name is not important and it is a completely empty project, no filed other than what the Xcode template creates.
I then added GRDB.swift
and SQLCipher
using CocoaPods
to this project
platform :ios, '10.0'
use_frameworks!
target 'GRDBCipher' do
# GRDB with SQLCipher 3
pod 'GRDB.swift/SQLCipher', :git => 'https://github.com/groue/GRDB.swift', :branch => 'GRDB-4.0'
pod 'SQLCipher', '3.4.2'
end
When I built the project Xcode created GRDB.framework
and SQLCipher.framework
that looked ready to be used.
Dealing with multiple architectures
When you do a build in Xcode you do it for a certain architecture. Either for the iOS simulator or for a real ARM device. GRDB.framework
and SQLCipher.framework
created by building the new GRDBCipher
project can only be used either with the iOS simulator or a real device. To fix this issue you have to create a so called “fat” framework containing all the architectures.
The easiest way to do this was adding an aggregate target (File | New target | Cross-platform | aggregate
) to the Pods
project. I changed its build configuration to Release
and added both GRDB.framework
and SQLCipher.framework
as its Target dependencies
.
I then needed to add scripts to build both frameworks for all the architectures and merge them together using lipo
. I copied the scripts for this from an Instabug post about Creating and Distributing an iOS Binary Framework.
I had to make slight changes to the script because GRDB.swift
uses GRDB.swift
as the scheme name but GRDB.framework
instead of GRDB.swift.framework
as the name of the framework that actually gets built. I also added a script that copies the resulting fat GRDB.framework
and SQLCipher.framework
to a directory in the main project.
Every time I updated the libraries using CocoaPods
the project got regenerated, but I could then just revert the project changes so I did not have to add the aggregated target again.
With this setup, whenever I need to build a new version of GRDB.swift
, I just update CocoaPods
in the GRDBCipher
project and run the aggregate target. After a few minutes the frameworks are built and ready in the correct folder.
I created a Github repository showing the whole project: https://www.github.com/igorkulman/GRDBCipher.
Adding the frameworks to the main project
Both GRDB.framework
and SQLCipher.framework
built as fat libraries can be embedded to the application just like any other static framework.
Update: Meanwhile I found a much simpler solution. Just install the cocoapods-rome plugin for CocoaPods
and use it in a Podfile
platform :ios, '10.0'
use_frameworks!
plugin 'cocoapods-rome', {:pre_compile => Proc.new { |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['BITCODE_GENERATION_MODE'] = 'bitcode'
config.build_settings['ENABLE_BITCODE'] = 'YES'
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'YES'
end
end
target 'GRDBCipher' do
# GRDB with SQLCipher 4
pod 'GRDB.swift/SQLCipher'
pod 'SQLCipher', '~> 4.0'
end
With this Podfile
running pod install
will build both GRDB.framework
and SQLCipher.framework
.