TweetFollow Us on Twitter

Building a System Preference Pane

Volume Number: 19 (2003)
Issue Number: 11
Column Tag: Programming

Mac OS X Programming Secrets

Building a System Preference Pane

by Scott Knaster

Most applications include a Preference item in their application menu that lets users tweak the way things work. In fact, the very program I'm using to type these words includes a vast set of preferences with over 100 settings divided into ten categories. Even the Finder includes its own preferences.

Sometimes, little bits of software need to have preferences, but are not substantial enough to easily provide a user interface for them. These can include parts of the system, such as the Dock or the built-in Screen Effects, or cool third-party utilities. In Mac OS 9, users often tweaked these settings in control panels. As with so many things, the preferences world has changed radically since those bygone days. Mac OS X provides a System Preferences application that includes preference panes for these assorted but important system settings.


Figure 1. System Preferences window.

OS X comes with a bunch of preference panes used for system settings, as you can see in Figure 1. But in addition, System Preferences (since OS X 10.1, anyway) provides a plug-in framework that you can use to create you own preference panes that appear in the System Preferences window right alongside the system's built-in panes.

For this month's column, I'm borrowing code and the concept from Joe Zobkiw's cool new book, Mac OS X Advanced Development Techniques. This book provides real code and information about a nifty collection of OS X topics, including applications, system services, multiple threads, and more. If you want to read a fun, well-written book that will broaden your knowledge of OS X programming topics, check it out.

They Thought of This

For our sample in this column, we'll create a simple preference pane called BigPane that has a checkbox and a text field. To get started, crank up Project Builder and choose New Project from the File menu. When you get the New Project Assistant, take a look under the Standard Apple Plug-ins section. Hey, there's a project type called PreferencePane! By choosing that one, we get a project that's already hooked up to the preference pane framework. We'll go ahead and create our new preference pane project.


Figure 2. New, empty preference pane project..

Once the project has been created, we can move forward with the primary task of writing the code. The empty project declares BigPanePref, a subclass of NSPreferencePane. The listing follows:

@interface BigPanePref : NSPreferencePane 
{
   CFStringRef m_appID;   // our application ID
   
   IBOutlet   NSButton   *m_checkBox;
   IBOutlet   NSTextField   *m_textField;
}
- (IBAction)checkboxClicked:(id)sender;
@end

Here we're simply declaring the class, including a string to hold the application ID so we can find our preferences file, references to the controls that are included in the pane, and the checkBoxClicked action that we'll implement.

First Things First

Getting into the actual code, the first thing we'll deal with is overriding initWithBundle, which is called when the preference pane starts up.

#import "MyPreferencePanePref.h"
@implementation MyPreferencePanePref
- (id)initWithBundle:(NSBundle *)bundle
// System Preferences calls this method when 
// the pane is initialized.
{
   // Initialize the location of our preferences
    if ((self = [super initWithBundle:bundle]) != nil) {
        m_appID = CFSTR("com.knaster.bigpane");
    }
    
    return self;
}

This override gets us the appID, which we'll use to locate the file that preferences are stored in. You should use a filename that is unlikely to be the same as any other file, and is named after the application or service whose preferences it contains. Also, because this is an init method, Apple says we should be good citizens and call the inherited implementation, which we do.

Once the view is created and the nib file is loaded, our mainViewDidLoad method will be called. This is where we get a chance to initialize the preference controls with their saved values:

- (void)mainViewDidLoad
{
    CFPropertyListRef value;
      // Get the value of the checkbox from the saved prefs
      // (if any)
    value = CFPreferencesCopyAppValue
(CFSTR("Boolean Value Key"), m_appID);
    if (value && CFGetTypeID(value) == CFBooleanGetTypeID()) {
        [m_checkBox setState:CFBooleanGetValue(value)];
    } else {
        [m_checkBox setState:NO];         
      // Turn it off if we can't find a saved value
    }
    if (value) CFRelease(value);
    // Get the contents of the text field
    value = CFPreferencesCopyAppValue
(CFSTR("String Value Key"), m_appID);
    if (value && CFGetTypeID(value) == CFStringGetTypeID()) {
        [m_textField setStringValue:(NSString *)value];
    } else {
        [m_textField setStringValue:@""];   
      // Make the string empty if there's no saved value
    }
    if (value) CFRelease(value);
}

In our override of mainViewDidLoad, we get the values of our two preferences (a checkbox and a text field, remember) from the file. We're getting some help from the cool Core Foundation Preference Services here. We call CFPreferencesCopyAppValue to get the values out of the file, and we use CFGetTypeID to ensure the values are valid. If not, we set the controls to default values. Continuing in our quest for good citizenship in case we have to run for governor some day, we release the value objects when we don't need them any more.

