一.建立SDK打包项目
##1.创建SMSDK文件夹
##2.打开xcode创建xcworkspace放入到SMSDK文件夹
##3.新建framework归于SMSDK.xcworkspace
##3.SMSDK的pod init pod install(因为up主的要打SDK包的项目pod集成了第三方库)
这里就随便举几个例子
##4.配置SDK
1.设置SDK最低支持版本和对应支持设备
2.设置SDK为静态库
3.linker flags添加-objc
4.prelink为yes
5.新建对外暴露的文件,类,方法,属性等等
import UIKit
open class LDIMSDK: NSObject {
public static func getBundleImage(target: UIViewController) {
let img = UIImage.bundledImage(named: "icon-20")
let img1 = UIImage.bundledImage(named: "icon-20.png")
let img2 = UIImage.bundledImage(named: "icon-30")
let img3 = UIImage.bundledImage(named: "icon-30.png")
let img4 = UIImage.bundledImage(named: "icon-40")
let img5 = UIImage.bundledImage(named: "icon-40.png")
print(img)
print(img1)
print(img2)
print(img3)
print(img4)
print(img5)
if let image = UIImage.bundledImage(fileName: "icon-40.png"){
print("✅ \(image) 加载成功: \(image.size)")
} else {
print("❌ 加载失败")
}
if let image = UIImage.bundledImage(fileName: "icon-20.png"){
print("✅ \(image) 加载成功: \(image.size)")
} else {
print("❌ 加载失败")
}
let imageview1 = UIImageView(image: img)
imageview1.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
let imageview2 = UIImageView(image: img2)
imageview2.frame = CGRect(x: 100, y: 200, width: 100, height: 100)
let imageview3 = UIImageView(image: img4)
imageview1.frame = CGRect(x: 100, y: 300, width: 100, height: 100)
target.view.addSubview(imageview1)
target.view.addSubview(imageview2)
target.view.addSubview(imageview3)
}
}
import UIKit
extension UIImage {
/// 从 SDK Bundle 中加载图片
/// - Parameter imageName: 图片名称(不含扩展名)
/// - Returns: UIImage 对象,如果找不到则返回空图片
static func bundledImage(named imageName: String) -> UIImage? {
#if DEBUG
print("🔍 LDIMSDK: 尝试加载图片 '\(imageName)'")
#endif
// 获取 SDK 的 Bundle
guard let bundle = Bundle.sdkBundle else {
print("⚠️ LDIMSDK: 无法找到 SDK Bundle")
return nil
}
#if DEBUG
print(" SDK Bundle 路径: \(bundle.bundlePath)")
#endif
// 方式1: 从 Assets.xcassets (编译后的 Assets.car) 中加载
// 这是最优先的方式,因为 XCFramework 中的图片通常会被编译到 Assets.car
if let image = UIImage(named: imageName, in: bundle, compatibleWith: nil) {
#if DEBUG
print(" ✅ 从 Assets.car 加载成功: \(image.size)")
#endif
return image
}
// 方式2: 从嵌套的 .bundle 中加载
if let resourceBundle = Bundle.resourceBundle {
#if DEBUG
print(" 尝试从 Resource Bundle: \(resourceBundle.bundlePath)")
#endif
if let image = UIImage(named: imageName, in: resourceBundle, compatibleWith: nil) {
#if DEBUG
print(" ✅ 从 Resource Bundle 加载成功: \(image.size)")
#endif
return image
}
}
// 方式3: 尝试直接从文件路径加载 (从 SDK Bundle)
let imageExtensions = ["png", "jpg", "jpeg", "pdf"]
for ext in imageExtensions {
if let path = bundle.path(forResource: imageName, ofType: ext) {
#if DEBUG
print(" 找到文件路径: \(path)")
#endif
if let image = UIImage(contentsOfFile: path) {
#if DEBUG
print(" ✅ 从文件路径加载成功: \(image.size)")
#endif
return image
}
}
// 同时尝试从资源 bundle 中加载
if let resourceBundle = Bundle.resourceBundle,
let path = resourceBundle.path(forResource: imageName, ofType: ext) {
#if DEBUG
print(" 找到 Resource Bundle 文件路径: \(path)")
#endif
if let image = UIImage(contentsOfFile: path) {
#if DEBUG
print(" ✅ 从 Resource Bundle 文件路径加载成功: \(image.size)")
#endif
return image
}
}
}
print("❌ LDIMSDK: 无法找到图片 '\(imageName)'")
#if DEBUG
// 列出 Bundle 中的所有文件以便调试
if let contents = try? FileManager.default.contentsOfDirectory(atPath: bundle.bundlePath) {
print(" Bundle 中的文件:")
contents.prefix(10).forEach { print(" - \($0)") }
}
#endif
return nil
}
/// 从 SDK Bundle 中加载图片(使用完整文件名)
/// - Parameter fileName: 图片文件名(含扩展名)
/// - Returns: UIImage 对象
static func bundledImage(fileName: String) -> UIImage? {
guard let bundle = Bundle.sdkBundle else {
print("⚠️ LDIMSDK: 无法找到 SDK Bundle")
return nil
}
let components = fileName.components(separatedBy: ".")
let name = components.first ?? fileName
let ext = components.count > 1 ? components.last : nil
if let path = bundle.path(forResource: name, ofType: ext) {
return UIImage(contentsOfFile: path)
}
if let resourceBundle = Bundle.resourceBundle,
let path = resourceBundle.path(forResource: name, ofType: ext) {
return UIImage(contentsOfFile: path)
}
return nil
}
}
// MARK: - Bundle Extension
extension Bundle {
/// SDK 的主 Bundle
static var sdkBundle: Bundle? {
// 方式1: 通过类获取 Bundle
let bundle = Bundle(for: LDIMSDKBundleMarker.self)
// 方式2: 如果是 XCFramework,可能需要这样获取
if bundle.bundleURL.pathExtension == "framework" {
print("方式2: 如果是 XCFramework,可能需要这样获取\(bundle)")
return bundle
}
// 方式3: 尝试获取 LDIMSDK.framework
if let frameworkBundle = Bundle(identifier: "LDIMSDK") {
print("// 方式3: 尝试获取 LDIMSDK.framework\(frameworkBundle)")
return frameworkBundle
}
return bundle
}
/// SDK 资源 Bundle (如果使用了单独的资源 bundle)
static var resourceBundle: Bundle? {
guard let sdkBundle = sdkBundle else { return nil }
// 查找 LDIMSDK.bundle
if let bundlePath = sdkBundle.path(forResource: "LDIMSDK", ofType: "bundle"),
let resourceBundle = Bundle(path: bundlePath) {
return resourceBundle
}
// 查找其他可能的资源 bundle 名称
let bundleNames = ["LDIMSDKResources", "Resources", "mm"]
for bundleName in bundleNames {
if let bundlePath = sdkBundle.path(forResource: bundleName, ofType: "bundle"),
let resourceBundle = Bundle(path: bundlePath) {
return resourceBundle
}
}
return nil
}
/// 从 SDK 所在 Bundle 加载图片,便于以 Framework 形式集成时正确找到资源;找不到时回退到 main Bundle
static func sdk(named name: String) -> UIImage? {
UIImage(named: name, in: LDIM.sdkBundle, compatibleWith: nil)
?? UIImage(named: name)
}
}
// MARK: - Bundle Marker Class
/// 用于标识 SDK Bundle 的辅助类
private class LDIMSDKBundleMarker {
// 这个类只用于获取 Bundle,不需要任何实现
}
public class LDIM {
/// SDK 内部使用的 Bundle,不对外暴露
static var sdkBundle: Bundle {
Bundle(for: LDIM.self)
}
}
6.建立Bundle管理资源文件
7.添加对外暴露文件
8.以下是脚本文件(真机和模拟器一起生成SDK)
#!/bin/bash
# =====================================================
# SMSDK XCFramework 构建脚本
# 用于生成同时支持模拟器和真机的通用 Framework
# =====================================================
set -e
# ==================== 配置参数 ====================
SCHEME_NAME="SMSDK"
PROJECT_NAME="SMSDK"
FRAMEWORK_NAME="SMSDK"
OUTPUT_DIR="./Build"
FRAMEWORK_PATH="${OUTPUT_DIR}/XCFramework"
# 工作区路径
WORKSPACE_PATH="./SMSDK/${PROJECT_NAME}.xcworkspace"
# 如果没有 workspace,使用 project
if [ ! -d "$WORKSPACE_PATH" ]; then
PROJECT_PATH="./SMSDK/${PROJECT_NAME}.xcodeproj"
BUILD_OPTION="-project ${PROJECT_PATH}"
else
BUILD_OPTION="-workspace ${WORKSPACE_PATH}"
fi
# ==================== 清理旧文件 ====================
echo "🧹 清理旧的构建文件..."
rm -rf "${OUTPUT_DIR}"
mkdir -p "${OUTPUT_DIR}"
# ==================== 构建真机 Framework ====================
echo "📱 开始构建真机 (iOS) Framework..."
xcodebuild archive \
${BUILD_OPTION} \
-scheme "${SCHEME_NAME}" \
-configuration Release \
-destination "generic/platform=iOS" \
-archivePath "${OUTPUT_DIR}/iOS.xcarchive" \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
| xcpretty || true
# ==================== 构建模拟器 Framework ====================
echo "🖥️ 开始构建模拟器 (iOS Simulator) Framework..."
xcodebuild archive \
${BUILD_OPTION} \
-scheme "${SCHEME_NAME}" \
-configuration Release \
-destination "generic/platform=iOS Simulator" \
-archivePath "${OUTPUT_DIR}/iOSSimulator.xcarchive" \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
| xcpretty || true
# ==================== 创建 XCFramework ====================
echo "📦 创建 XCFramework..."
xcodebuild -create-xcframework \
-framework "${OUTPUT_DIR}/iOS.xcarchive/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework" \
-framework "${OUTPUT_DIR}/iOSSimulator.xcarchive/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework" \
-output "${FRAMEWORK_PATH}/${FRAMEWORK_NAME}.xcframework"
# ==================== 清理中间文件 ====================
echo "🧹 清理中间文件..."
rm -rf "${OUTPUT_DIR}/iOS.xcarchive"
rm -rf "${OUTPUT_DIR}/iOSSimulator.xcarchive"
# ==================== 完成 ====================
echo "✅ 构建完成!"
echo "📦 XCFramework 位置: ${FRAMEWORK_PATH}/${FRAMEWORK_NAME}.xcframework"
echo ""
echo "使用方法:"
echo "1. 拖拽 ${FRAMEWORK_NAME}.xcframework 到你的项目中"
echo "2. 在 General -> Frameworks, Libraries, and Embedded Content 中设置为 'Embed & Sign'"
然后终端跑
cd 目标文件夹
# 1. 赋予脚本执行权限
chmod +x build_xcframework.sh
# 2. 执行构建
./build_xcframework.sh
至此SDK生成成功
#二.配置spec 1.配置podspec文件 1).新建文件夹SMUP 2).终端创建podspec文件
pod spec create SMSDK
Pod::Spec.new do |spec|
spec.name = "SMSDK"
spec.version = "0.0.1"
spec.summary = "简短的SDK描述"
spec.description = <<-DESC
这是对SDK的一段描述
DESC
spec.homepage = "https://github.com/saman008/SMSDK"
spec.license = "MIT (example)"
spec.license = { :type => "MIT", :file => "FILE_LICENSE" }
spec.author = { "saman008" => "1975566197@qq.com" }
spec.platform = :ios, "15.0"
spec.source = { :git => "https://github.com/saman008/SMSDK.git", :tag => "#{spec.version}" }
spec.vendored_frameworks = "SMSDK.xcframework"
spec.source_files = 'SMSDK/*.{swift,h,m}'
spec.resource_bundles = {
'SMSDK' => ['SMSDK.xcframework/ios-arm64/SMSDK.framework/SMSDK.bundle/**/*.{storyboard,xib,xcassets,json,imageset,png,strings,mp3}'],
'SMSDK' => ['SMSDK.xcframework/ios-arm64_x86_64-simulator/SMSDK.framework/SMSDK.bundle/**/*.{storyboard,xib,xcassets,json,imageset,png,strings,mp3}'],
}
# 必须的框架
spec.frameworks = 'UIKit', 'Foundation'
# 静态框架
spec.static_framework = true
spec.ios.deployment_target = '15.0'
spec.swift_version = ['5.0']
#以下是你引用的第三方库 最好把三方库对应的版本加上
spec.dependency "RxCocoa", "~> 6.6.0"
spec.dependency "SnapKit"
spec.dependency "RxSwift"
spec.dependency "RxDataSources"
spec.dependency "RxGesture"
spec.dependency "Differentiator"
spec.dependency "RxRelay"
end
2.远程仓库
3.clone项目到SMUP文件夹中
在pod里面添加这些
pod 'SMSDK', :path => 'Pods/SMUP'
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = "YES"#yes一定要加
end
end
end
然后pod install
最后展示结果(bundle里面的图片加载成功,调用对应类方法成功)
SDK本地拖入成功
#三.建立分发出去的git的SDK包
下面介绍git远程依赖
注意打tag 要和spec上的保持一致 我只里都是0.0.1
spec.version = "0.0.1"
pod上添加对应远程路径
pod 'SMSDK', :git => 'https://github.com/saman008/SMSDK.git', :tag => '0.0.1'
然后pod install一下
最后项目跑一下 也是成功
完美 撒花🎉 觉得有用的大佬给个点赞和打赏,只对我最好的支持
最后有些同学想要pod xxx这样的引用第三方库 如下 仓库推送到CocoaPods上
pod trunk register 邮箱 ‘用户名’
注意:邮箱为github上的登录邮箱、用户名为github上的用户名 注册后,你填写的邮箱会收到一份确认邮件,点击里面的链接,注册成功。
pod trunk me
推送到CocoaPods 把你的.podspec文件推送到CocoaPods上
pod trunk push SMIMDEMOSDK.podspec --allow-warnings
成功了
查询对应的第三方库
pod search SMIMDEMOSDK
我这里刚注册成功 估计没那么快显示,而且本来应该SMSDK的名字的但是被别人占了,就改了