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:

  1. Hook up the ContentViewModel as a @StateObject
  2. Add some Buttons that will set the presentedAppRecommendation in the ContentViewModel
  3. Add and configure the appStoreOverlay(isPresented:configuration:) modifier with the isPresentingOverlay and presentedAppRecommendation.id properties from the ContentViewModel
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: