TipKit: How to use Tips with Objective C and UIKit

484 Views Asked by At

TipKit was just released in Xcode 15 beta 5 but unfortunately the app I am working on has a majority Objective C and UIKit codebase. Apple has only provided code snippets with Swift and SwiftUI (so far that I have seen). Does anyone know how to implement TipKit with Objective C and UIKit? I have seen articles such as https://tarikdahic.com/posts/display-swiftui-views-from-objective-c-codebase/ that describe how to launch a SwiftUI view from Objective C. From the TipKit documentation, I did a brief dive and saw TipUIPopoverViewController, which is a UIViewController and can be loaded with UIKit. I have not tested it, but that could be a solution.

As far as configuring the Tips, which you must do in the main class that launches the app, I don't know how to configure them in Objective C. My class looks like:

int main(int argc, char *argv[]) {
    @autoreleasepool {
        NSString *logPath = [NSHomeDirectory() stringByAppendingPathComponent: @"removed for privacy"];
        freopen([logPath fileSystemRepresentation], "a", stderr);
        int retVal = UIApplicationMain(argc, argv, nil, nil);
        return retVal;
    }
}

In Swift, you can configure the Tips like so:

var body: some Scene {
    WindowGroup {
        ContentView()
            .task {
                try? await Tips.configure()
            }
    }
}

What is the Objective C version of the above code? I am not an adept Objective C coder, I have very little experience.

1

There are 1 best solutions below

1
grep On

Step 1 - In your AppDelegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
     
        if #available(iOS 17.0, *) {
            try? Tips.configure([.displayFrequency(.daily)])
        }
        
        return true
}

Step 2 - Define your tip

import TipKit
import UIKit

@available(iOS 17.0, *)
class AppTips {
    
    struct TipData: Tip {
        var id: String
        var title: Text
        var message: Text?
        
        @Parameter
        var readyToLoad: Bool = false
        
        var rules: [Rule] {
            #Rule($readyToLoad) { $0 == true }
        }
        
        func checkAndLoad(aVC: UIViewController, aBT: UIView) {
            var tipObservationTask: Task<Void, Never>?
            tipObservationTask = tipObservationTask ?? Task.detached { @MainActor in
                for await shouldDisplay in self.shouldDisplayUpdates {
                    if shouldDisplay {
                        let popoverController = TipUIPopoverViewController(self, sourceItem: aBT)
                        aVC.present(popoverController, animated: true)
                    } else {
                        if aVC.presentedViewController is TipUIPopoverViewController {
                            aVC.dismiss(animated: true)
                        }
                    }
                }
            }
        }
    }
    
    
    static let someTip = TipData(
        id: "SomeTipID",
        title: Text("Your tip title"),
        message: Text("Your tip message.")
    )   
}

Step 3 - Load your tip

class YourViewController: UIViewController {

func viewDidAppear() {
    checkAndLoadTips()
}

func checkAndLoadTips() {
        if #available(iOS 17.0, *) {
            AppTips.someTip.checkAndLoad(aVC: self, aBT: someButton)
        }
    }
}