I want to create a custom framework in Swift, so that I can use it in multiple projects. Therefore I started with
mkdir TestFramework && cd TestFramework
swift package init --type library
and
swift package generate-xcodeproj
Then I started to create in there some classes and I built the code. The result is a .framework
file. I moved it to my other project and linked it there (therefore I just copy the .framework-folder to the Xcode-project and add it to the target). import TestFramework
works, but I have no access to any classes, I created in the Framework: Use of unresolved identifier 'TestClass'
The strange thing is: TestFramework.framework/Versions/A/TestFramework
seems to be an executable. This is the Package.swift:
let package = Package(
name: "TestFramework",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "TestFramework",
targets: ["TestFramework"]),
],
The best way to approach this is using an Xcode toplevel workspace (File -> New -> Workspace). So create a workspace named Example.
As the second step create a directory named lib and place it inside the Example directory. Placing it inside the directory is not strictly required but I do this to keep the files in a single project directory. Next run inside the lib
directory:
swift package init --type library
and add some code to create your framework.
Next create a new project using Xcode. So File -> New -> Project and store that project also inside the Example directory (Again not strictly required to put it inside Example). For now let's assume you create a Cocoa App named TestUI.
Now open your example workspace and add the two projects to the workspace using File -> Add Files to "Example". You should add the generated lib.xcodeproj
for the lib
directory and the TestUI.xcodeproj
for TestUI.
From this moment on Xcode will automatically take care of things. It will store all files from both projects in a single build directory and you can create/run/build/debug your application as you would expect.
The structure should look like this:
Example
|-- Example.xcworkspace
|-- TestUI
| |-- TestUI
| | |-- AppDelegate.swift
| | |-- Assets.xcassets
| | | |-- AppIcon.appiconset
| | | | `-- Contents.json
| | | `-- Contents.json
| | |-- Base.lproj
| | | `-- Main.storyboard
| | |-- Info.plist
| | |-- MailboxViewController.swift
| | |-- TestUI.entitlements
| | `-- ViewController.swift
| `-- TestUI.xcodeproj
`-- lib
|-- lib.xcodeproj
|-- Package.resolved
|-- Package.swift
|-- README.md
|-- Sources
| |-- ExampleLib
| | |-- Account.swift
| | |-- Credentials.swift
There is one major limitation to this approach. If your library has non Swift components, e.g. a wrapper around a C-library it will not work out of the box. You will get messages like "Cannot find required Framework xxx" if you try to use it. This would also happen if you add some required packages to your library that are 'C' based.
I have found the following work around. First go to the lib
directory and run the following command:
find lib.xcodeproj -name module.modulemap
In the case that you have used/created the xxx
library you get the following output:
lib.xcodeproj/GeneratedModuleMap/xxx/module.modulemap
Now go to the Example workspace and select TestUI -> Build Settings -> All and enter in the Search box "Other Swift Flags".
Open that and add the following two fields:
-Xcc
-fmodule-map-file=$(SRCROOT)/../lib/lib.xcodeproj/GeneratedModuleMap/xxx/module.modulemap
And that's it. It should all work now. For compilation and running it is not even necessary to embed binaries in the TestUI application although I assume it will be necessary for distribution.