The StoreKit
updates introduced in iOS 14 provide a quick way for developers to recommend other apps using SKOverlay
or, in SwiftUI, the appStoreOverlay(isPresented:configuration:)
modifier. In SwiftUI, the implementation is particularly simple.
In a new Xcode SwiftUI project, the first thing to do in your ContentView
file is add the following import declaration: import StoreKit
. My preference is then to create an enumeration for app recommendations:
enum AppRecommendation {
case bbcEasyCook, bbcTopGear, bbcSport, none
var id: String {
switch self {
case .bbcEasyCook:
return "527622729"
case .bbcTopGear:
return "487247465"
case .bbcSport:
return "377388936"
default:
return ""
}
}
var name: String {
switch self {
case .bbcEasyCook:
return "BBC Easy Cook Magazine"
case .bbcTopGear:
return "BBC Top Gear Magazine"
case .bbcSport:
return "BBC Sport"
default:
return ""
}
}
}
Then create a ContentViewModel
:
class ContentViewModel: ObservableObject {
@Published var isPresentingOverlay: Bool = false
@Published var presentedAppRecommendation: AppRecommendation = .none {
willSet {
if newValue == .none {
isPresentingOverlay = false
} else {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
self.isPresentingOverlay = true
})
}
}
}
}
Why is there a delay when setting isPresentingOverlay
to true
? It gives any existing overlay enough time to dismiss before presenting the next overlay.
Finally, in the ContentView
itself:
- Hook up the
ContentViewModel
as a@StateObject
- Add some
Buttons
that will set thepresentedAppRecommendation
in theContentViewModel
- Add and configure the
appStoreOverlay(isPresented:configuration:)
modifier with theisPresentingOverlay
andpresentedAppRecommendation.id
properties from theContentViewModel
struct ContentView: View {
@StateObject private var model = ContentViewModel() // 1
var body: some View {
List {
Button(action: {
model.presentedAppRecommendation = .none
model.presentedAppRecommendation = .bbcEasyCook
}, label: {
Label(
title: { Text(AppRecommendation.bbcEasyCook.name).foregroundColor(.primary) },
icon: { Image(systemName: "timer")
.foregroundColor(.purple) }
)
}) // 2
Button(action: {
model.presentedAppRecommendation = .none
model.presentedAppRecommendation = .bbcTopGear
}, label: {
Label(
title: { Text(AppRecommendation.bbcTopGear.name).foregroundColor(.primary) },
icon: { Image(systemName: "car")
.foregroundColor(.red) }
)
}) // 2
Button(action: {
model.presentedAppRecommendation = .none
model.presentedAppRecommendation = .bbcSport
}, label: {
Label(
title: { Text(AppRecommendation.bbcSport.name).foregroundColor(.primary) },
icon: { Image(systemName: "sportscourt")
.foregroundColor(.yellow) }
)
}) // 2
}
.listStyle(InsetGroupedListStyle())
.appStoreOverlay(isPresented: $model.isPresentingOverlay) { () -> SKOverlay.Configuration in
SKOverlay.AppConfiguration(appIdentifier: model.presentedAppRecommendation.id, position: .bottom) // 3
}
}
}
Tying everything together and launching an app gives you this: