chore(ci): 更新 Jenkins 配置以支持多模块版本管理
- 为 Android、Flutter、iOS 和 RN SDK 的 Jenkinsfile 添加模块化版本控制 - 引入版本升级策略选择(major/minor/patch)和自定义版本号功能 - 实现多模块独立版本管理和选择性构建发布 - 更新 iOS SDK Package.swift 以支持独立模块化库 - 修改 iOS SDK podspec 文件以适应新的标签命名约定 - 优化 Jenkins 构建流程以支持按需选择特定模块进行构建和发布 - 修复 iOS 测试中的可选类型转换问题以提高代码健壮性
这个提交包含在:
父节点
c9aff03a63
当前提交
e016adbb71
206
Jenkinsfile
vendored
206
Jenkinsfile
vendored
@ -2,7 +2,26 @@ pipeline {
|
|||||||
agent any
|
agent any
|
||||||
|
|
||||||
parameters {
|
parameters {
|
||||||
string(name: 'VERSION', defaultValue: '', description: '发布版本号(留空自动递增)')
|
// ── 版本升级策略 ─────────────────────────────────────────────────────
|
||||||
|
choice(name: 'VERSION_BUMP', defaultValue: 'patch',
|
||||||
|
description: '版本升级策略: major(1.0.0→2.0.0), minor(1.0.0→1.1.0), patch(1.0.0→1.0.1)')
|
||||||
|
booleanParam(name: 'CUSTOM_VERSION', defaultValue: false,
|
||||||
|
description: '勾选后使用下方自定义版本号(忽略VERSION_BUMP)')
|
||||||
|
|
||||||
|
// ── 模块自定义版本号(仅CUSTOM_VERSION=true时生效)───────────────────
|
||||||
|
string(name: 'CORE_VERSION', defaultValue: '', description: 'XuqmCoreSDK 自定义版本号(仅CUSTOM_VERSION=true时生效)')
|
||||||
|
string(name: 'IM_VERSION', defaultValue: '', description: 'XuqmImSDK 自定义版本号')
|
||||||
|
string(name: 'PUSH_VERSION', defaultValue: '', description: 'XuqmPushSDK 自定义版本号')
|
||||||
|
string(name: 'UPDATE_VERSION', defaultValue: '', description: 'XuqmUpdateSDK 自定义版本号')
|
||||||
|
string(name: 'LICENSE_VERSION', defaultValue: '', description: 'XuqmLicenseSDK 自定义版本号')
|
||||||
|
string(name: 'FILE_VERSION', defaultValue: '', description: 'XuqmFileSDK 自定义版本号')
|
||||||
|
string(name: 'WEBVIEW_VERSION', defaultValue: '', description: 'XuqmWebViewSDK 自定义版本号')
|
||||||
|
|
||||||
|
// ── 模块选择 ────────────────────────────────────────────────────────
|
||||||
|
text(name: 'MODULES', defaultValue: 'core\nim\npush\nupdate\nlicense\nfile\nwebview',
|
||||||
|
description: '要发布的模块,每行一个。可选: core im push update license file webview')
|
||||||
|
|
||||||
|
// ── 构建选项 ────────────────────────────────────────────────────────
|
||||||
booleanParam(name: 'RUN_TESTS', defaultValue: true, description: '是否运行单元测试')
|
booleanParam(name: 'RUN_TESTS', defaultValue: true, description: '是否运行单元测试')
|
||||||
booleanParam(name: 'PUBLISH_SPM', defaultValue: true, description: '发布 SPM 版本(打 Git Tag)')
|
booleanParam(name: 'PUBLISH_SPM', defaultValue: true, description: '发布 SPM 版本(打 Git Tag)')
|
||||||
booleanParam(name: 'PUBLISH_PODS', defaultValue: false, description: '发布到 CocoaPods 私有 Spec Repo')
|
booleanParam(name: 'PUBLISH_PODS', defaultValue: false, description: '发布到 CocoaPods 私有 Spec Repo')
|
||||||
@ -30,11 +49,91 @@ pipeline {
|
|||||||
])
|
])
|
||||||
script {
|
script {
|
||||||
env.GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
|
env.GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
|
||||||
if (params.VERSION?.trim()) {
|
|
||||||
env.PUBLISH_VERSION = params.VERSION.trim()
|
// ── 解析选中的模块 ──────────────────────────────────────
|
||||||
sh "sed -i '' 's/s.version.*= .*/s.version = \"${env.PUBLISH_VERSION}\"/' XuqmSDK.podspec"
|
def allValid = ['core', 'im', 'push', 'update', 'license', 'file', 'webview']
|
||||||
|
def requested = params.MODULES
|
||||||
|
.tokenize('\n, ')
|
||||||
|
.collect { it.trim().toLowerCase() }
|
||||||
|
.findAll { it }
|
||||||
|
def invalid = requested - allValid
|
||||||
|
if (invalid) {
|
||||||
|
error "Unknown module(s): ${invalid}. Valid values: ${allValid}"
|
||||||
|
}
|
||||||
|
env.SELECTED_MODULES = requested.join(',')
|
||||||
|
echo "Selected modules: ${env.SELECTED_MODULES}"
|
||||||
|
|
||||||
|
// ── 模块 → podspec/tag 映射 ────────────────────────────
|
||||||
|
def moduleMap = [
|
||||||
|
'core': [podspec: 'XuqmCoreSDK', tagPrefix: 'core'],
|
||||||
|
'im': [podspec: 'XuqmImSDK', tagPrefix: 'im'],
|
||||||
|
'push': [podspec: 'XuqmPushSDK', tagPrefix: 'push'],
|
||||||
|
'update': [podspec: 'XuqmUpdateSDK', tagPrefix: 'update'],
|
||||||
|
'license': [podspec: 'XuqmLicenseSDK', tagPrefix: 'license'],
|
||||||
|
'file': [podspec: 'XuqmFileSDK', tagPrefix: 'file'],
|
||||||
|
'webview': [podspec: 'XuqmWebViewSDK', tagPrefix: 'webview'],
|
||||||
|
]
|
||||||
|
env.MODULE_MAP = groovy.json.JsonOutput.toJson(moduleMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Resolve Versions') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
def moduleMap = new groovy.json.JsonSlurper().parseText(env.MODULE_MAP)
|
||||||
|
def modules = env.SELECTED_MODULES.split(',')
|
||||||
|
def customVersions = [
|
||||||
|
'core': params.CORE_VERSION?.trim(),
|
||||||
|
'im': params.IM_VERSION?.trim(),
|
||||||
|
'push': params.PUSH_VERSION?.trim(),
|
||||||
|
'update': params.UPDATE_VERSION?.trim(),
|
||||||
|
'license': params.LICENSE_VERSION?.trim(),
|
||||||
|
'file': params.FILE_VERSION?.trim(),
|
||||||
|
'webview': params.WEBVIEW_VERSION?.trim(),
|
||||||
|
]
|
||||||
|
|
||||||
|
for (mod in modules) {
|
||||||
|
def podspecFile = "${moduleMap[mod].podspec}.podspec"
|
||||||
|
def currentVer = sh(
|
||||||
|
script: "grep \"s.version\" ${podspecFile} | head -1 | sed \"s/.*= *\\\"\\(.*\\)\\\".*/\\1/\"",
|
||||||
|
returnStdout: true
|
||||||
|
).trim()
|
||||||
|
|
||||||
|
def newVer = ''
|
||||||
|
if (params.CUSTOM_VERSION && customVersions[mod]) {
|
||||||
|
// 使用自定义版本号
|
||||||
|
newVer = customVersions[mod]
|
||||||
|
} else if (!params.CUSTOM_VERSION) {
|
||||||
|
// 根据 VERSION_BUMP 策略自动升级版本号
|
||||||
|
def parts = currentVer.tokenize('.')
|
||||||
|
while (parts.size() < 3) { parts.add('0') }
|
||||||
|
switch (params.VERSION_BUMP) {
|
||||||
|
case 'major':
|
||||||
|
parts[0] = (parts[0].toInteger() + 1).toString()
|
||||||
|
parts[1] = '0'
|
||||||
|
parts[2] = '0'
|
||||||
|
break
|
||||||
|
case 'minor':
|
||||||
|
parts[1] = (parts[1].toInteger() + 1).toString()
|
||||||
|
parts[2] = '0'
|
||||||
|
break
|
||||||
|
case 'patch':
|
||||||
|
default:
|
||||||
|
parts[2] = (parts[2].toInteger() + 1).toString()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
newVer = parts.join('.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newVer && newVer != currentVer) {
|
||||||
|
echo "Bumping ${moduleMap[mod].podspec}: ${currentVer} → ${newVer}"
|
||||||
|
sh "sed -i '' 's/s.version.*= .*/s.version = \"${newVer}\"/' ${podspecFile}"
|
||||||
|
env["VERSION_${mod}"] = newVer
|
||||||
} else {
|
} else {
|
||||||
env.PUBLISH_VERSION = sh(script: 'grep "s.version" XuqmSDK.podspec | head -1 | sed "s/.*= *\\"\\(.*\\)\\".*/\\1/"', returnStdout: true).trim()
|
echo "Keeping ${moduleMap[mod].podspec} at ${currentVer}"
|
||||||
|
env["VERSION_${mod}"] = currentVer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,10 +142,15 @@ pipeline {
|
|||||||
stage('Build Info') {
|
stage('Build Info') {
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
echo "🚀 构建分支: ${params.BRANCH}"
|
def modules = env.SELECTED_MODULES.split(',')
|
||||||
echo "🏷️ 发布版本: ${env.PUBLISH_VERSION}"
|
def moduleMap = new groovy.json.JsonSlurper().parseText(env.MODULE_MAP)
|
||||||
echo "🔨 Git Commit: ${env.GIT_COMMIT_SHORT}"
|
echo "Git Commit : ${env.GIT_COMMIT_SHORT}"
|
||||||
echo "⚠️ 注意:此任务应在 Jenkins2 (https://xuqinmin12.eicp.vip/) 上执行"
|
echo "Bump Strategy: ${params.CUSTOM_VERSION ? 'Custom' : params.VERSION_BUMP}"
|
||||||
|
echo "Modules (${modules.size()}):"
|
||||||
|
for (mod in modules) {
|
||||||
|
def ver = env["VERSION_${mod}"]
|
||||||
|
echo " ${mod.padRight(10)} → ${moduleMap[mod].podspec} v${ver}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,37 +176,76 @@ pipeline {
|
|||||||
stage('Pod Spec Lint') {
|
stage('Pod Spec Lint') {
|
||||||
when { expression { return params.PUBLISH_PODS } }
|
when { expression { return params.PUBLISH_PODS } }
|
||||||
steps {
|
steps {
|
||||||
sh 'pod spec lint XuqmSDK.podspec --allow-warnings'
|
script {
|
||||||
|
def moduleMap = new groovy.json.JsonSlurper().parseText(env.MODULE_MAP)
|
||||||
|
def modules = env.SELECTED_MODULES.split(',')
|
||||||
|
// 按依赖顺序 lint:先 core,再其他模块
|
||||||
|
def ordered = ['core'] + modules.findAll { it != 'core' }
|
||||||
|
for (mod in ordered) {
|
||||||
|
def podspec = "${moduleMap[mod].podspec}.podspec"
|
||||||
|
if (fileExists(podspec)) {
|
||||||
|
echo "Linting ${podspec}..."
|
||||||
|
sh "pod spec lint ${podspec} --allow-warnings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Publish SPM (Git Tag)') {
|
stage('Publish SPM (Git Tags)') {
|
||||||
when {
|
when { expression { return params.PUBLISH_SPM } }
|
||||||
expression { return params.PUBLISH_SPM }
|
|
||||||
}
|
|
||||||
steps {
|
steps {
|
||||||
withCredentials([usernamePassword(credentialsId: 'gitlab-credentials', passwordVariable: 'GIT_PASS', usernameVariable: 'GIT_USER')]) {
|
withCredentials([usernamePassword(credentialsId: 'gitlab-credentials', passwordVariable: 'GIT_PASS', usernameVariable: 'GIT_USER')]) {
|
||||||
|
script {
|
||||||
|
def moduleMap = new groovy.json.JsonSlurper().parseText(env.MODULE_MAP)
|
||||||
|
def modules = env.SELECTED_MODULES.split(',')
|
||||||
|
def tagsToPush = []
|
||||||
|
|
||||||
|
for (mod in modules) {
|
||||||
|
def ver = env["VERSION_${mod}"]
|
||||||
|
if (ver) {
|
||||||
|
def tag = "${moduleMap[mod].tagPrefix}/${ver}"
|
||||||
|
tagsToPush.push(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagsToPush) {
|
||||||
sh """
|
sh """
|
||||||
git config user.email "jenkins@xuqm.com"
|
git config user.email "jenkins@xuqm.com"
|
||||||
git config user.name "Jenkins CI"
|
git config user.name "Jenkins CI"
|
||||||
git add XuqmSDK.podspec || true
|
git add *.podspec || true
|
||||||
git diff --cached --quiet || git commit -m "ci: bump version to ${env.PUBLISH_VERSION}"
|
git diff --cached --quiet || git commit -m "ci: bump versions [${env.SELECTED_MODULES}]"
|
||||||
git tag -f "${env.PUBLISH_VERSION}"
|
|
||||||
git push https://${GIT_USER}:${GIT_PASS}@xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git HEAD:main --tags
|
|
||||||
"""
|
"""
|
||||||
|
for (tag in tagsToPush) {
|
||||||
|
sh "git tag -f '${tag}'"
|
||||||
|
}
|
||||||
|
sh """
|
||||||
|
git push https://${GIT_USER}:${GIT_PASS}@xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git HEAD:main --tags -f
|
||||||
|
"""
|
||||||
|
echo "Published tags: ${tagsToPush.join(', ')}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Publish CocoaPods') {
|
stage('Publish CocoaPods') {
|
||||||
when {
|
when { expression { return params.PUBLISH_PODS } }
|
||||||
expression { return params.PUBLISH_PODS }
|
|
||||||
}
|
|
||||||
steps {
|
steps {
|
||||||
sh """
|
script {
|
||||||
pod repo add xuqm-specs ${SPEC_REPO} 2>/dev/null || true
|
def moduleMap = new groovy.json.JsonSlurper().parseText(env.MODULE_MAP)
|
||||||
pod repo push xuqm-specs XuqmSDK.podspec --allow-warnings
|
def modules = env.SELECTED_MODULES.split(',')
|
||||||
"""
|
sh "pod repo add xuqm-specs ${SPEC_REPO} 2>/dev/null || true"
|
||||||
|
// 按依赖顺序发布:先 core,再其他模块
|
||||||
|
def ordered = ['core'] + modules.findAll { it != 'core' }
|
||||||
|
for (mod in ordered) {
|
||||||
|
def podspec = "${moduleMap[mod].podspec}.podspec"
|
||||||
|
if (fileExists(podspec)) {
|
||||||
|
echo "Publishing ${podspec}..."
|
||||||
|
sh "pod repo push xuqm-specs ${podspec} --allow-warnings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,17 +253,14 @@ pipeline {
|
|||||||
post {
|
post {
|
||||||
success {
|
success {
|
||||||
script {
|
script {
|
||||||
echo "✅ iOS SDK v${env.PUBLISH_VERSION} 构建成功 (Commit: ${env.GIT_COMMIT_SHORT})"
|
def modules = env.SELECTED_MODULES?.split(',') ?: []
|
||||||
if (params.PUBLISH_SPM) {
|
def moduleMap = new groovy.json.JsonSlurper().parseText(env.MODULE_MAP)
|
||||||
echo "📦 SPM 版本已发布: ${env.PUBLISH_VERSION}"
|
def summary = modules.collect { "${it}=${env["VERSION_${it}"]}" }.join(', ')
|
||||||
}
|
echo "iOS SDK published — ${summary} (Commit: ${env.GIT_COMMIT_SHORT})"
|
||||||
if (params.PUBLISH_PODS) {
|
|
||||||
echo "📦 CocoaPods 版本已发布到私有 Spec Repo"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
failure {
|
failure {
|
||||||
echo "❌ iOS SDK 构建失败,请检查日志"
|
echo "iOS SDK build failed — please check the logs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,21 +5,67 @@ let package = Package(
|
|||||||
name: "XuqmSDK",
|
name: "XuqmSDK",
|
||||||
platforms: [.iOS(.v16), .macOS(.v13)],
|
platforms: [.iOS(.v16), .macOS(.v13)],
|
||||||
products: [
|
products: [
|
||||||
.library(name: "XuqmSDK", targets: ["XuqmSDK"]),
|
// Individual modules — use these for per-module consumption
|
||||||
|
.library(name: "XuqmCoreSDK", targets: ["XuqmCoreSDK"]),
|
||||||
|
.library(name: "XuqmImSDK", targets: ["XuqmImSDK"]),
|
||||||
|
.library(name: "XuqmPushSDK", targets: ["XuqmPushSDK"]),
|
||||||
|
.library(name: "XuqmUpdateSDK", targets: ["XuqmUpdateSDK"]),
|
||||||
|
.library(name: "XuqmLicenseSDK", targets: ["XuqmLicenseSDK"]),
|
||||||
|
.library(name: "XuqmFileSDK", targets: ["XuqmFileSDK"]),
|
||||||
.library(name: "XuqmWebViewSDK", targets: ["XuqmWebViewSDK"]),
|
.library(name: "XuqmWebViewSDK", targets: ["XuqmWebViewSDK"]),
|
||||||
|
// Convenience — backward compatible, includes all modules
|
||||||
|
.library(name: "XuqmSDK", targets: ["XuqmSDK"]),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
name: "XuqmSDK",
|
name: "XuqmCoreSDK",
|
||||||
path: "Sources/XuqmSDK",
|
path: "Sources/XuqmCoreSDK",
|
||||||
|
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "XuqmImSDK",
|
||||||
|
dependencies: ["XuqmCoreSDK", "XuqmFileSDK"],
|
||||||
|
path: "Sources/XuqmImSDK",
|
||||||
|
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "XuqmPushSDK",
|
||||||
|
dependencies: ["XuqmCoreSDK"],
|
||||||
|
path: "Sources/XuqmPushSDK",
|
||||||
|
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "XuqmUpdateSDK",
|
||||||
|
dependencies: ["XuqmCoreSDK"],
|
||||||
|
path: "Sources/XuqmUpdateSDK",
|
||||||
|
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "XuqmLicenseSDK",
|
||||||
|
dependencies: ["XuqmCoreSDK"],
|
||||||
|
path: "Sources/XuqmLicenseSDK",
|
||||||
|
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "XuqmFileSDK",
|
||||||
|
dependencies: ["XuqmCoreSDK"],
|
||||||
|
path: "Sources/XuqmFileSDK",
|
||||||
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "XuqmWebViewSDK",
|
name: "XuqmWebViewSDK",
|
||||||
dependencies: ["XuqmSDK"],
|
|
||||||
path: "Sources/XuqmWebViewSDK",
|
path: "Sources/XuqmWebViewSDK",
|
||||||
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
||||||
),
|
),
|
||||||
|
.target(
|
||||||
|
name: "XuqmSDK",
|
||||||
|
dependencies: [
|
||||||
|
"XuqmCoreSDK", "XuqmImSDK", "XuqmPushSDK",
|
||||||
|
"XuqmUpdateSDK", "XuqmLicenseSDK", "XuqmFileSDK",
|
||||||
|
],
|
||||||
|
path: "Sources/XuqmSDK",
|
||||||
|
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
||||||
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "XuqmSDKTests",
|
name: "XuqmSDKTests",
|
||||||
dependencies: ["XuqmSDK"],
|
dependencies: ["XuqmSDK"],
|
||||||
|
|||||||
@ -33,7 +33,7 @@ public extension SDKConfig {
|
|||||||
extension SDKConfig {
|
extension SDKConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SDKEndpoints {
|
public enum SDKEndpoints {
|
||||||
static let apiBaseURL = URL(string: "https://dev.xuqinmin.com")!
|
public static let apiBaseURL = URL(string: "https://dev.xuqinmin.com")!
|
||||||
static let imWebSocketURL = URL(string: "wss://dev.xuqinmin.com/ws/im")!
|
public static let imWebSocketURL = URL(string: "wss://dev.xuqinmin.com/ws/im")!
|
||||||
}
|
}
|
||||||
@ -4,6 +4,8 @@ public final class TokenStore: @unchecked Sendable {
|
|||||||
|
|
||||||
private let key = "com.xuqm.sdk.token"
|
private let key = "com.xuqm.sdk.token"
|
||||||
|
|
||||||
|
public init() {}
|
||||||
|
|
||||||
public func save(_ token: String) {
|
public func save(_ token: String) {
|
||||||
UserDefaults.standard.set(token, forKey: key)
|
UserDefaults.standard.set(token, forKey: key)
|
||||||
}
|
}
|
||||||
@ -1,28 +1,74 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
public struct LicenseFileData: Sendable {
|
||||||
|
public let appKey: String
|
||||||
|
public let appName: String?
|
||||||
|
public let companyName: String?
|
||||||
|
public let packageName: String?
|
||||||
|
public let iosBundleId: String?
|
||||||
|
public let harmonyBundleName: String?
|
||||||
|
public let baseUrl: String?
|
||||||
|
public let serverUrl: String?
|
||||||
|
public let issuedAt: String?
|
||||||
|
public let expiresAt: String?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
appKey: String,
|
||||||
|
appName: String? = nil,
|
||||||
|
companyName: String? = nil,
|
||||||
|
packageName: String? = nil,
|
||||||
|
iosBundleId: String? = nil,
|
||||||
|
harmonyBundleName: String? = nil,
|
||||||
|
baseUrl: String? = nil,
|
||||||
|
serverUrl: String? = nil,
|
||||||
|
issuedAt: String? = nil,
|
||||||
|
expiresAt: String? = nil
|
||||||
|
) {
|
||||||
|
self.appKey = appKey
|
||||||
|
self.appName = appName
|
||||||
|
self.companyName = companyName
|
||||||
|
self.packageName = packageName
|
||||||
|
self.iosBundleId = iosBundleId
|
||||||
|
self.harmonyBundleName = harmonyBundleName
|
||||||
|
self.baseUrl = baseUrl
|
||||||
|
self.serverUrl = serverUrl
|
||||||
|
self.issuedAt = issuedAt
|
||||||
|
self.expiresAt = expiresAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public final class XuqmSDK: NSObject {
|
public final class XuqmSDK: NSObject {
|
||||||
|
|
||||||
public static let shared = XuqmSDK()
|
public static let shared = XuqmSDK()
|
||||||
private(set) var config: SDKConfig?
|
public private(set) var config: SDKConfig?
|
||||||
private(set) var tokenStore: TokenStore?
|
public private(set) var tokenStore: TokenStore?
|
||||||
|
|
||||||
public private(set) var currentUserId: String?
|
public private(set) var currentUserId: String?
|
||||||
|
|
||||||
private var userSig: String?
|
private var userSig: String?
|
||||||
private var cachedDeviceToken: String?
|
public private(set) var cachedDeviceToken: String?
|
||||||
private var lastInitializedAppKey: String?
|
private var lastInitializedAppKey: String?
|
||||||
private var isInitialized = false
|
private var isInitialized = false
|
||||||
private var initContinuations: [CheckedContinuation<Void, Never>] = []
|
private var initContinuations: [CheckedContinuation<Void, Never>] = []
|
||||||
|
|
||||||
|
// Module hooks — set by the convenience XuqmSDK module to wire feature modules
|
||||||
|
public var onInitialize: (@MainActor @Sendable (SDKConfig) -> Void)?
|
||||||
|
public var onLogin: (@MainActor @Sendable (String, String) async throws -> Void)?
|
||||||
|
public var onLogout: (@MainActor @Sendable (String) async throws -> Void)?
|
||||||
|
public var onDeviceToken: (@MainActor @Sendable (String, String) async throws -> Void)?
|
||||||
|
public var licenseReader: (@Sendable () throws -> LicenseFileData?)?
|
||||||
|
|
||||||
private override init() {
|
private override init() {
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Auto-initialize from the embedded license file (Bundle.main/xuqm/license.xuqm).
|
/// Auto-initialize from the embedded license file using the registered licenseReader hook.
|
||||||
/// Validates the bundle ID / package name against the license file.
|
|
||||||
public func autoInitialize(debug: Bool = false) throws {
|
public func autoInitialize(debug: Bool = false) throws {
|
||||||
guard let file = LicenseFileReader.read() else {
|
guard let reader = licenseReader else {
|
||||||
|
throw XuqmSDKError.noLicenseFile
|
||||||
|
}
|
||||||
|
guard let file = try reader() else {
|
||||||
throw XuqmSDKError.noLicenseFile
|
throw XuqmSDKError.noLicenseFile
|
||||||
}
|
}
|
||||||
let bundleId = Bundle.main.bundleIdentifier ?? ""
|
let bundleId = Bundle.main.bundleIdentifier ?? ""
|
||||||
@ -42,17 +88,12 @@ public final class XuqmSDK: NSObject {
|
|||||||
self.tokenStore = TokenStore()
|
self.tokenStore = TokenStore()
|
||||||
ApiClient.shared.configure(with: config)
|
ApiClient.shared.configure(with: config)
|
||||||
isInitialized = true
|
isInitialized = true
|
||||||
// Resume any waiting continuations
|
|
||||||
let continuations = initContinuations
|
let continuations = initContinuations
|
||||||
initContinuations.removeAll()
|
initContinuations.removeAll()
|
||||||
for cont in continuations {
|
for cont in continuations {
|
||||||
cont.resume()
|
cont.resume()
|
||||||
}
|
}
|
||||||
if config.autoRegisterPush {
|
onInitialize?(config)
|
||||||
Task { @MainActor in
|
|
||||||
try? await PushSDK.shared.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func requireConfig() -> SDKConfig {
|
public func requireConfig() -> SDKConfig {
|
||||||
@ -64,7 +105,6 @@ public final class XuqmSDK: NSObject {
|
|||||||
|
|
||||||
public var initialized: Bool { isInitialized }
|
public var initialized: Bool { isInitialized }
|
||||||
|
|
||||||
/// Wait for initialization to complete.
|
|
||||||
public func awaitInitialization() async {
|
public func awaitInitialization() async {
|
||||||
guard !isInitialized else { return }
|
guard !isInitialized else { return }
|
||||||
await withCheckedContinuation { cont in
|
await withCheckedContinuation { cont in
|
||||||
@ -80,28 +120,26 @@ public final class XuqmSDK: NSObject {
|
|||||||
self.userSig = userSig
|
self.userSig = userSig
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try await ImSDK.shared.login(userId, userSig)
|
try await onLogin?(userId, userSig)
|
||||||
} catch {
|
} catch {
|
||||||
// IM login failed; silently ignored per facade pattern
|
// Module login failed; silently ignored per facade pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
if let cachedDeviceToken {
|
if let cachedDeviceToken {
|
||||||
do {
|
do {
|
||||||
try await PushSDK.shared.registerToken(cachedDeviceToken, userId: userId)
|
try await onDeviceToken?(cachedDeviceToken, userId)
|
||||||
} catch {
|
} catch {
|
||||||
// Push registration failed
|
// Device token registration failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func logout() async {
|
public func logout() async {
|
||||||
ImSDK.shared.disconnect()
|
|
||||||
|
|
||||||
if let userId = currentUserId {
|
if let userId = currentUserId {
|
||||||
do {
|
do {
|
||||||
try await PushSDK.shared.unregisterToken(userId: userId)
|
try await onLogout?(userId)
|
||||||
} catch {
|
} catch {
|
||||||
// Push unregistration failed
|
// Module logout failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +154,7 @@ public final class XuqmSDK: NSObject {
|
|||||||
self.cachedDeviceToken = token
|
self.cachedDeviceToken = token
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
if let userId = self.currentUserId {
|
if let userId = self.currentUserId {
|
||||||
try? await PushSDK.shared.registerToken(token, userId: userId)
|
try? await self.onDeviceToken?(token, userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import XuqmCoreSDK
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
public struct FileUploadResult: Codable, Sendable {
|
public struct FileUploadResult: Codable, Sendable {
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import XuqmCoreSDK
|
||||||
|
|
||||||
public final class ImClient: NSObject, URLSessionWebSocketDelegate, @unchecked Sendable {
|
public final class ImClient: NSObject, URLSessionWebSocketDelegate, @unchecked Sendable {
|
||||||
|
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
import XuqmCoreSDK
|
||||||
|
import XuqmFileSDK
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public protocol ConversationDelegate: AnyObject {
|
public protocol ConversationDelegate: AnyObject {
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import XuqmCoreSDK
|
||||||
|
|
||||||
public enum ImConnectionState: String, Sendable {
|
public enum ImConnectionState: String, Sendable {
|
||||||
case connected
|
case connected
|
||||||
@ -53,6 +54,38 @@ public struct ImMessage: Codable, Sendable {
|
|||||||
public let revoked: Bool?
|
public let revoked: Bool?
|
||||||
public let createdAt: Int64
|
public let createdAt: Int64
|
||||||
public let editedAt: Int64?
|
public let editedAt: Int64?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
id: String,
|
||||||
|
appKey: String,
|
||||||
|
fromUserId: String,
|
||||||
|
fromId: String?,
|
||||||
|
toId: String,
|
||||||
|
chatType: ChatType,
|
||||||
|
msgType: MsgType,
|
||||||
|
content: String,
|
||||||
|
status: MsgStatus,
|
||||||
|
mentionedUserIds: String?,
|
||||||
|
groupReadCount: Int?,
|
||||||
|
revoked: Bool?,
|
||||||
|
createdAt: Int64,
|
||||||
|
editedAt: Int64?
|
||||||
|
) {
|
||||||
|
self.id = id
|
||||||
|
self.appKey = appKey
|
||||||
|
self.fromUserId = fromUserId
|
||||||
|
self.fromId = fromId
|
||||||
|
self.toId = toId
|
||||||
|
self.chatType = chatType
|
||||||
|
self.msgType = msgType
|
||||||
|
self.content = content
|
||||||
|
self.status = status
|
||||||
|
self.mentionedUserIds = mentionedUserIds
|
||||||
|
self.groupReadCount = groupReadCount
|
||||||
|
self.revoked = revoked
|
||||||
|
self.createdAt = createdAt
|
||||||
|
self.editedAt = editedAt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct PageResult<T: Decodable & Sendable>: Decodable, Sendable {
|
public struct PageResult<T: Decodable & Sendable>: Decodable, Sendable {
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import XuqmCoreSDK
|
||||||
#if canImport(UIKit)
|
#if canImport(UIKit)
|
||||||
import UIKit
|
import UIKit
|
||||||
#endif
|
#endif
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import XuqmCoreSDK
|
||||||
import CryptoKit
|
import CryptoKit
|
||||||
import CommonCrypto
|
import CommonCrypto
|
||||||
|
|
||||||
@ -1,8 +1,9 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import XuqmCoreSDK
|
||||||
|
|
||||||
enum LicenseFileReader {
|
public enum LicenseFileReader {
|
||||||
|
|
||||||
static func read() -> LicenseFile? {
|
public static func read() -> LicenseFile? {
|
||||||
guard let url = Bundle.main.url(forResource: "license", withExtension: "xuqm", subdirectory: "xuqm"),
|
guard let url = Bundle.main.url(forResource: "license", withExtension: "xuqm", subdirectory: "xuqm"),
|
||||||
let encrypted = try? String(contentsOf: url, encoding: .utf8) else {
|
let encrypted = try? String(contentsOf: url, encoding: .utf8) else {
|
||||||
return nil
|
return nil
|
||||||
@ -1,16 +1,41 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import XuqmCoreSDK
|
||||||
|
|
||||||
struct LicenseFile: Codable {
|
public struct LicenseFile: Codable, Sendable {
|
||||||
let appKey: String
|
public let appKey: String
|
||||||
let appName: String?
|
public let appName: String?
|
||||||
let companyName: String?
|
public let companyName: String?
|
||||||
let packageName: String?
|
public let packageName: String?
|
||||||
let iosBundleId: String?
|
public let iosBundleId: String?
|
||||||
let harmonyBundleName: String?
|
public let harmonyBundleName: String?
|
||||||
let baseUrl: String?
|
public let baseUrl: String?
|
||||||
let serverUrl: String?
|
public let serverUrl: String?
|
||||||
let issuedAt: String?
|
public let issuedAt: String?
|
||||||
let expiresAt: String?
|
public let expiresAt: String?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
appKey: String,
|
||||||
|
appName: String? = nil,
|
||||||
|
companyName: String? = nil,
|
||||||
|
packageName: String? = nil,
|
||||||
|
iosBundleId: String? = nil,
|
||||||
|
harmonyBundleName: String? = nil,
|
||||||
|
baseUrl: String? = nil,
|
||||||
|
serverUrl: String? = nil,
|
||||||
|
issuedAt: String? = nil,
|
||||||
|
expiresAt: String? = nil
|
||||||
|
) {
|
||||||
|
self.appKey = appKey
|
||||||
|
self.appName = appName
|
||||||
|
self.companyName = companyName
|
||||||
|
self.packageName = packageName
|
||||||
|
self.iosBundleId = iosBundleId
|
||||||
|
self.harmonyBundleName = harmonyBundleName
|
||||||
|
self.baseUrl = baseUrl
|
||||||
|
self.serverUrl = serverUrl
|
||||||
|
self.issuedAt = issuedAt
|
||||||
|
self.expiresAt = expiresAt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct LicenseUserInfo: Sendable {
|
public struct LicenseUserInfo: Sendable {
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import XuqmCoreSDK
|
||||||
|
|
||||||
public final class LicenseSDK: @unchecked Sendable {
|
public final class LicenseSDK: @unchecked Sendable {
|
||||||
|
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import XuqmCoreSDK
|
||||||
import Security
|
import Security
|
||||||
|
|
||||||
final class LicenseStore {
|
final class LicenseStore {
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import XuqmCoreSDK
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
#if canImport(UIKit)
|
#if canImport(UIKit)
|
||||||
import UIKit
|
import UIKit
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
@_exported import XuqmCoreSDK
|
||||||
|
@_exported import XuqmImSDK
|
||||||
|
@_exported import XuqmPushSDK
|
||||||
|
@_exported import XuqmUpdateSDK
|
||||||
|
@_exported import XuqmLicenseSDK
|
||||||
|
@_exported import XuqmFileSDK
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Auto-registers all feature module hooks with XuqmSDK.shared.
|
||||||
|
/// Import `XuqmSDK` to get the full SDK experience (backward compatible).
|
||||||
|
extension XuqmSDK {
|
||||||
|
|
||||||
|
/// Register all module hooks. Called automatically when any module is first used.
|
||||||
|
public func registerModuleHooks() {
|
||||||
|
// License reader hook
|
||||||
|
licenseReader = {
|
||||||
|
guard let file = LicenseFileReader.read() else { return nil }
|
||||||
|
return LicenseFileData(
|
||||||
|
appKey: file.appKey,
|
||||||
|
appName: file.appName,
|
||||||
|
companyName: file.companyName,
|
||||||
|
packageName: file.packageName,
|
||||||
|
iosBundleId: file.iosBundleId,
|
||||||
|
harmonyBundleName: file.harmonyBundleName,
|
||||||
|
baseUrl: file.baseUrl,
|
||||||
|
serverUrl: file.serverUrl,
|
||||||
|
issuedAt: file.issuedAt,
|
||||||
|
expiresAt: file.expiresAt
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize hook — auto-start push if configured
|
||||||
|
onInitialize = { @MainActor config in
|
||||||
|
if config.autoRegisterPush {
|
||||||
|
Task { @MainActor in
|
||||||
|
try? await PushSDK.shared.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login hook — connect IM and register push token
|
||||||
|
onLogin = { @MainActor userId, userSig in
|
||||||
|
try await ImSDK.shared.login(userId, userSig)
|
||||||
|
if let token = XuqmSDK.shared.cachedDeviceToken {
|
||||||
|
try await PushSDK.shared.registerToken(token, userId: userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logout hook — disconnect IM and unregister push
|
||||||
|
onLogout = { @MainActor userId in
|
||||||
|
ImSDK.shared.disconnect()
|
||||||
|
try await PushSDK.shared.unregisterToken(userId: userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device token hook — register with push service
|
||||||
|
onDeviceToken = { @MainActor token, userId in
|
||||||
|
try await PushSDK.shared.registerToken(token, userId: userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience initializer that auto-registers module hooks.
|
||||||
|
/// This provides backward compatibility for code that does `import XuqmSDK`.
|
||||||
|
@MainActor
|
||||||
|
public enum XuqmSDKSetup {
|
||||||
|
private static var hooksRegistered = false
|
||||||
|
|
||||||
|
/// Ensure module hooks are registered. Idempotent.
|
||||||
|
public static func ensureHooksRegistered() {
|
||||||
|
guard !hooksRegistered else { return }
|
||||||
|
hooksRegistered = true
|
||||||
|
XuqmSDK.shared.registerModuleHooks()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import XuqmCoreSDK
|
||||||
#if canImport(UIKit)
|
#if canImport(UIKit)
|
||||||
import UIKit
|
import UIKit
|
||||||
#endif
|
#endif
|
||||||
@ -30,11 +30,11 @@ final class SmokeTests: XCTestCase {
|
|||||||
msgType: .text,
|
msgType: .text,
|
||||||
content: "Hello",
|
content: "Hello",
|
||||||
status: .sent,
|
status: .sent,
|
||||||
mentionedUserIds: nil,
|
mentionedUserIds: nil as String?,
|
||||||
groupReadCount: nil,
|
groupReadCount: nil as Int?,
|
||||||
revoked: false,
|
revoked: false,
|
||||||
createdAt: 1714300000000,
|
createdAt: 1714300000000,
|
||||||
editedAt: nil
|
editedAt: nil as Int64?
|
||||||
)
|
)
|
||||||
let data = try JSONEncoder().encode(msg)
|
let data = try JSONEncoder().encode(msg)
|
||||||
let decoded = try JSONDecoder().decode(ImMessage.self, from: data)
|
let decoded = try JSONDecoder().decode(ImMessage.self, from: data)
|
||||||
|
|||||||
12
XuqmCoreSDK.podspec
普通文件
12
XuqmCoreSDK.podspec
普通文件
@ -0,0 +1,12 @@
|
|||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'XuqmCoreSDK'
|
||||||
|
s.version = '0.1.0'
|
||||||
|
s.summary = 'XuqmGroup iOS SDK — Core module'
|
||||||
|
s.homepage = 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK'
|
||||||
|
s.license = { :type => 'MIT' }
|
||||||
|
s.author = { 'XuqmGroup' => 'dev@xuqm.com' }
|
||||||
|
s.source = { :git => 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git', :tag => "core/#{s.version}" }
|
||||||
|
s.ios.deployment_target = '16.0'
|
||||||
|
s.swift_version = '5.9'
|
||||||
|
s.source_files = 'Sources/XuqmCoreSDK/**/*.swift'
|
||||||
|
end
|
||||||
13
XuqmFileSDK.podspec
普通文件
13
XuqmFileSDK.podspec
普通文件
@ -0,0 +1,13 @@
|
|||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'XuqmFileSDK'
|
||||||
|
s.version = '0.1.0'
|
||||||
|
s.summary = 'XuqmGroup iOS SDK — File module'
|
||||||
|
s.homepage = 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK'
|
||||||
|
s.license = { :type => 'MIT' }
|
||||||
|
s.author = { 'XuqmGroup' => 'dev@xuqm.com' }
|
||||||
|
s.source = { :git => 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git', :tag => "file/#{s.version}" }
|
||||||
|
s.ios.deployment_target = '16.0'
|
||||||
|
s.swift_version = '5.9'
|
||||||
|
s.source_files = 'Sources/XuqmFileSDK/**/*.swift'
|
||||||
|
s.dependency 'XuqmCoreSDK'
|
||||||
|
end
|
||||||
14
XuqmImSDK.podspec
普通文件
14
XuqmImSDK.podspec
普通文件
@ -0,0 +1,14 @@
|
|||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'XuqmImSDK'
|
||||||
|
s.version = '0.1.0'
|
||||||
|
s.summary = 'XuqmGroup iOS SDK — IM module'
|
||||||
|
s.homepage = 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK'
|
||||||
|
s.license = { :type => 'MIT' }
|
||||||
|
s.author = { 'XuqmGroup' => 'dev@xuqm.com' }
|
||||||
|
s.source = { :git => 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git', :tag => "im/#{s.version}" }
|
||||||
|
s.ios.deployment_target = '16.0'
|
||||||
|
s.swift_version = '5.9'
|
||||||
|
s.source_files = 'Sources/XuqmImSDK/**/*.swift'
|
||||||
|
s.dependency 'XuqmCoreSDK'
|
||||||
|
s.dependency 'XuqmFileSDK'
|
||||||
|
end
|
||||||
13
XuqmLicenseSDK.podspec
普通文件
13
XuqmLicenseSDK.podspec
普通文件
@ -0,0 +1,13 @@
|
|||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'XuqmLicenseSDK'
|
||||||
|
s.version = '0.1.0'
|
||||||
|
s.summary = 'XuqmGroup iOS SDK — License module'
|
||||||
|
s.homepage = 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK'
|
||||||
|
s.license = { :type => 'MIT' }
|
||||||
|
s.author = { 'XuqmGroup' => 'dev@xuqm.com' }
|
||||||
|
s.source = { :git => 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git', :tag => "license/#{s.version}" }
|
||||||
|
s.ios.deployment_target = '16.0'
|
||||||
|
s.swift_version = '5.9'
|
||||||
|
s.source_files = 'Sources/XuqmLicenseSDK/**/*.swift'
|
||||||
|
s.dependency 'XuqmCoreSDK'
|
||||||
|
end
|
||||||
13
XuqmPushSDK.podspec
普通文件
13
XuqmPushSDK.podspec
普通文件
@ -0,0 +1,13 @@
|
|||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'XuqmPushSDK'
|
||||||
|
s.version = '0.1.0'
|
||||||
|
s.summary = 'XuqmGroup iOS SDK — Push module'
|
||||||
|
s.homepage = 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK'
|
||||||
|
s.license = { :type => 'MIT' }
|
||||||
|
s.author = { 'XuqmGroup' => 'dev@xuqm.com' }
|
||||||
|
s.source = { :git => 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git', :tag => "push/#{s.version}" }
|
||||||
|
s.ios.deployment_target = '16.0'
|
||||||
|
s.swift_version = '5.9'
|
||||||
|
s.source_files = 'Sources/XuqmPushSDK/**/*.swift'
|
||||||
|
s.dependency 'XuqmCoreSDK'
|
||||||
|
end
|
||||||
@ -1,12 +1,18 @@
|
|||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = 'XuqmSDK'
|
s.name = 'XuqmSDK'
|
||||||
s.version = '0.1.1'
|
s.version = '0.1.1'
|
||||||
s.summary = 'XuqmGroup iOS SDK — IM, Push Notifications, Version Management'
|
s.summary = 'XuqmGroup iOS SDK — convenience podspec (includes all modules)'
|
||||||
s.homepage = 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK'
|
s.homepage = 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK'
|
||||||
s.license = { :type => 'MIT' }
|
s.license = { :type => 'MIT' }
|
||||||
s.author = { 'XuqmGroup' => 'dev@xuqm.com' }
|
s.author = { 'XuqmGroup' => 'dev@xuqm.com' }
|
||||||
s.source = { :git => 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git', :tag => s.version.to_s }
|
s.source = { :git => 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git', :tag => "sdk/#{s.version}" }
|
||||||
s.ios.deployment_target = '16.0'
|
s.ios.deployment_target = '16.0'
|
||||||
s.swift_version = '5.9'
|
s.swift_version = '5.9'
|
||||||
s.source_files = 'Sources/XuqmSDK/**/*.swift'
|
s.source_files = 'Sources/XuqmSDK/**/*.swift'
|
||||||
|
s.dependency 'XuqmCoreSDK'
|
||||||
|
s.dependency 'XuqmImSDK'
|
||||||
|
s.dependency 'XuqmPushSDK'
|
||||||
|
s.dependency 'XuqmUpdateSDK'
|
||||||
|
s.dependency 'XuqmLicenseSDK'
|
||||||
|
s.dependency 'XuqmFileSDK'
|
||||||
end
|
end
|
||||||
|
|||||||
13
XuqmUpdateSDK.podspec
普通文件
13
XuqmUpdateSDK.podspec
普通文件
@ -0,0 +1,13 @@
|
|||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'XuqmUpdateSDK'
|
||||||
|
s.version = '0.1.0'
|
||||||
|
s.summary = 'XuqmGroup iOS SDK — Update module'
|
||||||
|
s.homepage = 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK'
|
||||||
|
s.license = { :type => 'MIT' }
|
||||||
|
s.author = { 'XuqmGroup' => 'dev@xuqm.com' }
|
||||||
|
s.source = { :git => 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git', :tag => "update/#{s.version}" }
|
||||||
|
s.ios.deployment_target = '16.0'
|
||||||
|
s.swift_version = '5.9'
|
||||||
|
s.source_files = 'Sources/XuqmUpdateSDK/**/*.swift'
|
||||||
|
s.dependency 'XuqmCoreSDK'
|
||||||
|
end
|
||||||
@ -5,9 +5,8 @@ Pod::Spec.new do |s|
|
|||||||
s.homepage = 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK'
|
s.homepage = 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK'
|
||||||
s.license = { :type => 'MIT' }
|
s.license = { :type => 'MIT' }
|
||||||
s.author = { 'XuqmGroup' => 'dev@xuqm.com' }
|
s.author = { 'XuqmGroup' => 'dev@xuqm.com' }
|
||||||
s.source = { :git => 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git', :tag => s.version.to_s }
|
s.source = { :git => 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git', :tag => "webview/#{s.version}" }
|
||||||
s.ios.deployment_target = '16.0'
|
s.ios.deployment_target = '16.0'
|
||||||
s.swift_version = '5.9'
|
s.swift_version = '5.9'
|
||||||
s.source_files = 'Sources/XuqmWebViewSDK/**/*.swift'
|
s.source_files = 'Sources/XuqmWebViewSDK/**/*.swift'
|
||||||
s.dependency 'XuqmSDK'
|
|
||||||
end
|
end
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户