Home iOS & Swift Books Catalyst by Tutorials

7
Preferences & Settings Bundle Written by Andy Pereira

Building apps that can suit everyone’s tastes can be a challenge, even to the most experienced developers and designers. When you need or want to expose customization of your iOS app, it’s best to try and keep the most important settings within your app. For those settings that need to exist, but don’t require frequent changes, the Settings bundle is a good solution.

The Human Interface Guidelines (https://apple.co/2HY5vtf) caution against putting frequently used settings within the iOS Settings app. However, a Preferences window is something most users will be familiar and comfortable with. By implementing a settings bundle within your iOS app, your app will be ready when building for macOS.

Getting started

To begin, open the starter project for this chapter. Select any iPad for the active scheme, then build and run. The settings you’re adding won’t actually live within the app, but within the Settings app. Press the Home button on the simulator, and open Settings. Right now, you’ll see only the default settings.

You’re going to add a few preferences to this app:

  • Currently, when the app runs with the system in dark mode, it will make the text view dark. Sometimes, users might prefer the area they type or read to be lighter. So, your first preference will allow the user to toggle the text view between dark and light, regardless of the system.
  • Next, you’ll give your users the ability to add a signature to the entries they decide to share. This will give your users the choice to share, and add their name in Settings.

Adding the settings bundle

To add your app and its settings to the system Settings app, in your project, select the Journalyst group, and then File ▸ New ▸ File…. Under the Resource header, select Settings Bundle.

Select Next then Create. You will now have Settings.bundle in your app’s files. There are two files that are created by default for you:

  1. Root.strings: If you want to localize the strings in your settings, the localizations will need to live here. You don’t have any access to your app’s code within Settings, so this allows you to still support any translations your app does.
  2. Root.plist: This is where your settings will live.

Build and run, and switch back to Settings. You’ll now see your app is available, with some default preferences.

Back in Xcode, open Root.plist. Then, expand the entries so you can see all of the items under Preference Items. Each one corresponds to what you saw in Settings. Delete each item under Preference Items by highlighting one item at a time and selecting Edit ▸ Delete.

Note: The plist editor won’t let you select multiple items at a time.

Select Preference Items, and then from the menu, select Editor ▸ Add Item. Repeat this two more times, reselecting Preference Items each time. Because the plist editor can be finicky when attempting to rearrange items, it’ll be easier to add all three right now. Expand Item 0, and use the toggle arrows at the far right for Type and change it to Toggle Switch:

Set the other values to the following:

  • Title: Include Signature When Sharing
  • Identifier: signature_preference

Although you’ve added a “toggle switch” in the context of settings, the user will see a familiar UISwitch. The title you entered will be displayed to the left of the toggle.

In both iOS and macOS, your app’s preferences or settings can be a property stored within the user defaults. Whenever the toggle value is changed, it will use Identifier to know what key to save the value to in the user defaults.

With Item 0 highlighted, select Editor ▸ Add Item. Set the newly added key and value to the following:

  • Key: Default Value
  • Value: NO

This will give your user default an initial value, allowing the system to know what to set the toggle to initially.

Next, set the following values for Item 1 in Preferences Items:

  • Type: Text Field
  • Title: Name When Sharing
  • Identifier: name_preference
  • Keyboard Type: Alphabet
  • Autocapitalization Style: Words
  • Autocorrection Style: No Autocorrection

This will add a text field that the user can enter their name in.

And finally add the following items for Item 2:

  • Type: Toggle Switch
  • Title: Show Entry With Light Background
  • Identifier: entry_color_preference
  • Default Value: NO

This setting will allow the user to decide whether or not they want the text view to always be a light color, regardless of whether the system is in dark mode or not.

Build and run, go to Settings, and you’ll now see all of your new items.

Responding to change

You won’t be able to actually see any changes if you don’t listen for changes to user defaults. Open EntryTableViewController.swift, and add the following to the end of viewDidLoad():

UserDefaults.standard
.addObserver(self,
             forKeyPath: colorPreference,
             options: .new,
             context: nil)

Apple states that changes to a setting should have an immediate effect on the app, so you want to observe when the user defaults change. Here, you’ve set your view controller to observe when user defaults changes the key entry_color_preference.

Next, add the following methods to EntryTableViewController:

override func observeValue(forKeyPath keyPath: String?,
                           of object: Any?,
                           change: [NSKeyValueChangeKey : Any]?,
                           context: UnsafeMutableRawPointer?) {
  if keyPath == colorPreference {
    updateEntryCellColor()
  }
}

private func updateEntryCellColor() {
  let overrideColorPreference = UserDefaults
    .standard.bool(forKey: colorPreference)
  if overrideColorPreference {
    entryCell.contentView.backgroundColor = .white
    textView.textColor = .black
  } else {
    entryCell.contentView.backgroundColor = nil
    textView.textColor = .label
  }
}

The first method you added will be called when a user changes the toggle for Show Entry With Light Background.

The second method checks if the toggle value is true. If so, it overrides the text color of the text view to be black, and the background color of the cell to be white. If the value is false, it will use the default label and cell background colors.

Build and run. Then still in Xcode, use Environment Overrides (Debug ▸ View Debugging ▸ Configure Environment Overrides) to change Interface Style to Dark.

Now on the iPad, you can go back to Settings and set the toggle for Show Entry With Light Background to true. When you return to the app, your text area should appear white. Try changing the interface style toggle back and forth to see how it changes properly.

Next, back in Xcode, and the following property to EntryTableViewController.swift:

private var shareText: String? {
  guard var textToShare = textView.text,
        !textToShare.isEmpty else { return nil }
  if let namePreference
      = UserDefaults.standard.string(forKey: namePreference),
     UserDefaults.standard.bool(forKey: signaturePreference) {
    textToShare += "\n-\(namePreference)"
  }
  return textToShare
}

This will create the string that will be shared. If the user has enabled Include Signature When Sharing in Settings, the app will append their signature to the text of any entry shared.

Next, replace share(_:) with the following:

@IBAction private func share(_ sender: Any?) {
  guard let shareText = shareText else { return }
  let activityController
    = UIActivityViewController(activityItems: [shareText],
                               applicationActivities: nil)
  if let popoverController
      = activityController.popoverPresentationController {
    popoverController.barButtonItem
      = navigationItem.rightBarButtonItem
  }
  present(activityController,
          animated: true,
          completion: nil)
}

This method will now use the new text that will be generated by shareText.

Build and run. Go to Settings and turn on Include Signature When Sharing, and enter your name in Name When Sharing. Go back to Journalyst, and enter some text in the entry, then select Action. You’ll be able to see in the preview that your text now includes your name at the end.

Child panes

Settings.app can do more than just show a simple list of items. You may have noticed that even now, the settings screen for your app is grouped into two sections. You can also add groups and even multiple pages. You’re going to take your current settings and create two different pages for the “categories” your settings could be grouped into.

In Xcode, select File ▸ New ▸ File…, pick Property List and name it Sharing. Drag the file into Settings.bundle:

Open Sharing.plist, and add the following item:

  • key: StringsTable
  • value: Root

Note: In the property list, you can right-click on Root, then select Property List Type ▸ iPhone Settings Plist. This will let Xcode know that it should show you the appropriate keys and options for a settings bundle. However, it currently will not save the Property List Type after you switch files.

Add another item:

  • key: PreferenceSpecifiers
  • type: Array

Click the arrow to the left of the new entry you created and add an array element to it using the plus sign to the right.

Note: When editing a plist, you can use the plus sign to quickly add new items. If you click “plus” next to an item that can’t have children, like a string, it will create a sibling entry. If you click “plus” next to an item that can have children, it behaves differently depending on whether the current item is “expanded” or “collapsed”. When the arrow to the left of the item is pointing right, the item is “collapsed” and the new entry will be a sibling. When the arrow to the left of the item is pointing down, the item is “expanded” and the new entry will be a child.

Change the type of your new entry to Dictionary and add the following children:

  • Type: PSGroupSpecifier
  • Title: Sharing

This will treat any following items in the list as part of a grouped table view section, with a table view header that says “Sharing”.

Next, open Root.plist, and highlight Item 0, then select Edit ▸ Cut. Open Sharing.plist, collapse and highlight Item 0, and select Edit ▸ Paste.

Select the new Item 0 from Root.plist, select Edit ▸ Cut, collapse Item 1 in Sharing.plist, and select Edit ▸ Paste.

Now, Sharing.plist should look like the following:

Last, back in Root.plist, add a new dictionary entry to the end of Preference Items. Add the following entries to the dictionary:

  • Type: Child Pane
  • Title: Sharing
  • Filename: Sharing

A Child Pane is an option that allows you to specify the name of another plist within Settings.bundle to navigate to. The Filename value must match the file name that has the settings you want to display, without the extension.

To organize everything more clearly, add two groups to Root.plist. In Root.plist, select Preference Items and then Editor ▸ Add Item. Set the following entries on the new dictionary:

  • Type: Group
  • Title: Viewing

Next, collapse and select Item 1 and then choose Editor ▸ Add Item. Change dictionary to have only the following entry: Type: Group

Root.plist should look like the following:

Build and run, then go to Settings. You should now see two entries at the bottom of the settings list. You’ll also see how the groups break out the settings more clearly:

When you select the “Sharing” setting, you’ll navigate to another screen where you’ll see the specific settings from the corresponding file:

Key Points

  • Settings and preferences are helpful for your users.
  • Keeping your app’s preferences in Settings.plist quickly provides a standard way for your users to find available customizations.
  • Using multiple plists bring hierarchy and organization to your preferences.

Where to go from here?

You can find out about all the other controls available to you in a settings bundle from Apple’s Preferences and Settings Programming Guide. https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/UserDefaults/Preferences/Preferences.html.

You can also learn about Apple’s guidelines for settings in the Human Interface Guidelines. https://developer.apple.com/design/human-interface-guidelines/ios/app-architecture/settings/

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2020 Razeware LLC