Preparing for the Invasion

Now that the pane is up and running and the controls have their values loaded, what happens when the user starts changing stuff? If the user clicks on the checkbox, we'll get a checkboxClicked message, so we'll override that method to change the associated preference at the same time:

- (IBAction)checkboxClicked:(id)sender 
{
      if ( [sender state] ) 
         CFPreferencesSetAppValue(CFSTR("Boolean Value Key"), 
         kCFBooleanTrue, appID ); 
      else 
         CFPreferencesSetAppValue(CFSTR("Boolean Value Key"), 
         kCFBooleanFalse, appID ); 
}

In this method, we're simply setting the value of the preference based on the state of the checkbox. We use the handy CFPreferencesSetAppValue call from Core Foundation Preference Services. Why don't we save the value of the text field this way, too? We could, by watching for various NSText messages, but it's probably sufficient just to save the text field when the user is done with the pane, which we'll do in didUnselect, coming up next.

Can you believe we're almost finished writing the code for our little sample? All that's left is the implementation of the didUnselect method:

- (void)didUnselect
// The system calls didUnselect when another pane is
// opening or the user quits System Preferences.
{
    CFNotificationCenterRef center;
   
   // Save the value in the text field
   // to our preferences file.
    CFPreferencesSetAppValue(CFSTR("String Value Key"), [m_textField stringValue], m_appID);
    
   // Make sure the file is flushed and its contents written
   // to disk.
   CFPreferencesAppSynchronize(m_appID);
   // Broadcast a notification that preferences have
   // changed, in case Big Brother is watching.
center = CFNotificationCenterGetDistributedCenter();
    CFNotificationCenterPostNotification(center, CFSTR("Preferences Changed"), m_appID, NULL, TRUE);
}

The main work of the didUnselect override is to save the contents of the text field to our prefs file, which we do by calling CFPreferencesSetAppValue and CFPreferencesAppSynchronize. Also, we call CFNotificationCenterPostNotification to send a notification to any interested code that our prefs have changed - probably, nobody cares, but you never know.

More Details

Putting together the nib file and finishing the project are pretty straightforward. Using Interface Builder, just place the controls you're using - in this case, our check box and text field - in the window provided by the project. Figure 3 shows how this looks in Interface Builder.


Figure 3. Our preference pane in Interface Builder.

Of course, when you're making your own preference pane, just arrange the controls and other user interface elements however you like. You can even add a tabbed view (NSTabView) to the window and place controls in multiple tabs. Your code doesn't know or care which tab contains a particular control.

Another neat trick is to add your own custom 32 by 32 pixel Finder icon for the pane. You can do this by putting your icon into the project's .tiff file - in our example, that would be BigPane.tiff. This file is automatically created by ProjectBuilder with the default light switch icon.

If you're building a preference pane as part of a real project, you'll want to include the pane in your installer. The pane should go into ~/Library/PreferencePanes. When you're just practicing, you can drag the preference bundle there and install it yourself. If the System Preferences application is running, quit it and run it again. You should see your new preference pane listed in the "Other" ghetto at the bottom of the screen if you're viewing by category, and in proper alphabetical order otherwise. Note that you can't place your pane into an existing category, nor can you create new categories. Bummer. Maybe that ability will come in a future version.

Summing Up

This covers the basics of creating a system preference pane, so you should now be able to go forward with your plans for that awesome system service or other minimal-UI widget. If you want to learn more about this topic, or many other related cool OS X tricks, please check out Mac OS X Advanced Development Techniques, written by Joe Zobkiw and published by Sams Publishing Developer's Library. See http://www.triplesoft.com/macosx/ to find out more.


