背景
當前pod 開發(fā)需要手動維護framework工程的文件的引用。
1. 在主工程使用development pod開發(fā)時,非常容易遺漏個別添加新增文件或文件改名的引用,維護成本高。
2. 多人并行開發(fā)時,引用的維護還可能產(chǎn)生沖突,進一步增加維護成本。
3. 部分文件,不通過直接調(diào)用,例如runtime的Swizzling method,如果遺漏可能導(dǎo)致功能錯誤。
4. 實際pod工程中,會產(chǎn)生重復(fù)文件,沒有被引用的文件,成了垃圾文件,混在其中,可能出現(xiàn)改了沒效果的疑難問題。
方案
使用pod命令,創(chuàng)建標準化的pod工程,代替Xcode創(chuàng)建的framework工程。
pod lib create MyPod

pod lib create MyPod

pod目錄
Pod標準工程無法build sdk,使用腳本改造。(以下以LMGeofence為例)
open_sdk.sh
#!/bin/bash
set -e
#SDK_TYPE宏,實現(xiàn)在編譯時,不同SDK使用不同的邏輯
SDK_TYPE=${1:-0}
GITIGNORE_FILE=".gitignore"
# 如果 .gitignore 不存在,則創(chuàng)建
if [ ! -f "$GITIGNORE_FILE" ]; then
echo "?? .gitignore not found, creating..."
touch "$GITIGNORE_FILE"
fi
# Example/build和Example/Pods兩個文件夾不必管理(兩個目錄都體積都比較大)
IGNORE_ENTRIES=("Example/build" "Example/Pods")
# 循環(huán)檢查并添加
for ENTRY in "${IGNORE_ENTRIES[@]}"; do
if ! grep -qxF "$ENTRY" "$GITIGNORE_FILE"; then
echo "$ENTRY" >> "$GITIGNORE_FILE"
echo "? Added: $ENTRY"
else
echo "?? Already exists: $ENTRY"
fi
done
echo "?? .gitignore updated successfully."
if ! command -v pod &>/dev/null; then
echo "? 未檢測到 CocoaPods,請先執(zhí)行:sudo gem install cocoapods"
exit 1
fi
# pod file 需要把所有pod庫設(shè)置為static,不使用static的話,如果被依賴的庫中有static的話,會無法運行
# 如果項目中有一個庫必須是靜態(tài)庫時, 那么其整個依賴鏈路上的所有庫都必須以靜態(tài)庫被引入
PLUGIN="cocoapods-pod-linkage"
if pod plugins | grep -q "$PLUGIN"; then
# echo "? 已安裝 $PLUGIN,檢查是否有更新..."
echo "? 已安裝 $PLUGIN"
# gem update "$PLUGIN"
else
echo "?? 未安裝 $PLUGIN,正在安裝..."
gem install "$PLUGIN"
fi
echo "? $PLUGIN 已準備就緒"
cd Example
insert_or_update_constant() {
local FILE="$1"
local VAR_NAME="$2"
local NEW_VALUE="$3"
local SECOND_LINE="$4"
if [ ! -f "$FILE" ]; then
echo "? File not found: $FILE"
return 1
fi
echo "?? Editing file: $FILE"
# 第一行變量存在則更新
if grep -q "^${VAR_NAME}" "$FILE"; then
sed -i '' "s|^${VAR_NAME} *=.*|${VAR_NAME} = ${NEW_VALUE}|" "$FILE"
echo "?? Updated ${VAR_NAME} = ${NEW_VALUE}"
else
# 插入到文件頂部
sed -i '' "1s|^|${VAR_NAME} = ${NEW_VALUE}\n|" "$FILE"
echo "? Inserted ${VAR_NAME} = ${NEW_VALUE} at top"
fi
# 第二行插入邏輯(可選)
if [ -n "$SECOND_LINE" ] && ! grep -q "^${SECOND_LINE}$" "$FILE"; then
sed -i '' "2i\\
${SECOND_LINE}
" "$FILE"
echo "? Inserted second line: ${SECOND_LINE}"
fi
}
# Pod file插入sdk_type變量及注釋
insert_or_update_constant "Podfile" "sdk_type" "${SDK_TYPE}" "#sdk_type參數(shù), Aqara SDK: 0, 第三方SDK: 1, 其他: 待定"
cd ..
PODSPEC_FILE=$(ls *.podspec 2>/dev/null | head -n 1)
if [ -z "$PODSPEC_FILE" ]; then
echo "? No .podspec file found in current directory."
exit 1
fi
echo "? Found podspec: $PODSPEC_FILE"
# podspec插入sdk_type變量及注釋
insert_or_update_constant "$PODSPEC_FILE" "sdk_type" "${SDK_TYPE}" "#sdk_type參數(shù), Aqara SDK: 0, 第三方SDK: 1, 其他: 待定"
cd Example
# 1. 源碼安裝 Pods
all_source=1 pod install
cd ..
# 選擇對應(yīng)的scheme
PROJECT_PATH="_Pods.xcodeproj"
USER_NAME=$(id -un)
SCHEME_DIR="${PROJECT_PATH}/xcuserdata/${USER_NAME}.xcuserdatad/xcschemes"
SCHEME_NAME="LMGeofence"
SCHEME_FILE="${SCHEME_DIR}/${SCHEME_NAME}.xcscheme"
USER_NAME=$(id -un)
PLIST_PATH="${PROJECT_PATH}/xcuserdata/${USER_NAME}.xcuserdatad/xcschemes/xcschememanagement.plist"
# 確保文件存在
if [ ! -f "$PLIST_PATH" ]; then
echo "?? xcschememanagement.plist not found, creating..."
mkdir -p "$(dirname "$PLIST_PATH")"
cat > "$PLIST_PATH" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict/>
</dict>
</plist>
EOF
fi
# 確保該 scheme 被設(shè)置為 isShown = true(不使用共享)
/usr/libexec/PlistBuddy -c "Set :SchemeUserState:${SCHEME_NAME}.xcscheme:isShown true" "$PLIST_PATH" 2>/dev/null \
|| /usr/libexec/PlistBuddy -c "Add :SchemeUserState:${SCHEME_NAME}.xcscheme:isShown bool true" "$PLIST_PATH"
echo "? 已勾選用戶私有 scheme: ${SCHEME_NAME}"
#修改scheme的配置為Release
echo "?? 修改 scheme 配置為 Release..."
echo "?? Scheme file path: $SCHEME_FILE"
open _Pods.xcodeproj
# 用 xmlstarlet(如果可用)優(yōu)雅修改,否則用 sed 簡單替換
if command -v xmlstarlet >/dev/null 2>&1; then
echo "?? xmlstarlet"
xmlstarlet ed -L \
-u '//LaunchAction/@buildConfiguration' -v 'Release' \
"$SCHEME_FILE"
else
# fallback:用 sed 直接替換 Debug → Release
echo "?? perl"
perl -i -pe '
BEGIN{$/="\n";}
if(/<LaunchAction/ .. />/){ s/buildConfiguration = "[^"]*"/buildConfiguration = "Release"/; }
' "$SCHEME_FILE"
fi
echo "? 已將 ${SCHEME_NAME}.xcscheme 的 Build Configuration 改為 Release"
sdk_type = 0
#sdk_type參數(shù), Aqara SDK: 0, 第三方SDK: 1, 其他: 待定
use_modular_headers!
use_frameworks!:linkage => :static #設(shè)置為靜態(tài)framework
source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/volcengine/volcengine-specs.git'
platform :ios, '13.0'
target 'LMGeofence_Example' do
#此處也可以根據(jù)情況設(shè)置Path,使用本地庫
pod 'LMBaseEncryption',:git =>'https://git.aqara.com/aqara-app/sdk/ios/lmbaseencryption.git',:tag=>'1.0.0'
pod 'RealReachability', :git => 'http://git.aqara.com/aqara-app/sdk/ios/Charts.git', :branch => 'RealReachability'
#pod 'LMFramework',:git =>'http://git.aqara.com/aqara-app/sdk/ios/lmframework.git',:tag=>'5.2.1'
pod 'LMFramework', :path=>'../../LMFramework'
pod 'CocoaLumberjack'
pod 'YYModel'
#pod 'LMCommonUI',:git =>'http://git.aqara.com/aqara-app/sdk/commonui/commonui_ios.git',:branch=>'dev_automation'
pod 'LMCommonUI', :path=>'../../LMCommonUI'
pod 'Masonry'
pod 'JZLocationConverter', :git => 'https://github.com/MrLittleWhite/JZLocationConverter.git'
pod 'LMModulesCenter',:git => 'https://git.aqara.com/aqara-app/sdk/ios/lmmodulescenter.git',:tag => '1.0.1'
#pod 'LMSwiftCore', :git => 'https://git.aqara.com/aqara-app/sdk/ios/lmswiftcore.git',:tag => '1.0.8'
pod 'LMSwiftCore', :path=>'../../lmswiftcore'
#pod 'LMCommonUISwift',:git => 'https://git.aqara.com/aqara-app/sdk/commonui/commonuiswift/lmcommonuiswift.git',:tag => '1.0.9'
pod 'LMCommonUISwift', :path=>'../../lmcommonuiswift'
pod 'LMGeofence', :path => '../'
target 'LMGeofence_Tests' do
inherit! :search_paths
end
end
post_install do |installer|
target_name = "LMGeofence"
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
#設(shè)置所有target的最低配置,否則編譯可能報不一致錯誤
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
end
end
#添加輸出sdk包腳本到build phrase中
installer.pods_project.targets.each do |target|
if target.name == target_name
# 修改Build Libraries for Distribution的Release下為YES
target.build_configurations.each do |config|
next unless config.name == 'Release'
config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
end
# 先清掉舊的同名腳本,避免重復(fù)
target.shell_script_build_phases
.select { |phase| phase.name == "#{target_name} SDK Build Script" }
.each { |phase| target.build_phases.delete(phase) }
# 添加新的 Run Script Phase
phase = target.new_shell_script_build_phase("#{target_name} SDK Build Script")
phase.shell_script = <<-SCRIPT
#******Script begin*******
# Type a script or drag a script file from your workspace to insert its path.
TARGET_NAME='LMGeofence'
SDK_DIR=$(dirname "$(dirname "$SRCROOT")")
if [ "${ACTION}" = "build" ]
then
INSTALL_DIR=${SRCROOT}/Products/${TARGET_NAME}.framework
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${TARGET_NAME}.framework
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework
# 如果真機包或模擬包不存在,則退出合并
if [ ! -d "${DEVICE_DIR}" ] || [ ! -d "${SIMULATOR_DIR}" ]
then
FMK_NAME=${TARGET_NAME}
rm -rf ${SDK_DIR}/SourceSDK
mkdir -p ${SDK_DIR}/SourceSDK
INSTALL_DIR1=${SDK_DIR}/SourceSDK/${FMK_NAME}.framework
DEVICE_DIR1=${BUILD_DIR}/Release-iphoneos/${FMK_NAME}/${FMK_NAME}.framework
echo ${INSTALL_DIR1}
echo ${DEVICE_DIR1}
cp -R "${DEVICE_DIR1}/" "${INSTALL_DIR1}/"
exit 0
fi
# 如果合并包已經(jīng)存在,則替換
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
# 使用lipo命令將其合并成一個通用framework
# 最后將生成的通用framework放置在工程根目錄下新建的Products目錄下
lipo -create "${DEVICE_DIR}/${TARGET_NAME}" "${SIMULATOR_DIR}/${TARGET_NAME}" -output "${INSTALL_DIR}/${TARGET_NAME}"
#合并完成后打開目錄
open "${SRCROOT}/Products"
fi
FMK_NAME=${TARGET_NAME}
rm -rf ${SDK_DIR}/SourceSDK
mkdir -p ${SDK_DIR}/SourceSDK
INSTALL_DIR=${SDK_DIR}/SourceSDK/${FMK_NAME}.framework
DEVICE_DIR=${BUILD_DIR}/Release-iphoneos/${FMK_NAME}/${FMK_NAME}.framework
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
#******Script end*******
SCRIPT
end
end
end

改造后工程
若習(xí)慣使用命令行,可直接調(diào)用open_sdk.sh,打開SDK工程,點擊運行,即可輸出SDK包。
也可直接雙擊OpenSDK程序,可自動打開終端執(zhí)行命令。若期間工程有編譯問題,修復(fù)后可直接在該終端點擊上顯示上次執(zhí)行的命令,再執(zhí)行即可。
源碼和SDK切換優(yōu)化
podspec
if ENV['all_source'] || ENV['geofence_source']
s.source_files = 'LMGeofence/**/*.{c,cpp,h,m,swift}'
else
s.vendored_frameworks = "SourceSDK/*.framework"
end
宿主工程Podfile
if ENV['geofence_source']
pod 'LMGeofence', :path => '../LMGeofence'
elsif
pod 'LMGeofence',:git => 'https://git.aqara.com/aqara-app/sdk/ios/lmgeofence.git',:commit => '6c6a695537c788e73cf6672de42bce27965fbeb6'
end
單獨源碼調(diào)試LMGeofence的pod源碼,命令如下:
local=1 chart_source=1 pod install --verbose