Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2021 EssentialsSwiftUI & UI Frameworks

WWDC21 · 24 min · Essentials / SwiftUI & UI Frameworks

Qualities of a great Mac Catalyst app

Discover best practices, tools, and techniques to help craft the best possible Mac Catalyst app. We’ll take you through key considerations when you bring your iPad app to macOS, explore detailed code examples for refining your interface and experience, and show you how to distribute your Mac app to everyone. To get the most out of this session, we recommend a basic familiarity with Mac Catalyst. Watch “What’s new in Mac Catalyst” from WWDC21 to get an overview of the latest features for bringing your iPad app to Mac. And for more on improving your macOS experience, watch “Optimize the interface of your Mac Catalyst app” from WWDC20.

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 14 snippets

System button swift · at 6:50 ↗
let button = UIButton(type: .system)
button.setTitle("Button", for: .normal)
Pull-down button swift · at 7:06 ↗
button.menu = UIMenu(...)
button.showsMenuAsPrimaryAction = true
Pop-up button swift · at 7:44 ↗
button.menu = UIMenu(...)
button.showsMenuAsPrimaryAction = true
button.changesSelectionAsPrimaryAction = true
Checkbox swift · at 8:24 ↗
let checkbox = UISwitch()
if checkbox.style == .checkbox {
    checkbox.title = "Checkbox"
}
Delegating actions swift · at 13:20 ↗
final class MyView: UIView {
    override func target(forAction action: Selector, withSender sender: Any?) -> Any? {
        if action == #selector(Model.setAsFavorite(_:)) {
            return myModel
        } else {
            return super.target(forAction: action, withSender: sender)
        }
    }
}
Requesting a new scene swift · at 14:43 ↗
let viewDetailActivityType = "viewDetail"
let itemIDKey = "itemID"

final class MyView: UIView {
    @objc func viewDoubleClicked(_ sender: Any?) {
        let userActivity = NSUserActivity(activityType: viewDetailActivityType)
        userActivity.userInfo = [itemIDKey: selectedItem.itemID]
        UIApplication.shared.requestSceneSessionActivation(nil,
            userActivity: userActivity,
            options: nil,
            errorHandler: { error in //...
        })
    }
    //...
}
Responding to a new scene request swift · at 15:57 ↗
let viewDetailActivityType = "viewDetail"

final class AppDelegate: UIApplicationDelegate {
    func application(_ application: UIApplication, 
        configurationForConnecting session: UISceneSession, 
        options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        if let activity = options.userActivities.first {
            if activity.activityType == viewDetailActivityType {
                return UISceneConfiguration(name: "DetailViewer", sessionRole:session.role)
            }
        }
        return UISceneConfiguration(name: "Default Configuration",
            sessionRole: session.role)
    }
    //...
}
Setting item ID on new scene's root view controller swift · at 17:13 ↗
let itemIDKey = "itemID"

final class SceneDelegate: UIWindowSceneDelegate {
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
        options: UIScene.ConnectionOptions) {
        if let userActivity = connectionOptions.userActivities.first {
            if let itemId = userActivity.userInfo?[itemIDKey] as? ItemIDType {
               // Set item ID on new view controller
            }
        }
        //...
    }
    //...
Saving state for later restoration swift · at 17:47 ↗
final class SceneDelegate: UIWindowSceneDelegate {
    func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
        //...
    }
}
State restoration swift · at 17:57 ↗
final class AppDelegate: UIApplicationDelegate {
    func application(_ application: UIApplication, 
        configurationForConnecting session: UISceneSession, 
        options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        //...
    }
}
Handle both new scene requests and state restoration swift · at 18:42 ↗
let itemIDKey = "itemID"

final class SceneDelegate: UIWindowSceneDelegate {
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
        options connectionOptions: UIScene.ConnectionOptions) {
        if let userActivity = connectionOptions.userActivities.first ??
            session.stateRestorationActivity {
            if let itemId = userActivity.userInfo?[itemIDKey] as? ItemIDType {
               // Set item ID on new view controller
            }
        }
    }
}
Provide sharing configuration for the scene swift · at 20:20 ↗
final class RootViewController: UIViewController {
    override var activityItemsConfiguration: UIActivityItemsConfigurationReading? {
      get { UIActivityItemsConfiguration(objects: [image]) }
      //...
    }
}
Support sharing through context menu swift · at 20:56 ↗
final class MyView: UIView {
    override var activityItemsConfiguration: UIActivityItemsConfigurationReading? {
      get { UIActivityItemsConfiguration(objects: images) }
      //...
    }

    func viewDidLoad() {
      let contextMenuInteraction = UIContextMenuInteraction(delegate: self)
      addInteraction(contextMenuInteraction)
    }
}
Supporting continuity camera swift · at 22:08 ↗
final class MyView: UIView {
    override var pasteConfiguration: UIPasteConfiguration? {
      get { UIPasteConfiguration(forAcceptingClass: UIImage.self) }
      //...
    }

    func willMove(toWindow: UIWindow) {
       addInteraction(contextMenuInteraction)
    }

    override func paste(itemProviders: [NSItemProvider]) {
       for itemProvider in itemProviders {
            if itemProvider.canLoadObject(ofClass: UIImage.self) {
                if let image = try? await itemProvider.loadObject(ofClass:UIImage.self) {
                    insertImage(image)
                }          
                //...

Resources