Scott Knaster has been writing about Macs for as long as there have been Macs. Scott's books How To Write Macintosh Software and Macintosh Programming Secrets were required reading for Mac programmers for more than a decade. Scott wrote developer books for General Magic and worked on Mac software for Microsoft. Scott's books have been translated into Japanese and Pascal. Scott has every issue of Mad magazine, which explains a lot.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Tokkun Studio unveils alpha trailer for...
We are back on the MMORPG news train, and this time it comes from the sort of international developers Tokkun Studio. They are based in France and Japan, so it counts. Anyway, semantics aside, they have released an alpha trailer for the upcoming... | Read more »
Win a host of exclusive in-game Honor of...
To celebrate its latest Jujutsu Kaisen crossover event, Honor of Kings is offering a bounty of login and achievement rewards kicking off the holiday season early. [Read more] | Read more »
Miraibo GO comes out swinging hard as it...
Having just launched what feels like yesterday, Dreamcube Studio is wasting no time adding events to their open-world survival Miraibo GO. Abyssal Souls arrives relatively in time for the spooky season and brings with it horrifying new partners to... | Read more »
Ditch the heavy binders and high price t...
As fun as the real-world equivalent and the very old Game Boy version are, the Pokemon Trading Card games have historically been received poorly on mobile. It is a very strange and confusing trend, but one that The Pokemon Company is determined to... | Read more »
Peace amongst mobile gamers is now shatt...
Some of the crazy folk tales from gaming have undoubtedly come from the EVE universe. Stories of spying, betrayal, and epic battles have entered history, and now the franchise expands as CCP Games launches EVE Galaxy Conquest, a free-to-play 4x... | Read more »
Lord of Nazarick, the turn-based RPG bas...
Crunchyroll and A PLUS JAPAN have just confirmed that Lord of Nazarick, their turn-based RPG based on the popular OVERLORD anime, is now available for iOS and Android. Starting today at 2PM CET, fans can download the game from Google Play and the... | Read more »
Digital Extremes' recent Devstream...
If you are anything like me you are impatiently waiting for Warframe: 1999 whilst simultaneously cursing the fact Excalibur Prime is permanently Vault locked. To keep us fed during our wait, Digital Extremes hosted a Double Devstream to dish out a... | Read more »
The Frozen Canvas adds a splash of colou...
It is time to grab your gloves and layer up, as Torchlight: Infinite is diving into the frozen tundra in its sixth season. The Frozen Canvas is a colourful new update that brings a stylish flair to the Netherrealm and puts creativity in the... | Read more »
Back When AOL WAS the Internet – The Tou...
In Episode 606 of The TouchArcade Show we kick things off talking about my plans for this weekend, which has resulted in this week’s show being a bit shorter than normal. We also go over some more updates on our Patreon situation, which has been... | Read more »
Creative Assembly's latest mobile p...
The Total War series has been slowly trickling onto mobile, which is a fantastic thing because most, if not all, of them are incredibly great fun. Creative Assembly's latest to get the Feral Interactive treatment into portable form is Total War:... | Read more »

Price Scanner via MacPrices.net

Early Black Friday Deal: Apple’s newly upgrad...
Amazon has Apple 13″ MacBook Airs with M2 CPUs and 16GB of RAM on early Black Friday sale for $200 off MSRP, only $799. Their prices are the lowest currently available for these newly upgraded 13″ M2... Read more
13-inch 8GB M2 MacBook Airs for $749, $250 of...
Best Buy has Apple 13″ MacBook Airs with M2 CPUs and 8GB of RAM in stock and on sale on their online store for $250 off MSRP. Prices start at $749. Their prices are the lowest currently available for... Read more
Amazon is offering an early Black Friday $100...
Amazon is offering early Black Friday discounts on Apple’s new 2024 WiFi iPad minis ranging up to $100 off MSRP, each with free shipping. These are the lowest prices available for new minis anywhere... Read more
Price Drop! Clearance 14-inch M3 MacBook Pros...
Best Buy is offering a $500 discount on clearance 14″ M3 MacBook Pros on their online store this week with prices available starting at only $1099. Prices valid for online orders only, in-store... Read more
Apple AirPods Pro with USB-C on early Black F...
A couple of Apple retailers are offering $70 (28%) discounts on Apple’s AirPods Pro with USB-C (and hearing aid capabilities) this weekend. These are early AirPods Black Friday discounts if you’re... Read more
Price drop! 13-inch M3 MacBook Airs now avail...
With yesterday’s across-the-board MacBook Air upgrade to 16GB of RAM standard, Apple has dropped prices on clearance 13″ 8GB M3 MacBook Airs, Certified Refurbished, to a new low starting at only $829... Read more
Price drop! Apple 15-inch M3 MacBook Airs now...
With yesterday’s release of 15-inch M3 MacBook Airs with 16GB of RAM standard, Apple has dropped prices on clearance Certified Refurbished 15″ 8GB M3 MacBook Airs to a new low starting at only $999.... Read more
Apple has clearance 15-inch M2 MacBook Airs a...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs now available starting at $929 and ranging up to $410 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at... Read more
Apple drops prices on 13-inch M2 MacBook Airs...
Apple has dropped prices on 13″ M2 MacBook Airs to a new low of only $749 in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, now available for $679 for 8-Core CPU/7-Core GPU/256GB models. Apple’s one-year warranty is included, shipping is free, and each... Read more

Jobs Board

Seasonal Cashier - *Apple* Blossom Mall - J...
Seasonal Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Seasonal Fine Jewelry Commission Associate -...
…Fine Jewelry Commission Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) Read more
Seasonal Operations Associate - *Apple* Blo...
Seasonal Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Read more
Hair Stylist - *Apple* Blossom Mall - JCPen...
Hair Stylist - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.