mirror of
https://github.com/sparkle-project/sparkle-project.github.io.git
synced 2025-11-01 15:35:08 +00:00
Revamp and improve SwiftUI programmatic samples (#168)
We provide a more sound SwiftUI sample and also show how to add user settings to a SwiftUI app. Also rename Preferences to Settings in across the documentation.
This commit is contained in:
@@ -23,7 +23,7 @@ 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. |
|
||||
| `SUEnableJavaScript` | Boolean | Default: `NO`. Set this to `YES` if you want to allow JavaScript in your release notes. |
|
||||
| `SURelaunchHostBundle` | Boolean | Default: `NO`. For plug-ins in Sparkle 2, set this to `YES` to re-launch the host targetted bundle instead of the application bundle. For example, this can be used to re-open a System Preferences prefpane after an update has been installed.
|
||||
| `SURelaunchHostBundle` | Boolean | Default: `NO`. For plug-ins in Sparkle 2, set this to `YES` to re-launch the host targetted bundle instead of the application bundle. For example, this can be used to re-open a System Settings prefpane after an update has been installed.
|
||||
|
||||
### Sandboxing Settings
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ That's it! You're done! You don't have to do any more. But you might want to:
|
||||
|
||||
* [Read more on publishing an update](/documentation/publishing/#publishing-an-update)
|
||||
* [Customize Sparkle's settings and behavior](/documentation/customization/) for your product.
|
||||
* [Add update settings](/documentation/preferences-ui/) to your preferences panel.
|
||||
* [Add update settings](/documentation/preferences-ui/) to your settings panel.
|
||||
* [Add binary delta updates](/documentation/delta-updates/) to your application.
|
||||
* [Add gentle update reminders](/documentation/gentle-reminders) for your application.
|
||||
* [Learn about gathering anonymous statistics about your users' systems](/documentation/system-profiling/).
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
---
|
||||
layout: documentation
|
||||
id: documentation
|
||||
title: Adding a Preferences UI
|
||||
title: Adding a Settings UI
|
||||
---
|
||||
## Adding a Preferences UI
|
||||
|
||||
### Sparkle 2
|
||||
## Sparkle 2
|
||||
|
||||
These instructions below are for adding preferences in a Cocoa nib and assume you have a Preferences nib with its `File's Owner` being set to your Preferences controller class (e.g, a `NSWindowController` subclass). The controller class should have an `updater` property set to your application's `SPUUpdater`, which may be passed from your application's delegate.
|
||||
### Adding a Settings UI in Cocoa
|
||||
|
||||
These instructions below are for adding settings in a Cocoa nib and assume you have a Settings nib with its `File's Owner` being set to your Settings controller class (e.g, a `NSWindowController` subclass). The controller class should have an `updater` property set to your application's `SPUUpdater`, which may be passed from your application's delegate.
|
||||
|
||||
#### Enable automatic checking
|
||||
|
||||
@@ -25,24 +26,75 @@ These instructions below are for adding preferences in a Cocoa nib and assume yo
|
||||
* Check the Bind to: check button, choose `File's Owner` from the popup, and set the Model Key Path to `updater.updateCheckInterval`.
|
||||
* Select Enabled, check the Bind to: check button, choose `File's Owner` from the popup, and set the Model Key Path to `updater.automaticallyChecksForUpdates`.
|
||||
|
||||
#### Other preferences
|
||||
#### Other settings
|
||||
|
||||
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.
|
||||
|
||||
### Adding Settings in SwiftUI
|
||||
|
||||
This is a continuation from [Creating an Updater in SwiftUI](/documentation/programmatic-setup#create-an-updater-in-swiftui).
|
||||
|
||||
Create a new view for the updater settings:
|
||||
|
||||
```swift
|
||||
// This is the view for our updater settings
|
||||
// It manages local state for checking for updates and automatically downloading updates
|
||||
// Upon user changes to these, the updater's properties are set. These are backed by NSUserDefaults.
|
||||
// Note the updater properties should *only* be set when the user changes the state.
|
||||
struct UpdaterSettingsView: View {
|
||||
private let updater: SPUUpdater
|
||||
|
||||
@State private var automaticallyChecksForUpdates: Bool
|
||||
@State private var automaticallyDownloadsUpdates: Bool
|
||||
|
||||
init(updater: SPUUpdater) {
|
||||
self.updater = updater
|
||||
self.automaticallyChecksForUpdates = updater.automaticallyChecksForUpdates
|
||||
self.automaticallyDownloadsUpdates = updater.automaticallyDownloadsUpdates
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Toggle("Automatically check for updates", isOn: $automaticallyChecksForUpdates)
|
||||
.onChange(of: automaticallyChecksForUpdates) { newValue in
|
||||
updater.automaticallyChecksForUpdates = newValue
|
||||
}
|
||||
|
||||
Toggle("Automatically download updates", isOn: $automaticallyDownloadsUpdates)
|
||||
.disabled(!automaticallyChecksForUpdates)
|
||||
.onChange(of: automaticallyDownloadsUpdates) { newValue in
|
||||
updater.automaticallyDownloadsUpdates = newValue
|
||||
}
|
||||
}.padding()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then add these settings to your `App`'s `body`:
|
||||
|
||||
```swift
|
||||
var body: some Scene {
|
||||
/* ... */
|
||||
Settings {
|
||||
UpdaterSettingsView(updater: updaterController.updater)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Sparkle 1
|
||||
## Sparkle 1
|
||||
|
||||
These instructions below are for adding preferences in a Cocoa nib and assume you have a Preferences nib with its `File's Owner` being set to your Preferences controller class (e.g, a `NSWindowController` subclass). The controller class should have an `updater` property set to your application's `SUUpdater`, which may be passed from your application's delegate.
|
||||
These instructions below are for adding settings in a Cocoa nib and assume you have a Settings nib with its `File's Owner` being set to your Settings controller class (e.g, a `NSWindowController` subclass). The controller class should have an `updater` property set to your application's `SUUpdater`, which may be passed from your application's delegate.
|
||||
|
||||
#### 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 `File's Owner` from the popup, and set the Model Key Path to `updater.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".
|
||||
@@ -51,13 +103,13 @@ These instructions below are for adding preferences in a Cocoa nib and assume yo
|
||||
* Check the Bind to: check button, choose `File's Owner` from the popup, and set the Model Key Path to `updater.updateCheckInterval`.
|
||||
* Select Enabled, check the Bind to: check button, choose `File's Owner` from the popup, and set the Model Key Path to `updater.automaticallyChecksForUpdates`.
|
||||
|
||||
#### Other preferences
|
||||
### Other settings
|
||||
|
||||
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
|
||||
### Settings 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):
|
||||
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 settings controller (the owner of the nib):
|
||||
|
||||
```objc
|
||||
- (SUUpdater *)updater {
|
||||
@@ -69,6 +121,6 @@ Then just bind the controls to the File's Owner, and start the Model Key Path wi
|
||||
|
||||
---
|
||||
|
||||
### Watch out for preference caching
|
||||
### 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 the `defaults` command.
|
||||
|
||||
@@ -35,6 +35,8 @@ import Sparkle
|
||||
}
|
||||
```
|
||||
|
||||
For adding additional updater settings, you may also want to check out [Adding a Settings UI in Cocoa](/documentation/preferences-ui#add-a-settings-ui-in-cocoa).
|
||||
|
||||
## Create an Updater in SwiftUI
|
||||
|
||||
This is an example of creating an updater in Sparkle 2 with SwiftUI. It creates a new menu item allowing users to check for new updates and ensures its disabled state is updated.
|
||||
@@ -43,53 +45,60 @@ This is an example of creating an updater in Sparkle 2 with SwiftUI. It creates
|
||||
import SwiftUI
|
||||
import Sparkle
|
||||
|
||||
// This view model class manages Sparkle's updater and publishes when new updates are allowed to be checked
|
||||
final class UpdaterViewModel: ObservableObject {
|
||||
private let updaterController: SPUStandardUpdaterController
|
||||
|
||||
// This view model class publishes when new updates can be checked by the user
|
||||
final class CheckForUpdatesViewModel: ObservableObject {
|
||||
@Published var canCheckForUpdates = false
|
||||
|
||||
init() {
|
||||
// If you want to start the updater manually, pass false to startingUpdater and call .startUpdater() later
|
||||
// This is where you can also pass an updater delegate if you need one
|
||||
updaterController = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil)
|
||||
|
||||
updaterController.updater.publisher(for: \.canCheckForUpdates)
|
||||
|
||||
init(updater: SPUUpdater) {
|
||||
updater.publisher(for: \.canCheckForUpdates)
|
||||
.assign(to: &$canCheckForUpdates)
|
||||
}
|
||||
|
||||
func checkForUpdates() {
|
||||
updaterController.checkForUpdates(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// This additional view is needed for the disabled state on the menu item to work properly before Monterey.
|
||||
// See https://stackoverflow.com/questions/68553092/menu-not-updating-swiftui-bug for more information
|
||||
// This is the view for the Check for Updates menu item
|
||||
// Note this intermediate view is necessary for the disabled state on the menu item to work properly before Monterey.
|
||||
// See https://stackoverflow.com/questions/68553092/menu-not-updating-swiftui-bug for more info
|
||||
struct CheckForUpdatesView: View {
|
||||
@ObservedObject var updaterViewModel: UpdaterViewModel
|
||||
@ObservedObject private var checkForUpdatesViewModel: CheckForUpdatesViewModel
|
||||
private let updater: SPUUpdater
|
||||
|
||||
init(updater: SPUUpdater) {
|
||||
self.updater = updater
|
||||
|
||||
// Create our view model for our CheckForUpdatesView
|
||||
self.checkForUpdatesViewModel = CheckForUpdatesViewModel(updater: updater)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Button("Check for Updates…", action: updaterViewModel.checkForUpdates)
|
||||
.disabled(!updaterViewModel.canCheckForUpdates)
|
||||
Button("Check for Updates…", action: updater.checkForUpdates)
|
||||
.disabled(!checkForUpdatesViewModel.canCheckForUpdates)
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
struct MyApp: App {
|
||||
@StateObject var updaterViewModel = UpdaterViewModel()
|
||||
private let updaterController: SPUStandardUpdaterController
|
||||
|
||||
init() {
|
||||
// If you want to start the updater manually, pass false to startingUpdater and call .startUpdater() later
|
||||
// This is where you can also pass an updater delegate if you need one
|
||||
updaterController = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil)
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
}
|
||||
.commands {
|
||||
CommandGroup(after: .appInfo) {
|
||||
CheckForUpdatesView(updaterViewModel: updaterViewModel)
|
||||
CheckForUpdatesView(updater: updaterController.updater)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For adding additional updater settings, you may also want to check out [Add Settings in SwiftUI](/documentation/preferences-ui#add-settings-in-swiftui).
|
||||
|
||||
## Create an Updater in Mac Catalyst
|
||||
|
||||
This is an example for creating an updater in Sparkle 2 with Mac Catalyst. The code illustrated below is inside an AppKit bundle plug-in that is loaded by the Catalyst application using a shared `PlugIn` protocol. This snippet is missing plenty of details including adding a menu item and inter-opt between the host application and plug-in.
|
||||
@@ -275,7 +284,7 @@ This section only applies if you plan to use additional updater APIs that are no
|
||||
|
||||
Prefer to set the updater's initial properties in your bundle's Info.plist as described in [Customizing Sparkle](/documentation/customization/). This includes properties like the updater's feed URL and its update checking behavior. Only configuring the feed URL and signing keys (via `SUFeedURL` and `SUPublicEDKey`) in your bundle's Info.plist is strongly recommended, which are described in [later documentation sections](/documentation/#3-segue-for-security-concerns).
|
||||
|
||||
Only set [SPUUpdater](/documentation/api-reference/Classes/SPUUpdater.html) (or `SUUpdater` for Sparkle 1) properties, related to update checking, programatically when the user wants to make updater setting changes, otherwise you may be ignoring the user's preferences and resetting the updater's cycle unnecessarily. Similarly, developers shouldn't maintain their own set of user defaults on top of [SPUUpdater](/documentation/api-reference/Classes/SPUUpdater.html) (or `SUUpdater`) properties that are documented to already be backed by `NSUserDefaults` from user setting changes.
|
||||
Only set [SPUUpdater](/documentation/api-reference/Classes/SPUUpdater.html) (or `SUUpdater` for Sparkle 1) properties, related to update checking, programatically when the user wants to make updater setting changes, otherwise you may be ignoring the user's preferences and resetting the updater's cycle unnecessarily. Similarly, developers shouldn't maintain their own set of user defaults on top of [SPUUpdater](/documentation/api-reference/Classes/SPUUpdater.html) (or `SUUpdater`) properties that are documented to already be backed by `NSUserDefaults` from user setting changes. Please also refer to [Adding a Settings UI](/documentation/preferences-ui) for examples.
|
||||
|
||||
If you want to switch to an alternative feed at runtime, please check our section on [setting the feed programmatically](/documentation/publishing#setting-the-feed-programmatically) first on the recommended API usage (which is not through `-setFeedURL:`).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user