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

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

Avatar for Kuba Suder

Kuba Suder

March 23, 2017
Tweet

More Decks by Kuba Suder

Other Decks in Programming

Transcript

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

    (HTML/JS) • no ObjC/Swi> knowledge required • easy to port from Chrome/Firefox
  2. (Old) Safari Extensions Cons: • you have to use web

    technologies ! • no way to make " # • no analy5cs? $ • the review process is an absolute nightmare %
  3. 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. Safari App Extensions Cons: • no way to reuse code/skills

    from other browsers • needs to be contained in an app
  5. 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. 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. 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. 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. 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. 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. 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: "…") { … }
  12. 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>
  13. Communica)ng with the app • enable App Groups • project

    capabili3es • NOT on developer.apple.com (unlike iOS apps)
  14. 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
  15. 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 ;)
  16. 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() }
  17. 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