Xcode distribute individual frameworks with common dependency framework(s)
Requirement
In my recent project, there was a requirement as follows
- Our organization supports N number of BLE (Bluetooth Low Energy) devices.
- Our clients will build their own app which uses 1 or more BLE devices.
- We have to share a BLE framework which should have API for the devices which our client is interested in.
- We should not explore the API of other devices that client is not interested in.
- Also we should not share the source code with any of our client.
Approach
Created a base Swift Package as BaseBLE which has a dependency of a 3rd party BLE based Swift Package. And this package will have source code with all basic BLE communication logic.
Then created N number of Swift Packages for individual devices, which depends on BaseBLE swift package.
Then we can use this in our sample iOS application and can able to communicate with all the supported BLE devices.
Now we have to convert this into Frameworks or XCFrameworks, so we can share it with our client obscuring the source code.
Swift Packages to XCFrameworks conversion
BaseBLE package to XCFrameworks
- Navigate to BaseBLE package directory from terminal.
- Create GenerateXcodeproj.xcconfig with following content.
MACOSX_DEPLOYMENT_TARGET = 10.10
IPHONEOS_DEPLOYMENT_TARGET = 9.0
WATCHOS_DEPLOYMENT_TARGET = 2.0
TVOS_DEPLOYMENT_TARGET = 9.0
BUILD_LIBRARY_FOR_DISTRIBUTION = YES
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) XCODE_PROJECT
- Our BaseBLE package has resource bundle. To work seamlessly when we build either using Swift package or using framework, we add XCODE_PROECT as active compilation flag. Please find the below sample code
#if XCODE_PROJECT
private static var bundle = Bundle(for: BaseBLE.self)
#else
private static var bundle = Bundle.module
#endif
- Generate a XCode project with following command.
swift package generate-xcodeproj — xcconfig-overrides GenerateXcodeproj.xcconfig
- Open the generated XCode project.
- Mark all dependent framework and libraries as Do Not Embed. (As we will generate these individual XCFramework from these each dependent framework and will share it with our client, do avoid duplicating these dependencies in individual device framework)
- Add required resources against BaseBLE target.
- Add BaseBLE scheme under Manage Schemes.
- Execute following script from project directory.
Sample command,
./GenerateBaseBLEXCFramework.sh -p iOS, iOS Simulator
XCFrameworks for BaseBLE and all its dependency frameworks will get generated in build directory.
Repeat the above steps whenever we make changes in the BaseBLE source code.
BLEDevice package to XCFramework
- Navigate to BLEDevice package in terminal.
- Create GenerateXcodeproj.xcconfig with following content, the reason for the use of XCODE_PROJECT is explained above
MACOSX_DEPLOYMENT_TARGET = 10.10
IPHONEOS_DEPLOYMENT_TARGET = 9.0
WATCHOS_DEPLOYMENT_TARGET = 2.0
TVOS_DEPLOYMENT_TARGET = 9.0
BUILD_LIBRARY_FOR_DISTRIBUTION = YES
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) XCODE_PROJECT
- Generate a XCode project with following command.
swift package generate-xcodeproj — xcconfig-overrides GenerateXcodeproj.xcconfig
- Open the generated XCode project.
- Mark all dependent framework and libraries as Do Not Embed. (As we have generated the dependent XCFrameworks above.)
- Add required resources against BLEDevice target.
- Add BLEDevice scheme under Manage Schemes.
- Execute following script from project directory.
Sample command,
./GenerateBLEDeviceXCFramework.sh -p iOS, iOS Simulator
XCFramework for BLEDevice will get generated in build directory.
Repeat the above steps whenever we make changes in the BLEDevice source code.
Repeat BLEDevice to XCFramework conversion for each device.
Conclusion
Share the BaseBLE XCFramework and its dependency XCFrameworks generated above and required BLEDevices’ XCFrameworks with corresponding client.