Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Effective Migration to Mac Catalyst

Effective Migration to Mac Catalyst

Starting in macOS Catalina, it’s possible to compile UIKit apps to run on macOS. This new environment can be confusing, since parts of UIKit work differently, and parts of AppKit are available to your iPad app! We’ll explore how to adapt your iPad app for Mac effectively and take a closer look into implementing macOS features like menu bars, tool bars, and window controls with UIKit.

Noah Gilmore

July 28, 2019
Tweet

More Decks by Noah Gilmore

Other Decks in Programming

Transcript

  1. @noahsark769 What is Catalyst? - Introduced at WWDC 2019 -

    Allows a UIKit codebase to compile into an AppKit Mac app
  2. @noahsark769 What is Catalyst? - “Mac Catalyst”, “UIKitForMac”, “iPad Apps

    for Mac” (formerly “Marzipan”) refer to the same thing
  3. @noahsark769 Agenda - Beyond the checkbox - Mac patterns in

    UIKit - AppKit bundles (and demo) - Should I use Catalyst?
  4. @noahsark769 Things that might not work - UIWebView - CoreTelephony

    (used by analytics libraries) - Your cross-platform static library (.a) - Things that don’t make sense: ARKit, etc @noahsark769
  5. @noahsark769 Conditional compilation This will cause a link error! #if

    canImport(UIKit) print(UIWebView.self) #endif
  6. @noahsark769 Dependencies - Submit pull requests or support tickets for

    dependencies - Request distribution of static libraries in .xcframework format
  7. @noahsark769 Windows guard let containingScene = self.view.window?.windowScene else { return

    } UIApplication.shared.requestSceneSessionDestruction( containingScene.session, options: nil, errorHandler: nil )
  8. @noahsark769 Windows - New windows assume system-assigned size and position

    - When the user drags, windows resize and update your autolayout constraints - No way to update the size or position of the window with code
  9. @noahsark769 App Menus override func buildMenu(with builder: UIMenuBuilder) { super.buildMenu(with:

    builder) builder.insertChild(UIMenu( __title: "Jump", image: nil, identifier: UIMenu.Identifier(rawValue: "jump"), options: [], children: [ // ... ] ), atEndOfMenu: .view) }
  10. @noahsark769 App Menus - Use a child UIKeyCommand for keyboard

    shortcut enabled items - Can also be used for non-menu keyCommands on your first responder let command = UIKeyCommand( input: "J", modifierFlags: [.command], action: #selector(self.jumpSelected(_:)) ) command.title = "Jump to Windows"
  11. @noahsark769 App Menus - Use UICommand for items without shortcuts

    let noShortcutCommand = UICommand( __title: "No shortcut", image: nil, action: #selector(self.noShortcutSelected), propertyList: nil )
  12. @noahsark769 Touch Bar extension TouchBarViewController: NSTouchBarProvider { var touchBar: NSTouchBar?

    { let bar = NSTouchBar() let identifier = NSTouchBarItem.Identifier(rawValue: "clickme") bar.defaultItemIdentifiers = [identifier] bar.templateItems = [ NSButtonTouchBarItem( identifier: identifier, title: "button", target: self, action: #selector(didTapClickMe(_:)) ) ] return bar } }
  13. @noahsark769 Touch Bar - All system default touch bar items

    available (color picker, slider, etc) - No NSCustomTouchBarItem
  14. @noahsark769 Tool Bar - UIWindowScene has a UITitleBar which you

    can give an NSToolbar - Initialize an NSToolbarItem with a UIBarButtonItem - Custom views (and images) are not supported - Import <UIKit/NSToolbar+UIKitAdditions.h>
  15. @noahsark769 Tool Bar #if targetEnvironment(macCatalyst) if let titlebar = scene.titlebar

    { let toolbar = NSToolbar(identifier: "toolbar") toolbar.delegate = self titlebar.toolbar = toolbar titlebar.titleVisibility = .hidden } #endif
  16. extension SceneDelegate: NSToolbarDelegate { var identifiers: [NSToolbarItem.Identifier] { return [

    NSToolbarItem.Identifier(rawValue: "test"), .flexibleSpace, NSToolbarItem.Identifier(rawValue: "other") ] } func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { return self.identifiers } func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { return self.identifiers } // ... }
  17. @noahsark769 Tool Bar func toolbar( _ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier:

    NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool ) -> NSToolbarItem? { switch itemIdentifier.rawValue { case "test": let item = NSToolbarItem( itemIdentifier: itemIdentifier, barButtonItem: UIBarButtonItem( barButtonSystemItem: .add, target: self, action: #selector(didTapAddButton) ) ) return item default: return nil } }
  18. @noahsark769 Other AppKit components - Drag and Drop - UIDragInteraction,

    UIDropInteraction, beta 4 issues - Context Menus - UIContextMenuInteraction - File access - UIDocumentPickerViewController
  19. @noahsark769 How much of AppKit can I import? - Pretty

    much just NSTouchBar and NSToolbar - No direct access to NSWindow - No complicated views like NSTableView
  20. @noahsark769 AppKit Bundles - Write an AppKit Plug-In bundle and

    import using NSBundle - Plug-In bundles have access to the full AppKit runtime (NSApplication, NSWindow, etc) - Present panels, control windows, etc - Communicate with UIKit via NotificationCenter or Obj-C interface - No mixing UIKit and AppKit view hierarchies
  21. Don’t want to learn AppKit, like me AppKit is more

    approachable than ever ❌ @noahsark769
  22. ✅ Significant existing UIKit iPad app ✅ Small number of

    external dependencies ✅ Little need for macOS-specific UI ✅ Experimental feature support @noahsark769
  23. Further reading - WWDC 2019 #205: Introducing iPad Apps for

    Mac - WWDC 2019 #235: Taking iPad Apps for Mac to the Next Level - WWDC 2019 #212: Introducing Multiple Windows on iPad - Ultimate Catalyst Guide - Beyond the Checkbox with Catalyst and AppKit @noahsark769