之前有介紹過如何建立一個 CocoaPods,而這篇文章則是紀錄一下這次直接在 Xcode 上建立 Framework 會遇上的問題。
建立 Framework 專案
首先我們先打開 Xcode,並選擇 iOS 類別底下的 Framework & Library 裡頭的 Framework
並依照所需要的欄位填寫相關資訊,這邊目前都和直接建立一個專案一致流程。
而如果你有打算在裡頭寫測試的話,就將 Include Unit Tests ☑️
Framework 開發
使用 Xcode 建立起來和 CocoaPods 創建,在開發上並沒有什麼差異;所以就依照著相對應的存取限制來設計你的 framework 即可。
這篇的重點會落在一些參數設定。
Mach-O Type 決定方式
在這個 framework 的 target 之中,我們可以從 Build Settings 的 Mach-O Type 來決定它是一個怎樣的 framework
而要如何取捨這個 framework 的輸出型態,可以依據一些使用方式來判斷
Static Library
- 有使用到其他 static library
- 使用 Singleton
Dynamic Library
- 有包含其他檔案,如圖片、JSON 之類的
輸出成 .framework
輸出成 .framework 的方式很簡單,你只要 build 這個專案就會產生出相對應的 .framework 檔案;但重點會是,若你今天使用模擬器 build 的話,它會建立一個 Debug-iphonesimulator
的資料夾,裡頭包含只支援模擬器的 .framework,而使用 Generic iOS Device
的話,就會產生出 Debug-iphoneos
的資料夾,裡頭的 .framework 也只會支援實體裝置。
這邊可以透過 lipo 的指令將兩者融合成一個同時支援模擬器又支援實體裝置的 .framework,或者可以建立一個新的 target 做這件事👌
建一個 Aggregate 來跑 script
首先在 TARGETS 那邊點擊 + 來建立一個 Aggregate
並在它的 Build Phases 欄位中加入一個 Run script
#!/bin/sh
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
# Step 2. Copy the framework structure (from iphoneos build) to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"
# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory
SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
fi
# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
# Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"
# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"
這樣在 build 這個 Aggregate target 時,就會產生出一個 universal 的 .framework 在專案裡頭了!