PreferenceLoader

From iPhone Development Wiki
Jump to: navigation, search
PreferenceLoader
PrefLoaderScreenshot.png
Cydia Package
Developer Dustin Howett
Package ID preferenceloader
Latest Version 2.2.0-1


PreferenceLoader is an open-source[1] MobileSubstrate based utility that allows developers to add entries to the Settings application, similar to the Settings bundles that AppStore apps use.

Contents

Entry

The approach PreferenceLoader takes is different from other approaches in that the Settings-iPhone.plist and Settings-iPod.plist files are not modified on disk. When the Settings application is loaded, entries are read from plists in /Library/PreferenceLoader/Preferences/ and are dynamically added to the list. Each entry is defined in its own plist and consists of a dictionary containing an element named entry, which is also a dictionary. The entry dictionary must define a cell of type PSLinkCell and have a label. Additionally, the bundle and isController keys can be set for entries referencing PreferenceBundles.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
   <key>entry</key>
   <dict>
      <key>cell</key>
      <string>PSLinkCell</string>
      <key>icon</key>
      <string>TestIcon.png</string>
      <key>label</key>
      <string>Test</string>
   </dict>
</dict>
</plist>

Simple Approach

In the simple approach the settings page is defined in a plist file located in the /Library/PreferenceLoader/Preferences/ folder. The name of the plist file must match the label option in the entry dictionary. For example, if the label in the entry dictionary is defined as Test, then the plist that defines the settings page must be named Test.plist.

Typically, the settings and entry plists are combined into a single plist. This works because the settings plist has elements named items and title, so adding an entry element does not break anything. Here's an example of a combined plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
   <key>entry</key>
   <dict>
      <key>cell</key>
         <string>PSLinkCell</string>
         <key>icon</key>
         <string>TestIcon.png</string>
         <key>label</key>
         <string>Test</string>
      </dict>
      <key>items</key>
      <array>
         <dict>
            <key>cell</key>
            <string>PSSwitchCell</string>
            <key>default</key>
            <true/>
            <key>defaults</key>
            <string>com.test.TestSettings</string>
            <key>key</key>
            <string>testKey</string>
            <key>label</key>
            <string>Test Setting</string>
         </dict>
      </array>
      <key>title</key>
      <string>Test</string>
   </dict>
</plist>

There is a good tutorial on how to create these settings plists on MMi at http://modmyi.com/forums/file-mods/22453-how-make-custom-menus-preferences-app-custom-preferences.html.

Localizable Simple Approach[2]

This approach is similar to the Simple Approach, except the settings and entry plists are located in a sub-folder of /Library/PreferenceLoader/Preferences/. In addition to the plists and icons, the sub-folder can also contain .lproj folders with appropriate localization strings.

PreferenceBundle Approach

With this technique, you can actually create custom settings pages that are able to execute code. The entry plist for this approach must now include a reference to the name of the PreferenceBundle and define the isController option to true. This will cause the Settings application to load the corresponding bundle located in /Library/PreferenceBundles/[2][3]. Unlike the simple method, all of the files except the entry plist are located inside the bundle. The following example shows an entry plist for loading a PreferenceBundle:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
   <key>entry</key>
   <dict>
      <key>bundle</key>
      <string>TestSettings</string>
      <key>cell</key>
      <string>PSLinkCell</string>
      <key>icon</key>
      <string>TestIcon.png</string>
      <key>isController</key>
      <true/>
      <key>label</key>
      <string>Test</string>
   </dict>
</dict>
</plist>

A skeleton PreferenceBundle project can be found at http://www.volatile-dev.com/PreferenceLoader/TestSettings.zip

Filtering[4]

In a way similar to that of MobileSubstrate, developers may add filters to entry plists and specifiers within simple and localized simple preferences. The filter should be a dictionary at the top level of the entry plist, or within a specifier, which can contain these keys:

  • CoreFoundationVersion (array): The entry or specifier is loaded or displayed only if the version of CoreFoundation.framework is above the specified values. Currently, only the first 2 values are checked.
Firmware 2.0 2.1 2.2 3.0 3.1 3.2 4.0 4.1 4.2 4.3 5.0 5.1 6.0 6.1 7.0 7.0.3
SourceCache version 478.23 478.26.1 478.29 478.47.7 478.52 478.61 550.32 550.38.1 550.52 550.58.1  ?
dylib version 478.23 478.26.1 478.29 478.47.7 478.52 478.61 550.32 550.38.1 550.52 550.58.1 675.00 690.10 793.00 847.20 847.21

Below sample specifier will be filter for iOS 6.0 or later.

<dict>
  <key>cell</key>
  <string>PSSwitchCell</string>
  <key>default</key>
  <true/>
  <key>defaults</key>
  <string>com.test.TestSettings</string>
  <key>key</key>
  <string>testKey</string>
  <key>label</key>
  <string>Test Setting</string>
  <key>pl_filter</key>
  <dict>
    <key>CoreFoundationVersion</key>
    <array>
      <string>793.00</string>
    </array>
  </dict>
</dict>

Issues with OS 3.2 and 4.0

PSViewController underwent a massive change after 3.1, breaking all custom subclasses on the iPad and on 4.0 - it is now a UIViewController.

Improper implementations of PSListController subclasses will fail to work properly on 4.0. You must set _specifiers within the -(id) specifiers method (instead of returning a different array of specifiers.) This is because OS 4.0 relies on _specifiers to generate specifier metadata and group indices. Example:

- (id) specifiers {
	if (!_specifiers){
	    _specifiers = [[self loadSpecifiersFromPlistName: kNameOfPreferencesPlist target: self] retain];
	}
 
	return _specifiers;
}

Notes

  1. PreferenceLoader Source
  2. 2.0 2.1 PreferenceLoader version 1.2 and later
  3. Bundles can also be loaded from /System/Library/PreferenceBundles/, but this is no longer the preferred method as of PreferenceLoader 1.2
  4. PreferenceLoader version 2.2.0-1 and later

References