问题描述
在SwiftUI中引入requestReview,以便更好的让应用得到推广,但是在Xcode调试的过程中,存在因CPU过高导致卡死的情况。
因此,通过复现代码排查具体的问题原因以及寻找合适的解决方案。
import SwiftUI
import StoreKit
struct Home: View {
@Environment(\.requestReview) var requestReview
// 请求评分
@AppStorage("RatingClicks") var RatingClicks: Int = 0
var body: some View {
NavigationStack {
VStack {
Button("打开设置") {
isDisplaySettings.toggle()
}
}
.sheet(isPresented: $isDisplaySettings) {
// 显示设置视图
Settings()
}
}
.onTapGesture {
// 新增点击次数统计,用于激活评分
print("点击了动画,当前评分点击次数为:\(RatingClicks)")
RatingClicks += 1
// 当评分点击次数为 100 次时,激活评分。
if RatingClicks == 100 {
requestReview()
}
}
}
}
在主视图中打开设置视图。
import SwiftUI
struct Settings: View {
var body: some View {
NavigationStack {
// 背景、动画和图标
Group {
VStack(spacing: 0) {
NavigationLink(destination: MainInterfaceBackgroundView()){
Text("背景")
}
NavigationLink(destination:MainInterfaceAnimationView()){
Text("动画")
}
NavigationLink(destination: AppIconView()){
Text("图标")
}
}
}
}
}
}
点击按钮后,显示设置视图,在设置视图中点击NavigationLinks时,应用就会卡死。

如果隐藏requestReview,就不会出现卡死的情况。
// @Environment(\.requestReview) var requestReview
为了进一步测试,requestReview和NavigationLinks的冲突,我新增了一个简单的NavigationLink。
NavigationLink(destination: Text("You navigated to the new view")) {
Text("Go to Next View")
.font(.title)
.foregroundColor(.blue)
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(10)
}

在Xcode调试中发现,当点击“Go to Next View”按钮时,仍然会存在CPU过高,应用卡死的情况。

因此,可以确认@Environment(\.requestReview) 和 NavigationLink 之间确实存在冲突。
通过网络查询,发现在《request App Review causing strange crash》讨论文章中,有人遇到类似的问题。

但遗憾的是,文中并没有解决方案,有一个人提到NavigationPath 来替代 NavigationLink。

尝试方案
因为requestReview与NavigationLink存在冲突,因此可以考虑NavigationPath 来替代 NavigationLink。
因此,我尝试将requestReview放在一个独立的视图中,来解决这一问题。
首先,创建一个请求评分的视图,在视图显示时,触发请求评论。
import SwiftUI
import StoreKit
struct RequestReviewView: View {
@Environment(\.requestReview) var requestReview
var body: some View {
// 触发请求评论
Text("") // 不显示内容,只用于触发requestReview
.onAppear {
requestReview() // 触发请求评论
}
}
}
在应用的主视图中,添加关于这个视图的判定:
struct Home: View {
// 请求评分
@AppStorage("RatingClicks") var RatingClicks: Int = 0
var body: some View {
NavigationStack {
// 当评分点击次数为 100 次时,激活评分。
if RatingClicks == 100 {
RequestReviewView()
.onAppear{
RatingClicks += 1
}
}
}
.onTapGesture {
// 新增点击次数统计,用于激活评分
guard RatingClicks > 100 else {
RatingClicks += 1
return
}
}
}
}
当点击主视图100次时,会显示评分视图,在评分视图中触发请求评论,当评分视图显示后,通过RatingClicks+1隐藏评分视图。

但是,尽管将requestReview放到一个独立的视图,但是仍然会存在点击NavigationLink卡死的情况。
因此,这个问题存在两个可能的原因:
1、视图生命周期问题:@Environment(\.requestReview) 的使用可能与 SwiftUI 中的视图更新机制产生了冲突,尤其是当视图栈发生变化时(比如使用 NavigationLink 导航到另一个视图)。
2、请求评论机制不稳定:requestReview() 的触发机制可能在某些条件下与系统的资源管理或视图堆栈管理不兼容,导致卡死。
解决方案
解决方案一
使用条件性的外部逻辑触发 requestReview 而不是直接依赖 @Environment(\.requestReview)。
通过 StoreKit 的其他 API 或自定义机制来代替 @Environment(\.requestReview),从而避免环境变量引起的问题。可以通过 SKStoreReviewController 手动触发请求评论。
import SwiftUI
import StoreKit
struct ContentView: View {
@State private var triggerReview = false
var body: some View {
VStack {
Button("Request Review") {
// 触发请求评论
triggerReview = true
}
if triggerReview {
SKStoreReviewController.requestReview()
// 触发后可以跳转或做其他操作
}
}
}
}
经过实测,SKStoreReviewController.requestReview()可以在系统允许的情况下,弹出评分视图。

同时,点击NavigationLink不会因CPU过高导致卡死。

但是在使用SKStoreReviewController.requestReview()时,Xcode会提示:
'requestReview()' was deprecated in iOS 14.0
这标识requestReview在iOS 14.0中已被弃用requestReview(),但目前仍然是@Environment(\.requestReview)的替代方案。
解决方案二
使用 NavigationPath 替代 NavigationLink,因为博主没有学过NavigationPath,这里不过多赘述,有兴趣的可以了解一下。
总结
以上就是本文的全部内容,这个问题应该是SwiftUI的BUG,目前来看使用SKStoreReviewController.requestReview()的方案更优,因为不需要修改NavigationLink。
参考文章
1、request App Review causing strange crash:https://www.hackingwithswift.com/forums/swiftui/request-app-review-causing-strange-crash/24633