Building Safari app extensions

Building Safari app extensions

A short talk about building Safari extensions using the new SDK from Safari 10.0 - as extensions included in a Mac app, written in Xcode using native code

145a2482320cc755549b37feb424d8c5?s=128

Kuba Suder

March 23, 2017
Tweet

Transcript

  1. 6.

    (Old) Safari Extensions Pros: • you can use web technologies

    (HTML/JS) • no ObjC/Swi> knowledge required • easy to port from Chrome/Firefox
  2. 7.

    (Old) Safari Extensions Cons: • you have to use web

    technologies ! • no way to make " # • no analy5cs? $ • the review process is an absolute nightmare %
  3. 11.

    Safari App Extensions Pros: • built in Xcode/Swi1 ❤ (or

    ObjC if you have to :) • can be distributed via Mac App Store • updated together with the app • normal app review process " • can be paid ##
  4. 12.

    Safari App Extensions Cons: • no way to reuse code/skills

    from other browsers • needs to be contained in an app
  5. 15.

    Building an extension • Main object: SFSafariExtensionHandler • handle toolbar

    bu3on callbacks • handle context menu callbacks • handle messages from the JavaScript side • whatever else you need running in the background
  6. 17.

    Injec&ng scripts • SFSafariContentScript key • can be applied only

    to selected sites/pages • runs in the opened website's environment • safari.extension - communicate with Safari and your app • can access assets from the app bundle
  7. 18.

    Communica)ng with the script JS: safari.self.addEventListener('message', function() { … });

    safari.extension.dispatchMessage('newItemsLoaded'); App: func messageReceived(withName messageName: String, from page: SFSafariPage, userInfo: [String: Any]!) { page.dispatchMessageToScript(withName: "reload", userInfo: nil) }
  8. 20.

    Adding a toolbar bu.on • SFSafariToolbarItem key & class •

    icon as a template image • PDF (!), 19×19, simple black outline • 10.1: can be changed at runDme • can be enabled/disabled • can have a badge
  9. 21.

    Responding to bu.on clicks Op#on 1: no UI • get

    a call toolbarItemClicked(in:) Op#on 2: popover • na$ve Cocoa view + SFSafariExtensionViewController • scales automa$cally, must use AutoLayout • might behave in weird ways (layers, transparency, anima$ons)
  10. 23.

    Context menu items • SFSafariContextMenu key • contextMenuItemSelected(withCommand:in:userInfo:) • valida>on

    - can be shown or hidden depending on the context • you can add mul>ple items
  11. 24.
  12. 25.

    Content blocking • Safari extension can manage the content blocker

    (Safari 10.1) SFContentBlockerManager.getStateOfContentBlocker( withIdentifier: "eu.mackuba.BannerHunterMac.SafariExtension", completionHandler: { state, error in … } ) SFContentBlockerManager.reloadContentBlocker(withIdentifier: "…") { … }
  13. 27.

    Site access permissions • declare access level: None / Some

    / All • access needed to read URL :( <key>SFSafariWebsiteAccess</key> <dict> <key>Level</key> <string>Some</string> <key>Allowed Domains</key> <array> <string>*.facebook.com</string> </array> </dict>
  14. 29.

    Communica)ng with the app • enable App Groups • project

    capabili3es • NOT on developer.apple.com (unlike iOS apps)
  15. 30.

    Communica)ng with the app • shared data folder let appGroupId

    = "KL585M628D.eu.mackuba.BannerHunter" let fileManager = FileManager.defaultManager() let container = fileManager.containerURL( forSecurityApplicationGroupIdentifier: appGroupId ) • located in: ~/Library/Group Containers
  16. 31.

    Communica)ng with the app • shared user defaults container let

    defaults = UserDefaults(suiteName: appGroupId) • no support for "defaults write …" ! • edit .plist file in the Group Container • killall cfprefsd • plz file a radar ;)
  17. 32.

    Communica)ng with the app (10.1) SFSafariApplication.dispatchMessage( withName: "startSync", toExtensionWithIdentifier: "eu.mackuba.BannerHunterMac.Extension",

    userInfo: ["userId": 567] ) func messageReceivedFromContainingApp( withName messageName: String, userInfo: [String : Any]? = nil) { startSync() }
  18. 33.

    Pro %ps • Develop → Allow Unsigned Extensions • console.log-

    and NSLog-based debugging ! • disable app sandbox during development • use a framework to share code • extension is disabled by default aDer installaEon • for non-MAS apps, app needs to be started once