You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()
        }
    }
}

关键注意事项

  1. BundleID自动获取:代码里用NSBundle.mainBundle.bundleIdentifier自动获取当前应用的BundleID,避免硬编码写错导致API返回空数据
  2. 版本号比较逻辑:绝对不能直接用字符串比较!比如字符串层面"1.10"会被认为比"1.2"小,必须拆分版本号为数字数组后逐位比较
  3. 错误处理全覆盖:网络请求失败、API返回无效数据、版本号解析失败的情况都要捕获,返回对应的错误状态

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 10:44:35