Pro Tip: Add Settings for your iOS Application to the Settings App
By Molly Maskrey
All but the simplest apps you’re likely to use typically include a preferences window where the user sets application-specific options. On macOS, you’ll usually find a Preferences… menu item in the application’s menu. Selecting it brings up a window where the user enters and changes various options. The iPhone and iPad include a dedicated application called Settings that you’ve likely used before. Let’s look at how to add settings for your iOS application to the Settings app.
The Settings application lets the user enter and change preferences for any application that includes a settings bundle. The settings bundle contains a group of files built in to an application that tells the Settings application which preferences the application wishes to collect from the user, as shown in Figure 1. The Settings application acts as a common user interface for the iOS User Defaults mechanism. User Defaults stores and retrieves preferences for the application.
Figure 1. The Settings app on a typical iPhone display
In an iOS application, the NSUserDefaults class provides the user defaults service. If you’ve programmed in Cocoa on macOS, you’re probably already familiar with NSUserDefaults because it’s the same class used to store and read preferences on macOS. Your applications use NSUserDefaults to read and store preference data using pairs of keys and values, just as you would access keyed data from a dictionary, the difference being NSUserDefaults data persists to the file system rather than stored in an object instance in memory.
Because the Settings app provides a standard interface, you don’t need to design your own UI for your app’s preferences. You create a property list describing your application’s available settings, and the Settings app creates the interface for you. Immersive applications, such as games, generally should provide their own preferences view so that the user doesn’t need to quit to make a change. Even utility and productivity applications might, at times, have preferences that a user should be able to change without leaving the application.
iOS allows the user to switch from one application to the Settings app, change a preference, and then switch back to your still-running application.
The Settings application uses the contents of each application’s settings bundle to construct a settings view for that application. If an application has no settings bundle, then the Settings app doesn’t show anything for it. Each settings bundle must contain a property list called Root.plist that defines the root-level preferences view. This property list must follow a very precise format.
When the Settings application starts up, it checks each application for a settings bundle and adds a settings group for each application that includes a settings bundle. If we want our preferences to include any subviews, we need to add property lists to the bundle and add an entry to Root.plist for each child view.
In the Project Navigator, click the folder for your project. Our sample project is called “Bridge Control.” Select File ➤ New ➤ File… or press ⌘N. In the left pane, select Resource under the iOS heading, and then select the Settings Bundle icon (see Figure 2). Click the Next button, leave the default name of Settings.bundle, and click Create.
Figure 2. Adding a Settings Bundle to our project
You should now see a new item in the project window called Settings.bundle. Expand the Settings.bundle item. You should see two subitems : a folder named en.lproj, containing a file named Root.strings, and another named Root.plist. We’ll discuss en.lproj later when we talk about localizing an application into other languages. Here, we’ll concentrate on Root.plist.
Select Root.plist and take a look at the editor pane. You’re looking at the Xcode property list editor, as shown in Figure 3.
Figure 3. Root.plist in the property list editor pane . If your editing pane looks slightly different, don’t panic. Simply Control-click in the editing pane and select Show Raw Keys/Values from the contextual menu that appears
Notice the organization of the items in the property list. Property lists are essentially dictionaries, storing item types and values and using a key to retrieve them, just as a Dictionary does. Several different types of nodes can be put into a property list. The Boolean, Data, Date, Number, and String node types are meant to hold individual pieces of data, but you also have a couple of ways to deal with whole collections of nodes, as well. In addition to Dictionary node types, which allow you to store other dictionaries, there are Array nodes, which store an ordered list of other nodes similar to an array. The Dictionary and Array types are the only property list node types that can contain other nodes.
In the Root.plist editor pane, names of keys can either be displayed in their true, “raw” form or in a slightly more human-readable form. We’re big fans of seeing things as they truly are whenever possible, so right-click anywhere in the editor and make sure that the Show Raw Keys/Values option in the contextual menu is checked, as shown in Figure 4. The rest of our discussion here uses the real names for all the keys we’re going to talk about, so this step is important.
Figure 4. Right-click or Control-click anywhere in the property list editing pane and make sure the Show Raw Keys/Values item is checked. This will ensure that real names are used in the property list editor, which makes your editing experience more precise.
Click the disclosure triangle to the left of PreferenceSpecifiers to expand that node. You’ll notice that Xcode’s template kindly gave us four child nodes, as shown in Figure 5. Those nodes don’t reflect the preferences that we need in this example, so delete Item 1, Item 2, and Item 3 (select each one and press the Delete key, one after another), leaving just Item 0 in place.
Figure 5. Root.plist in the editor pane, this time with PreferenceSpecifiers expanded
Single-click Item 0 but don’t expand it. The Xcode property list editor lets you add rows simply by pressing the Return key . The current selection state—including which row is selected and whether it’s expanded—determines where the new row will be inserted. When an unexpanded array or dictionary is selected, pressing Return adds a sibling node after the selected row. In other words, it will add another node at the same level as the current selection. If you were to press Return (but don’t do that now), you would get a new row called Item 1 immediately after Item 0. Figure 6 shows an example of hitting Return to create a new row. Notice the drop-down menu that allows you to specify the kind of preference specifier this item represents.
Figure 6. We selected Item 0 and hit Return to create a new sibling row. Note the drop-down menu that appears, allowing us to specify the kind of preference specifier this item represents
Now expand Item 0 and see what it contains, as shown in Figure 6. The editor is now ready to add child nodes to the selected item. If you were to press Return at this point (again, don’t actually press it now), you would get a new first child row inside Item 0.
Figure 7. When you expand Item 0, you’ll find a row with a key of Type and a second row with a key of Title. This represents a group with a title of Group
One of the items inside Item 0 has a key of Type. Every property list node in the PreferenceSpecifiers array must have an entry with this key. The Type key tells the Settings application what type of data is associated with this item. In Item 0, the Type item has a value of PSGroupSpecifier. This indicates that the item represents the start of a new group. Each item that follows will be part of this group—until the next item with a Type of PSGroupSpecifier. If you look, you’ll see that the Settings application presents the application settings in a grouped table. Item 0 in the PreferenceSpecifiers array in a settings bundle property list should always be a PSGroupSpecifier, so that the settings start in a new group. This is important because you need at least one group in every Settings table.
The only other entry in Item 0 has a key of Title, and this is used to set an optional header just above the group that is being started. Now take a closer look at the Item 0 row itself and you’ll see that it’s actually shown as Item 0 (Group – Group). The values in parentheses represent the value of the Type item (the first Group) and the Title item (the second Group). This is a nice shortcut that Xcode gives you so that you can visually scan the contents of a settings bundle.
We called our first group General Info. Double-click the value next to Title, and change it from Group to General Info. When you enter the new title, you may notice a slight change to Item 0. It’s now shown as Item 0 (Group – General Info) to reflect the new title. In the Settings application, the title is shown in uppercase, so the user will actually see GENERAL INFO instead.
Figure 8. We changed the title of the Item 0 group from Group to General Info
We now need to add a second item in this array, which will represent the first actual preference field. We’re going to start with a simple text field. If you were to single-click the PreferenceSpecifiers row in the editor pane (don’t do this, just keep reading) and press Return to add a child, the new row would be inserted at the beginning of the list, which is not what we want. We want to add a row at the end of the array.
To add the row, click the disclosure triangle to the left of Item 0 to close it, and then select Item 0 and press Return. This gives you a new sibling row after the current row, as shown in Figure 8. As usual, when the item is added, a drop-down menu appears, showing the default value of Text Field.
Figure 9. Adding a new sibling row to Item 0
Click somewhere outside the drop-down menu to make it go away, and then click the disclosure triangle next to Item 1 to expand it. You’ll see that it contains a Type row set to PSTextFieldSpecifier . This is the Type value used to tell the Settings application that we want the user to edit this setting in a text field. It also contains two empty rows for Title and Key, as shown in Figure 9.
Figure 10. Our text field item, expanded to show the type, title, and key
Select the Title row, and then double-click in the whitespace of the Value column. Type in Commanding Officer to set the Title value. This is the text that will appear in the Settings app.
Now do the same for the Key row (no, that’s not a misprint, you’re really looking at a key called Key ). In the value field, type officer (note the lowercase first letter). which key to use when it stores the value entered in this text field.
NSUserDefaults lets you store values using a key, similar to a Dictionary. And the Settings application does the same thing for each of the preferences it saves on your behalf. If you give it a key value of foo, then later in your application, you can request the value for foo, and it will give you the value the user entered for that preference. We will use key value officer later to retrieve this setting from the user defaults in our application.
Now select the last of the three Item 1 rows (the one with a key of Key) and press Return to add another entry to the Item 1 dictionary, giving this one a key of AutocapitalizationType. Note that, as soon as you start typing AutocapitalizationType, Xcode presents you with a list of matching choices, so you can simply pick one from the list instead of typing the whole name. After you’ve entered AutocapitalizationType, press the Tab key or click the small up/down arrow icon on the right of the Value column to open a list where you can select from the available options. Choose Words. This specifies that the text field should automatically capitalize each word that the user types in this field.
Create one last new row and give it a key of AutocorrectionType and a value of No. This will tell the Settings application not to autocorrect values entered into this text field. In any situation where you do want the text field to use autocorrection, you would set the value in this row to Yes. Again, Xcode presents you with a list of matching choices as you begin entering AutocorrectionType. It shows you a list of valid options in a pop-up.
When you’re finished, your property list should look like the one shown in Figure 10.
Figure 11. The finished text field specified in Root.plist
This gives you a general introduction to working with the Settings application mechanism. You can explore the topic further in Beginning iPhone Development with Swift 3 available from Apress.
About the Author
Molly K. Maskrey first learned software while a sophomore in high school on a Wang punch card computer where you manually created an octal machine language program by popping out chads on a single card. While getting her undergraduate degree, she programmed COBOL on IBM System/360 computers at banks in and around Tampa, Florida, moving on, in her 20s and 30s to work for various large Aerospace companies including IBM Federal Systems, TRW (now Northrup-Grumman), Loral Systems, Lockheed-Martin, and Boeing. In 2010 she co-founded Global Tek Labs, an iOS development and accessory design services company that is now one of the leading consulting services for new designers looking to create smart attachments to Apple devices. Molly lives in Parker Colorado with Jennifer, her partner of 26 years and their two Yellow Labradors and basement dance practice floor.
This article is excerpted from Beginning iPhone Development with Swift 3: Exploring the iOS SDK by Molly Maskrey, ISBN 978-1-48422-222-5.