Kotlin Multiplatform iOS端实现App Store应用版本更新检测的技术咨询
Kotlin Multiplatform iOS端实现App Store应用版本更新检测的技术咨询
嘿,看你已经把Android端的应用更新检测顺顺当当搞定了,现在卡在iOS端获取App Store最新版本号这一步是吧?我帮你把这部分的坑填上,顺便把版本比较的小雷区也给你修正下——直接用字符串比版本号可是会出问题的哦!
核心问题拆解与修复
你的现有代码已经拿到了当前应用的版本号,现在缺的就是异步获取App Store最新版本号的实现,以及靠谱的版本号比较逻辑。先给你把这两部分补全,再整合到你的代码里。
完整实现代码(推荐用Ktor统一跨平台网络请求)
既然是Kotlin Multiplatform项目,用Ktor客户端做网络请求会更贴合跨平台的统一技术栈,避免原生API的回调嵌套。下面是完整的AppUpdateManager实现:
import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.request.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.Serializable import Foundation // 定义iTunes Lookup API的JSON解析模型 @Serializable private data class ItunesLookupResponse( val resultCount: Int, val results: List<AppStoreAppInfo> ) @Serializable private data class AppStoreAppInfo( val version: String ) actual class AppUpdateManager { // 初始化Ktor客户端(建议全局单例,这里简化写在类内) private val ktorClient = HttpClient { install(ContentNegotiation) { json() } } actual suspend fun checkForUpdate(): UpdateStatus { // 获取当前应用版本号,失败直接返回错误状态 val currentVersion = NSBundle.mainBundle.infoDictionary?.get("CFBundleShortVersionString") as? String ?: return UpdateStatus.Error("无法获取当前应用版本号") return try { val latestVersion = fetchLatestAppStoreVersion() ?: return UpdateStatus.Error("无法获取App Store最新版本号") // 用靠谱的版本号比较逻辑判断是否有更新 if (isLatestVersionNewer(latestVersion, currentVersion)) { UpdateStatus.UpdateAvailable } else { UpdateStatus.NoUpdate } } catch (e: Exception) { UpdateStatus.Error(e.message ?: "版本检测失败:未知错误") } } // 异步获取App Store最新版本号 private suspend fun fetchLatestAppStoreVersion(): String? { // 自动获取当前应用的BundleID,避免硬编码出错 val bundleId = NSBundle.mainBundle.bundleIdentifier ?: return null return try { val response = ktorClient.get("https://itunes.apple.com/lookup?bundleId=$bundleId") .body<ItunesLookupResponse>() // 确保API返回有效数据 if (response.resultCount > 0) { response.results.first().version } else { null } } catch (e: Exception) { e.printStackTrace() null } } // 修复版本号比较逻辑:字符串直接比较会出现"1.10" < "1.2"的错误 private fun isLatestVersionNewer(latestVersion: String, currentVersion: String): Boolean { val latestParts = latestVersion.split(".").mapNotNull { it.toIntOrNull() } val currentParts = currentVersion.split(".").mapNotNull { it.toIntOrNull() } val maxLength = maxOf(latestParts.size, currentParts.size) for (i in 0 until maxLength) { val latestNum = latestParts.getOrNull(i) ?: 0 val currentNum = currentParts.getOrNull(i) ?: 0 if (latestNum > currentNum) return true if (latestNum < currentNum) return false } // 版本号完全一致 return false } }
备选方案:用iOS原生NSURLSession实现
如果你更倾向于用iOS原生API做网络请求,可以用suspendCancellableCoroutine把回调式的NSURLSession包装成协程,替换上面的fetchLatestAppStoreVersion函数即可:
private suspend fun fetchLatestAppStoreVersion(): String? { val bundleId = NSBundle.mainBundle.bundleIdentifier ?: return null val requestUrl = NSURL.URLWithString("https://itunes.apple.com/lookup?bundleId=$bundleId") ?: return null return suspendCancellableCoroutine { cont -> val task = NSURLSession.sharedSession.dataTaskWithURL(requestUrl) { data, _, error -> if (error != null) { cont.resumeWith(Result.success(null)) return@dataTaskWithURL } guard let validData = data else { cont.resumeWith(Result.success(null)) return@dataTaskWithURL } // 原生解析JSON数据 do { val json = try NSJSONSerialization.JSONObjectWithData(validData, 0) as? NSDictionary val resultCount = json?.objectForKey("resultCount") as? Int ?: 0 if (resultCount > 0) { val results = json?.objectForKey("results") as? NSArray val firstResult = results?.objectAtIndex(0) as? NSDictionary val latestVersion = firstResult?.objectForKey("version") as? String cont.resumeWith(Result.success(latestVersion)) } else { cont.resumeWith(Result.success(null)) } } catch (e: Exception) { cont.resumeWith(Result.success(null)) } } task.resume() // 协程取消时同步取消网络请求 cont.invokeOnCancellation { task.cancel() } } }
关键注意事项
- BundleID自动获取:代码里用
NSBundle.mainBundle.bundleIdentifier自动获取当前应用的BundleID,避免硬编码写错导致API返回空数据 - 版本号比较逻辑:绝对不能直接用字符串比较!比如字符串层面"1.10"会被认为比"1.2"小,必须拆分版本号为数字数组后逐位比较
- 错误处理全覆盖:网络请求失败、API返回无效数据、版本号解析失败的情况都要捕获,返回对应的错误状态
内容来源于stack exchange
相关产品推荐
相关产品推荐

