diff --git a/_layouts/documentation.html b/_layouts/documentation.html index 6e9088c..85563fc 100644 --- a/_layouts/documentation.html +++ b/_layouts/documentation.html @@ -2,14 +2,6 @@ layout: default ---
-
- -
{{ content }} diff --git a/documentation/bundles/index.md b/documentation/bundles/index.md index 8a00bf7..22e5e4f 100644 --- a/documentation/bundles/index.md +++ b/documentation/bundles/index.md @@ -3,7 +3,10 @@ layout: documentation id: documentation title: Bundles --- -### Bundles + +### Sparkle 1.x + +#### Bundles If you want to support updating for a non-app bundle, such as a Preference Pane, you cannot simply instantiate an SUUpdater instance in your MainMenu.nib as described in [Basic Setup](/documentation/#basic-setup). The reason is that SUUpdater creates a separate shared instance for every bundle, and the instance that is instantiated in a .nib file will always be the instance for the hosting .app bundle. This is the SUUpdater instance returned by `[SUUpdater sharedUpdater]` or `[[SUUpdater alloc] init]`. Note that the latter is used to instantiate an instance of a custom class in a .nib. @@ -17,7 +20,7 @@ For Sparkle version 1.5b6 there is one more consideration. If SUUpdater is insta Due to poor design, if you want to use profiling with a bundle, you'll have to proxy a call to applicationDidFinishLaunching to get the permission dialog to appear: -### Subclassing SUUpdater +#### Subclassing SUUpdater An alternative approach is to use a subclass of SUUpdater whose shared instance is the updater for your bundle. You can then also instantiate this class in a .nib, so you can essentially follow step 2 in [Basic Setup](/documentation/#basic-setup) to initialize your updater. Make sure you also read in the header for your SUUpdater subclass in your .nib. @@ -32,3 +35,15 @@ The subclass only needs to implement the following two methods: { return [self initForBundle:[NSBundle bundleForClass:[self class]]]; } + +### Sparkle 2.x (Beta) + +#### Bundles + +The story with updating bundles between Sparkle 1.x and 2.x is a bit different. + +First, Sparkle 2.x supports updating any Sparkle-based bundle, which is not just limited to your own application or process. The updater distinguishes the `hostBundle` from the `applicationBundle`. The `hostBundle` is the bundle that Sparkle updates and the `applicationBundle` is the bundle that is re-launched (if applicable). See [Sparkle 2.x's APIs](/documentation/customization#sparkle-2x-apis-beta) for more information on instantiating your own updater. + +Second, Sparkle 2.x doesn't keep track of shared updater instances and doesn't try to prohibit multiple updater instances from existing -- either inside the same process or multiple updaters updating the same bundle from different processes. In fact, it is even possible for one updater to start an update (say a silent deferred one), and for a second updater (say sparkle-cli) to resume that same update for the same bundle. + +If you want to update a plug-in, injecting Sparkle.framework or sharing a version of Sparkle with the host process is not advisable. Instead, look into calling out to an out-of-process tool that can update the plug-in like [sparkle-cli](/documentation/sparkle-cli) can. diff --git a/documentation/customization/index.md b/documentation/customization/index.md index 90cd9ce..397d6e9 100644 --- a/documentation/customization/index.md +++ b/documentation/customization/index.md @@ -13,7 +13,7 @@ Here are the main routes by which you can bend Sparkle's behavior to your will: | Key | Type | Value | | --- | ---- | ----- | ------- | | `SUFeedURL` | String | The URL of your appcast, e.g. `https://example.com/appcast.xml`. It's recommended to always set it in Info.plist, even if you change it later programmatically. | -| `SUEnableAutomaticChecks` | Boolean | Setting to `YES` (recommended) enables checking for updates (but not installation) by default, without asking your users for permission first.
By default, if it's not set to any value, users will be prompted for permission before the first update check.
Setting to `NO` disables update checks, but can be overridden by a call to `SUUpdater`'s `setAutomaticallyChecksForUpdates:` | +| `SUEnableAutomaticChecks` | Boolean | Setting to `YES` (recommended) enables checking for updates (but not installation) by default, without asking your users for permission first.
By default, if it's not set to any value, users will be prompted for permission before the first update check.
Setting to `NO` disables update checks, but can be overridden by a call to `SUUpdater`'s (or `SPUUpdater`'s in 2.x) `setAutomaticallyChecksForUpdates:` | | `SUPublicEDKey` | String | The base64-encoded public EdDSA key. Use Sparkle's `generate_keys` tool to get it. | | `SUEnableSystemProfiling` | Boolean | Default: `NO`. Enables anonymous system profiling. See [System Profiling](/documentation/system-profiling) for more. | | `SUScheduledCheckInterval` | Number | The number of seconds between updates. The default is `86400` (1 day). Setting to 0 disables updates.

