I'm trying to utilize Apple's new TipKit where I still support iOS16. Is there a way to initialize the Tip without making the entire struct as @available(iOS 17, *).  Example being
struct MainHomeEntryView: View {
   var myTip = MyTip()
      ....
}
error: 'SubmitTip' is only available in iOS 17 or newer
We had the same problem and came up with this small helper:
import SwiftUI
import TipKit
/// Small helper protocol for a tip that is available below iOS 17
/// so that we can pass tips around and have members storing tips,
/// as stored properties can't be marked with `@available`.
@available(iOS, obsoleted: 17, message: "Can be removed once we only support iOS 17+")
public protocol TipShim {
    @available(iOS 17, *)
    var tip: AnyTip { get }
}
@available(iOS 17, *)
public extension Tip where Self: TipShim {
    var tip: AnyTip { AnyTip(self) }
}
public extension View {
    /// Helper for making the `popupTip` modifier available for iOS <17.
    @available(iOS, obsoleted: 17, message: "Can be removed once we only support iOS 17+")
    @ViewBuilder func popupTipShim(_ tipShim: TipShim?, arrowEdge: Edge = .top) -> some View {
        if #available(iOS 17, *), let tip = tipShim?.tip {
            self.popoverTip(tip, arrowEdge: arrowEdge)
        } else {
            self
        }
    }
}
With it, you only need a minimal amount of if #available checks. You only have to let your tip conform to TipShim:
struct MyTip: Tip, TipShim {
    // no additional implementation required, 
    // as this was done in the extension above
}
and then you can use it like this:
struct MainHomeEntryView: View {
    var myTip: TipShim?
   
    init() {
        if #available(iOS 17, *) {
            self.myTip = MyTip()
        }
    }
    var body: some View {
        MyView()
            .popupTipShim(self.myTip)
    }
   
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With