**Note:** this has a minimum bound of 1 hour in order to keep you from accidentally overloading your servers. | @@ -23,7 +23,9 @@ Here are the main routes by which you can bend Sparkle's behavior to your will: | `SUBundleName` | String | Optional alternative bundle display name. For example, if your bundle name already has a version number appended to it, setting this may help smooth out certain messages, e.g. "MyApp 3 4.0 is now available" vs "MyApp 4.0 is now available". | | `SUDefaultsDomain` | String | Optional alternative `NSUserDefaults` domain name if you don't want to use the standard user defaults, for example when accessing preferences from an App Group suite. | -### Calls to SUUpdater +### Sparkle 1.x APIs + +#### Calls to SUUpdater The `SUUpdater` object is the main controller for the updating system in your app. There is a singleton instance of this class for each bundle being updated. If you're trying to update the running .app, you can retrieve the appropriate `SUUpdater` by calling `[SUUpdater sharedUpdater]`. If you're trying to update some [other bundle](/documentation/bundles/), you can use `[SUUpdater updaterForBundle:(NSBundle *)myBundle]`. @@ -82,7 +84,7 @@ A few more methods of interest: - (void)setDelegate:(id)delegate; // See below for more information on the delegate. - delegate; -### SUUpdater delegate methods +#### SUUpdater delegate methods You can control the SUUpdater's behavior a little more closely by providing it with a delegate. Here are the delegate methods you might implement: @@ -133,6 +135,133 @@ You can control the SUUpdater's behavior a little more closely by providing it w - (NSArray *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; -### Other options +#### Other options If these methods aren't enough to do what you need, you're going to have to dig into Sparkle's code. You might start by creating a different update driver: check out SUBasicUpdateDriver.h to get an idea. + +--- + +### Sparkle 2.x APIs (Beta) + +#### Calls to SPUUpdater + +The `SPUUpdater` object is the main controller for the updating system in your app. If you are using `SPUStandardUpdaterController` in a nib, you can retrieve the updater via its `updater` property. Otherwise you will have had to create an `SPUUpdater` and start the updater (`-startUpdater:error:`) yourself. + +Once you have the `SPUUpdater` instance, there are a few interesting accessors you could use. Please use them only if you need dynamic behavior (e.g. user preferences). Do not use these functions to set default configuration. Use Info.plist keys to set default configuration instead. + + @property (nonatomic) BOOL automaticallyChecksForUpdates; + + @property (nonatomic) NSTimeInterval updateCheckInterval; + + @property (nonatomic, readonly) NSURL *feedURL; + + // Using this method is discouraged. Consider -[SPUUpdaterDelegate feedURLStringForUpdater:] or -[SPUUpdaterDelegate feedParametersForUpdater:sendingSystemProfile:] instead. + - (void)setFeedURL:(NSURL *)feedURL; + + @property (nonatomic) BOOL sendsSystemProfile; + + @property (nonatomic) BOOL automaticallyDownloadsUpdates; + +There is a risk of race conditions. If you want to make sure these settings are changed before the first automatic update check, you should do this as soon as possible. For an application instantiating `SPUStandardUpdaterController` in a nib, this will be in the `NSApplication` delegate method `-applicationWillFinishLaunching:`. Otherwise this will be right after you instantiate a `SPUUpdater` instance in your code. + +A few methods of interest if you are instantiating `SPUUpdater` programmatically: + + // This is the SPUUpdater initializer. You choose: + // `hostBundle` - The bundle that should be targetted for updating. + // `applicationBundle` - The application bundle that should be relaunched and waited for termination. Usually this can be the same as hostBundle. This may differ when updating a plug-in or other non-application bundle. + // `userDriver` - The user driver that Sparkle uses for user update interaction. Use a `SPUStandardUserDriver` for Sparkle's standard UI. + // `delegate` - The optional delegate for SPUUpdater. + - (instancetype)initWithHostBundle:(NSBundle *)hostBundle applicationBundle:(NSBundle *)applicationBundle userDriver:(id )userDriver delegate:(id _Nullable)delegate; + + // This method checks if Sparkle is configured properly, may show a prompt asking for automatic updates (if needed), + // and may start an update check if automatic update checking is enabled. Must be called to start the updater. + - (BOOL)startUpdater:error:; + + // Checks for updates and display progress while doing so. This is meant for users initiating an update check. + - (void)checkForUpdates; + +The `SPUUserDriver` protocol is the API in Sparkle for controlling the user interface and interaction. If you are using `SPUStandardUpdaterController` in a nib, you can retrieve the user driver via its `userDriver` property. If you are interested in creating your own user interface, please see the header documentation in [`SPUUserDriver.h`](https://github.com/sparkle-project/Sparkle/blob/2.x/Sparkle/SPUUserDriver.h). Note few of the user-facing delegate methods in Sparkle 1.x's `SUUpdaterDelegate` were moved to `SPUStandardUserDriverDelegate`. + +Properties of interest if you are instantiating the standard user driver `SPUStandardUserDriver` yourself: + + // Indicates whether or not an update is in progress as far as the user's perspective is concerned + // A typical application may rely on this property for its check for updates menu item validation + // If you were using -[SUUpdater updateInProgress] in Sparkle 1.x, you can use !canCheckForUpdates instead. + @property (nonatomic, readonly) BOOL canCheckForUpdates; + +These are properties of interest if you are using `SPUStandardUpdaterController`. You should hook these up in Xcode's interface builder and not programmatically. + + // Interface builder outlet for the updater's delegate + @property (nonatomic, weak, nullable) IBOutlet id updaterDelegate; + + // Interface builder outlet for the user driver's delegate. + @property (nonatomic, weak, nullable) IBOutlet id userDriverDelegate; + + // Explicitly lets the user check for updates and displays a progress dialog while doing so. + // Connect this action to a main menu item if so desired. + - (IBAction)checkForUpdates:(id)sender; + +More methods of interest on `SPUUpdater`: + + // This kicks off an update meant to be programmatically initiated. That is, + // it will display no UI unless it actually finds an update, in which case it + // proceeds as usual. If the automated downloading is turned on, however, + // this will invoke that behavior, and if an update is found, it will be + // downloaded and prepped for installation. + // + // You do not need to call this. Sparkle calls it automatically according to + // the update schedule. + - (void)checkForUpdatesInBackground; + + // This begins a "probing" check for updates which will not actually offer to + // update to that version. The delegate methods, though, (up to updater:didFindValidUpdate: + // and updaterDidNotFindUpdate:), are called, so you can use that information in your UI. + // Essentially, you can use this to UI-lessly determine if there's an update. + - (void)checkForUpdateInformation; + + // Date of last update check. Returns nil if no check has been performed. + @property (nonatomic, readonly, copy, nullable) NSDate *lastUpdateCheckDate; + + // Call this to appropriately schedule or cancel the update checking timer according + // to the preferences for time interval and automatic checks. + - (void)resetUpdateCycle; + +#### Delegate methods + +You can control the SPUUpdater's behavior a little more closely by providing it with a delegate. Here are the delegate methods you might implement: + + // Use this to override the default behavior for Sparkle prompting the + // user about automatic update checks. You could use this to make Sparkle + // prompt for permission on the first launch instead of the second. + - (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SPUUpdater *)updater; + + - (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast; + + // If you're using special logic or extensions in your appcast, implement + // this to use your own logic for finding a valid update, if any, in the given appcast. + - (nullable SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SPUUpdater *)updater; + + - (void)updater:(SPUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)item; + - (void)updaterDidNotFindUpdate:(SPUUpdater *)updater; + + // Sent immediately before installing the specified update. + - (void)updater:(SPUUpdater *)updater willInstallUpdate:(SUAppcastItem *)item; + + // Return YES to delay the relaunch until you do some processing. + // Invoke the provided installHandler block to continue the relaunch. + - (BOOL)updater:(SPUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item untilInvokingBlock:(void (^)(void))installHandler; + + // Called immediately before relaunching. + - (BOOL)updaterShouldRelaunchApplication:(SPUUpdater *)updater; + + // This method allows you to provide a custom version comparator. + // If you don't implement this method or return nil, the standard version + // comparator will be used. See SUVersionComparisonProtocol.h for more. + - (nullable id)versionComparatorForUpdater:(SPUUpdater *)updater; + + // This method allows you to add extra parameters to the appcast URL, + // potentially based on whether or not Sparkle will also be sending along + // the system profile. This method should return an array of dictionaries + // with keys: "key", "value", "displayKey", "displayValue", the latter two + // being human-readable variants of the former two. + - (NSArray *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; diff --git a/documentation/index.md b/documentation/index.md index f4d235d..dc99b4f 100644 --- a/documentation/index.md +++ b/documentation/index.md @@ -5,9 +5,9 @@ title: Documentation --- ## Basic Setup -If your app already has an older version of Sparkle, see [upgrading from previous versions](/documentation/upgrading/). +If your app already has an older version of Sparkle or you wish to migrate to Sparkle 2.0 beta, see [upgrading from previous versions](/documentation/upgrading/). -Note that Sparkle does [not yet support](//github.com/{{ site.github_username }}/Sparkle/issues/363) sandboxed applications. +Note sandboxed applications are only supported in Sparkle 2.0 which is currently in beta. ### 1. Add the Sparkle framework to your project @@ -15,7 +15,6 @@ If you use [CocoaPods](//cocoapods.org): * Add `pod 'Sparkle'` to your Podfile. * Add or uncomment `use_frameworks!` in your Podfile. - * [Xcode 7 and older require more steps](/documentation/cocoapods/). If you don't have CocoaPods, then add Sparkle manually: @@ -27,14 +26,13 @@ If you don't have CocoaPods, then add Sparkle manually: * Make sure the framework is copied into your app bundle: * Click on your project in the Project Navigator. * Click your target in the project editor. - * Click on the Build Phases tab. - * Choose Editor › Add Build Phase › Add Copy Files Build Phase. - * Click the disclosure triangle next to the new build phase. - * Choose Frameworks from the Destination list. - * Drag Sparkle.framework from the Project Navigator left sidebar to the list in the new Copy Files phase. -* In Build Settings tab set "Runpath Search Paths" to `@loader_path/../Frameworks` (for non-Xcode projects add the flags `-Wl,-rpath,@loader_path/../Frameworks`). + * Click on the General tab. + * In Frameworks, Libraries, and Embedded Content section, change Sparkle.framework to Embed & Sign. +* In Build Settings tab set "Runpath Search Paths" to `@loader_path/../Frameworks` (for non-Xcode projects add the flags `-Wl,-rpath,@loader_path/../Frameworks`). This is not a necessary step in recent versions of Xcode. * If you have your own process for copying/packaging your app make sure it preserves symlinks! +CocoaPods and pre-built binaries for Sparkle 2.x aren't currently available. To build [2.x](https://github.com/sparkle-project/Sparkle/tree/2.x), clone Sparkle's repository with all its submodules, `git checkout 2.x` branch, run `make release`, and check out the binaries in the resulting `Sparkle-2.0.0.tar.xz` archive. Sandboxed applications using Sparkle 2.x require [additional setup](/documentation/sandboxing). + ### 2. Set up a Sparkle updater object These instructions are for regular .app bundles. If you want to update a non-app bundle, such as a Preference Pane or a plug-in, follow [step 2 for non-app bundles](/documentation/bundles/). @@ -47,6 +45,8 @@ These instructions are for regular .app bundles. If you want to update a non-app * Type `SUUpdater` in the Class box of the Custom Class section in the inspector. * If you'd like, make a "Check for Updates..." menu item in the application menu; set its target to the `SUUpdater` instance and its action to `checkForUpdates:`. +If you are using Sparkle 2.x, `SUUpdater` is a deprecated stub. While it is still functional for transitional purposes, new applications will want to use `SPUStandardUpdaterController` in the above steps instead. + ### 3. Segue for security concerns Since Sparkle is downloading executable code to your users' systems, you must be very careful about security. To let Sparkle know that a downloaded update is not corrupted and came from you (instead of a malicious attacker), we recommend: @@ -59,7 +59,7 @@ Since Sparkle is downloading executable code to your users' systems, you must be * Updates using [Installer package](/documentation/package-updates/) (`.pkg`) *must* be signed with EdDSA. * [Binary Delta updates](/documentation/delta-updates/) *must* be signed with EdDSA. * [Updates of preference panes and plugins](/documentation/bundles/) *must* be signed with EdDSA. - * EdDSA signatures are optional for updates using regular app bundles that are signed with Apple code signing (Apple's Developer ID program), but we still recommended EdDSA signatures as a backup. + * EdDSA signatures are optional for updates using regular app bundles that are signed with Apple code signing (Apple's Developer ID program), but we still recommended EdDSA signatures as a backup. In Sparkle 2.x, not supplying EdDSA signatures will emit a deprecation warning. #### EdDSA (ed25519) signatures @@ -78,8 +78,8 @@ Sparkle before version 1.21 used to use only older DSA signatures, which are now If you are code-signing your application via Apple's Developer ID program, Sparkle will ensure the new version's author matches the old version's. Sparkle also performs basic (but not deep) validation for testing if the new application is archived/distributed correctly as you intended. - * Note that embedding the `Sparkle.framework` into the bundle of a Developer ID application requires that you code-sign the framework with your Developer ID keys. Xcode should do this automatically if you let it "Code Sign on Copy" Sparkle's framework. - * You can diagnose code signing problems with [RB App Checker app](//brockerhoff.net/RB/AppCheckerLite/) and by checking logs in the Console.app. + * Note that embedding the `Sparkle.framework` into the bundle of a Developer ID application requires that you code-sign the framework with your Developer ID keys. Xcode should do this automatically if you create an archive via Product › Archive and Distribute App choosing Developer ID method of distribution. + * You can diagnose code signing problems with `codesign --deep -vvv --verify ` for code signing validity, `spctl -a -t exec -vv ` for Gatekeeper validity, and by checking logs in the Console.app. See [Code Signing in Depth](https://developer.apple.com/library/archive/technotes/tn2206/_index.html) for more code signing details. If you both code-sign your application and include a public EdDSA key for signing your update archive, Sparkle allows issuing a new update that changes either your code signing certificate or your EdDSA keys. Note however this is a last resort and should *only* be done if you lose access to one of them. @@ -87,15 +87,16 @@ If you both code-sign your application and include a public EdDSA key for signin If you distribute your app as a [Apple-certificate-signed disk image](https://developer.apple.com/library/content/technotes/tn2206/_index.html#//apple_ref/doc/uid/DTS40007919-CH1-TNTAG17) (DMG): - * Add an `/Applications` symlink in your DMG, or otherwise encourage the user to copy the app out of it (due to [quarantine/translocation](https://lapcatsoftware.com/articles/app-translocation.html) the app can't be updated when it's launched straight from the disk). + * Add an `/Applications` symlink in your DMG, to encourage the user to copy the app out of it. * Make sure the DMG is signed with a Developer ID and use macOS 10.11.5 or later to sign it (an older OS may not sign correctly). Signed DMG archives are backwards compatible. -If you distribute your app as a ZIP or a tar archive: +If you distribute your app as a ZIP or a tar archive (due to [app translocation](https://lapcatsoftware.com/articles/app-translocation.html)): - * Encourage users to move the app to `/Applications` (e.g. use [LetsMove](https://github.com/potionfactory/LetsMove/)). Due to [quarantine/translocation](https://lapcatsoftware.com/articles/app-translocation.html) the system will not allow the app to update itself until it's moved. * Avoid placing your app inside another folder in your archive, because copying of the folder as a whole doesn't remove the quarantine. * Avoid putting more than just the single app in the archive. +If your app is running from a read-only mount, you can encourage (if you so desire) your user to move the app into /Applications. Some frameworks, although not officially sanctioned, exist for this purpose. Note Sparkle will not by default automatically disturb your user if an update cannot be performed. + Sparkle supports updating from DMG, ZIP archives, tarballs, and installer packages, so you can generally reuse the same archive for distribution of your app on your website as well as Sparkle updates. For Sparkle, tarballs and ZIPs are fastest and most reliable. DMG are slowest. Installer packages should be used only if absolutely necessary (e.g. kernel extensions). diff --git a/documentation/package-updates/index.md b/documentation/package-updates/index.md index 83c34e8..35e1311 100644 --- a/documentation/package-updates/index.md +++ b/documentation/package-updates/index.md @@ -19,4 +19,4 @@ A guided installation can be started by applications other than the application An interactive installation occurs when Sparkle finds a `*.sparkle_interactive.pkg` or `*.sparkle_interactive.mpkg` file in the root of the download archive. -The package will be installed using macOS's built-in GUI installer. The installation will require user to manually click through the steps, so we don't recommend this type of installation. +The package will be installed using macOS's built-in GUI installer. The installation will require user to manually click through the steps, so we don't recommend this type of installation. This type of installation is also deprecated in Sparkle 2.x and may be removed one day. diff --git a/documentation/preferences-ui/index.md b/documentation/preferences-ui/index.md index 0da9a5b..cddf22b 100644 --- a/documentation/preferences-ui/index.md +++ b/documentation/preferences-ui/index.md @@ -5,20 +5,22 @@ title: Adding a Preferences UI --- ## Adding a Preferences UI -### Add the updater to your preferences +### Sparkle 1.x + +#### Add the updater to your preferences * Open up your preferences nib in Interface Builder. * Drag a generic Object (a blue cube) from the Library to your document. * Select this object in your document window, and under the Information tab of the inspector, set the class of the object to `SUUpdater`. This will instantiate your Sparkle updater object. -### Enable automatic checking +#### Enable automatic checking * Drag a check button from the Library to your document. * Set the title to something like "Automatically check for updates". * Select the check button, and under the Bindings tab, select Value. * Check the Bind to: check button, choose `Updater` from the popup, and set the Model Key Path to `automaticallyChecksForUpdates`. -### Update check interval +#### Update check interval * Drag a popup button from the Library to your document. * Set the titles of the menu items to e.g. "Hourly", "Daily", "Weekly", "Monthly". @@ -27,11 +29,11 @@ title: Adding a Preferences UI * Check the Bind to: check button, choose `Updater` from the popup, and set the Model Key Path to `updateCheckInterval`. * Select Enabled, check the Bind to: check button, choose `Updater` from the popup, and set the Model Key Path to `automaticallyChecksForUpdates`. -### Other preferences +#### Other preferences Follow directions similar to [Enable automatic checking](#enable-automatic-checking). to bind a check button to `sendsSystemProfile` or `automaticallyDownloadsUpdates`. See [customization](/documentation/customization/#infoplist-settings) for details on the available keys. -### Preferences for non-app bundles +#### Preferences for non-app bundles These directions do not work for non-app bundles, as the updater you add to the nib will be the `sharedUpdater` for the application bundle. To be able to bind to the updater for your bundle, you can add the following accessor to your preferences controller (the owner of the nib): @@ -41,6 +43,38 @@ These directions do not work for non-app bundles, as the updater you add to the Then just bind the controls to the File's Owner, and start the Model Key Path with updater., e.g. updater.automaticallyChecksForUpdates. +--- + +### Sparkle 2.x (Beta) + +#### Add the updater to your preferences + +* Open up your preferences nib in Interface Builder. +* Drag a generic Object (a blue cube) from the Library to your document. +* Select this object in your document window, and under the Information tab of the inspector, set the class of the object to `SPUStandardUpdaterController`. This will instantiate your Sparkle updater object. + +#### Enable automatic checking + +* Drag a check button from the Library to your document. +* Set the title to something like "Automatically check for updates". +* Select the check button, and under the Bindings tab, select Value. +* Check the Bind to: check button, choose `SPUStandardUpdaterController` from the popup, and set the Model Key Path to `updater.automaticallyChecksForUpdates`. + +#### Update check interval + +* Drag a popup button from the Library to your document. +* Set the titles of the menu items to e.g. "Hourly", "Daily", "Weekly", "Monthly". +* Set the tags of the menu items to the corresponding times in seconds, e.g. 3600, 86400, 604800, 2629800. +* Select the popup button, and under the Bindings tab, select Selected Tag. +* Check the Bind to: check button, choose `SPUStandardUpdaterController` from the popup, and set the Model Key Path to `updater.updateCheckInterval`. +* Select Enabled, check the Bind to: check button, choose `SPUStandardUpdaterController` from the popup, and set the Model Key Path to `updater.automaticallyChecksForUpdates`. + +#### Other preferences + +Follow directions similar to [Enable automatic checking](#enable-automatic-checking). to bind a check button to `updater.sendsSystemProfile` or `updater.automaticallyDownloadsUpdates`. See [customization](/documentation/customization/#infoplist-settings) for details on the available keys. + +--- + ### Watch out for preference caching -macOS caches plist files in `~/Library/Preferences`, so don't edit them directly. If you want to tweak these files for testing (e.g. change last update check date), use [PrefsEditor](http://www.tempel.org/PrefsEditor) or the `defaults` command. +macOS caches plist files in `~/Library/Preferences`, so don't edit them directly. If you want to tweak these files for testing (e.g. change last update check date), use the `defaults` command. diff --git a/documentation/publishing/index.md b/documentation/publishing/index.md index 8aebc8f..1609885 100644 --- a/documentation/publishing/index.md +++ b/documentation/publishing/index.md @@ -68,9 +68,9 @@ If your app is large, or if you're updating primarily only a small part of it, y ## Internal build numbers -If you use internal build numbers for your `CFBundleVersion` key (like an SVN revision number) and a human-readable `CFBundleShortVersionString`, you can make Sparkle hide the internal version from your users. +If you use internal build numbers for your `CFBundleVersion` key and a human-readable `CFBundleShortVersionString`, you can make Sparkle hide the internal version from your users. -Set the `sparkle:version` attribute on your enclosure to the internal, machine-readable version (ie: "1248"). Then set a `sparkle:shortVersionString` attribute on the enclosure to the human-readable version (ie: "12.X Sea Lion"). +Set the `sparkle:version` attribute on your enclosure to the internal, machine-readable version (ie: "1248"). Then set a `sparkle:shortVersionString` attribute on the enclosure to the human-readable version (ie: "1.5.1"). [Remember](//lists.apple.com/archives/carbon-dev/2006/Jun/msg00139.html) that the internal version number (`CFBundleVersion` and `sparkle:version`) is intended to be machine-readable and is not generally suitable for formatted text. @@ -100,6 +100,8 @@ Add a `sparkle:minimumAutoupdateVersion` child to the `` in question speci If this value is set, it indicates the lowest version that can automatically update to the version referenced by the appcast (i.e. without showing the _update available_ GUI). Apps with a lower `CFBundleVersion` will always see the _update available_ GUI, regardless of their `SUAutomaticallyUpdate` user defaults setting. +**Note**: Currently, Sparkle 2.x (Beta) does not support this feature. + ## Embedded release notes Instead of linking external release notes using the `` element, you can also embed the release notes directly in the appcast item, inside a `` element. If you wrap it in ``, you can use unescaped HTML. diff --git a/documentation/sandboxing/index.md b/documentation/sandboxing/index.md new file mode 100644 index 0000000..c4d2e42 --- /dev/null +++ b/documentation/sandboxing/index.md @@ -0,0 +1,59 @@ +--- +layout: documentation +id: documentation +title: Sandboxing with Sparkle +--- + +Note using Sparkle in a sandboxed application is only supported in Sparkle 2.0, which is currently in beta. + +## Sandboxing + +### XPC Services + +In order for Sparkle to work in a sandboxed application, the application must call out to XPC services to perform the updating and installation. Note if you do not sandbox your application, you do not need to use any XPC services and may skip this page. + +In an extracted `Sparkle-2.0.0.tar.xz` distribution in the `XPCServices/` directory you will notice: + +* org.sparkle-project.InstallerConnection.xpc +* org.sparkle-project.InstallerLauncher.xpc +* org.sparkle-project.InstallerStatus.xpc +* org.sparkle-project.Downloader.xpc & org.sparkle-project.Downloader.entitlements + +### Downloader Service + +The last downloader XPC Service is optional. Use it only if your sandboxed application does not request the `com.apple.security.network.client` entitlement. The downloader service allows using Sparkle without forcing the network client entitlement on your entire application. There are a couple caveats with using the downloader service though: + +* It may not work well if your release notes reference external content that would require making additional network requests. +* We fall back to using legacy WebKit view due to a [known WKWebView defect](https://github.com/feedback-assistant/reports/issues/1). + +### Code Signing + +All the other XPC Services are required. You will also need to code sign these services by running: + +``` +./bin/codesign_embedded_executable "Developer ID Application" XPCServices/*.xpc +``` + +I used "Developer ID Application" for my certificate; you may need to adjust this. + +### Adding the Services + +Then you will need to add the XPC Services to your application project: + +* Add the XPC Services to your app target: + * Drag the XPC Services you need into your Xcode project. + * Be sure to check the “Copy items into the destination group’s folder” box in the sheet that appears. + * Make sure the box is checked for your app’s target in the sheet’s Add to targets list +* Make sure the XPC Services are properly copied in your app bundle: + * Click on your project in the Project Navigator. + * Click your target in the project editor. + * Click on the Build Phases tab. + * Remove the XPC Services in the Copy Bundle Resources phase if Xcode auto-added them there. + * Click + to add a new Copy Files Phase. + * Choose XPC Services as the Destination. + * Drag the XPC Services you added from Xcode's project navigator to the new Copy Files Phase. + +### Testing + +Due to the `./bin/codesign_embedded_executable` script signing the XPC Services with the Hardened Runtime enabled, Xcode cannot debug the XPC Services and you may see that updating does not work when your application is attached to Xcode. You can work around this either by editing your project's Scheme and disabling *Debug XPC services used by app*, or by testing your application detached from Xcode, or by altering the script to not sign the services with Hardened Runtime enabled for development builds. + diff --git a/documentation/sparkle-cli/index.md b/documentation/sparkle-cli/index.md new file mode 100644 index 0000000..fd745e8 --- /dev/null +++ b/documentation/sparkle-cli/index.md @@ -0,0 +1,82 @@ +--- +layout: documentation +id: documentation +title: sparkle-cli +--- + +## sparkle-cli + +Sparkle 2.0 includes a command line utility that can update Sparkle-based applications and bundles. + +### Usage + +Check out its usage: + +``` +./sparkle.app/Contents/MacOS/sparkle + +Usage: sparkle.app/Contents/MacOS/sparkle bundle [--application ] [--check-immediately] [--probe] [--grant-automatic-checks] [--send-profile] [--defer-install] [--interactive] [--verbose] +Description: + Check if any new updates for a Sparkle supported bundle need to be installed. + + If any new updates need to be installed, the user application + is terminated and the update is installed immediately unless --defer-install + is specified. If the application was alive, then it will be relaunched after. + + To check if an update is available without installing, use --probe. + + if no updates are available now, or if the last update check was recently + (unless --check-immediately is specified) then nothing is done. + + If update permission is requested and --grant-automatic-checks is not + specified, then checking for updates is aborted. + + Unless --interactive is specified, this tool will not request for escalated + authorization. Running as root is not supported. + + If --defer-install is specified, this tool will exit leaving a spawned process + for finishing the installation after the target application terminates. +Options: + --application + Path to the application to watch for termination and to relaunch. + If not provided, this is assumed to be the same as the bundle. + --check-immediately + Immediately checks for updates to install. + Without this, updates are checked only when needed on a scheduled basis. + --probe + Probe for updates. Check if any updates are available but do not install. + An exit status of 0 is returned if a new update is available. + --feed-url + URL for appcast feed. This URL will be used for the feed instead of the one + in the bundle's Info.plist or in the bundle's user defaults. + --interactive + Allows prompting the user for an authorization dialog prompt if the + installer needs elevated privileges, or allows performing an interactive + installer package. + --grant-automatic-checks + If update permission is requested, this enables automatic update checks. + Note that this behavior may overwrite the user's defaults for the bundle. + This option has no effect if --check-immediately is passed, or if the + user has replied to this request already, or if the developer configured + to skip it. + --send-profile + Choose to send system profile information if update permission is requested. + This option can only take effect if --grant-automatic-checks is passed. + --defer-install + Defer installation until after the application terminates on its own. The + application will not be relaunched unless the installation is resumed later. + --verbose + Enable verbose logging. +``` + +### Example + +For example, I updated an application on my machine I knew was out of date by running: + +``` +./sparkle.app/Contents/MacOS/sparkle --check-immediately /Applications/Hex\ Fiend.app/ +``` + +### Caveats + +There are caveats for updating applications you do not own with sparkle-cli. For example an app may implement Sparkle's delegate methods for using a custom version comparator or feed URL, but sparkle-cli has no way of knowing to use these if they are not extractable externally. diff --git a/documentation/upgrading/index.md b/documentation/upgrading/index.md index cf76394..6d93422 100644 --- a/documentation/upgrading/index.md +++ b/documentation/upgrading/index.md @@ -4,7 +4,38 @@ id: documentation title: Upgrading from previous versions of Sparkle --- -We strongly recommend upgrading Sparkle to the [latest version](//github.com/{{ site.github_username }}/Sparkle/releases), as there have been important fixes in reliability and [security](/documentation/security) of updates. Very old versions of Sparkle also suffer some incompatibilities with the latest macOS versions. +We strongly recommend upgrading Sparkle to the [latest stable version](//github.com/{{ site.github_username }}/Sparkle/releases), as there have been important fixes in reliability and [security](/documentation/security) of updates. Very old versions of Sparkle also suffer some incompatibilities with the latest macOS versions. + +## Upgrading from Sparkle 1.x to 2.x (Beta) + +**Note**: Sparkle 2.x is in a pre-release / beta state and not production ready. + +The `SUUpdater` class has been deprecated and split up in Sparkle 2.x, but it is still functional for transitional purposes. + +Sparkle 2.x includes three new classes / protocols: +* **SPUUpdater** - The main API in Sparkle for controlling the update mechanism. +* **SPUUserDriver** - The API in Sparkle for controlling the user interface & interaction (`SPUStandardUserDriver` is the standard one). +* **SPUStandardUpdaterController** - A controller class that instantiates a `SPUUpdater` using `SPUStandardUserDriver` in a nib and allows binding UI to it. + +If you were previously instantiating a `SUUpdater` in a nib, you will want to adopt `SPUStandardUpdaterController` as shown in the [basic setup](/documentation/). + +If you were previously instantiating a `SUUpdater` in code, you will want to adopt instantiating a `SPUUpdater`. + +The deprecated `SUUpdater` in 2.x is now a stub that uses both a `SPUUpdater` and `SPUStandardUserDriver`. + +If you create a `SPUUpdater` instance programatically, you can now create an updater that can update other Sparkle-based bundles and/or an updater that can use your own `SPUUserDriver` / user interface. [sparkle-cli](/documentation/sparkle-cli) makes use of both features as an example. + +`SPUUpdater` and its delegate `SPUUpdaterDelegate` (unlike `SUUpdater`) does not contain any user-interface or AppKit logic. The UI bits were separated into classes implementing `SPUUserDriver` and its delegates. A developer writing their own updater user interface may choose to use the new `SparkleCore` framework which strips out the UI bits that Sparkle provides out of the box. + +`SPUUpdater` does not maintain singleton or global instances (unlike `SUUpdater`). Plug-ins that share the same process as their host should prefer to use an external tool like [sparkle-cli](/documentation/sparkle-cli) instead, rather than sharing or injecting a Sparkle.framework in its host. A bit more details about updating bundles [here](/documentation/bundles#sparkle-2x-beta). + +If you have scripts that reference Sparkle.framework's helper tools, here are the new paths (note Autoupdate is now a command line tool): +``` +Sparkle.framework/Versions/A/Resources/Autoupdate +Sparkle.framework/Versions/A/Resources/Updater.app/ +``` + +See [Sparkle 2.x's APIs](/documentation/customization#sparkle-2x-apis-beta) for more information. ## Upgrading from Sparkle 1.20 and older diff --git a/documentation/v2/index.md b/documentation/v2/index.md deleted file mode 100644 index e4185c7..0000000 --- a/documentation/v2/index.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -layout: documentation-v2 -id: documentation -title: Documentation for Sparkle 2 ---- - -## Getting Started - -Sparkle 2.x is the upcoming new version of Sparkle with support for sandboxed applications. - -It is currently in beta. - -We're working on the documentation. In the mean time, please have a look at the [README file](https://github.com/sparkle-project/Sparkle/blob/2.x/README.markdown) for details. - - -