< Previous PageNext Page > Hide TOC

Providing a Service

Suppose you are working on a program to read USENET news, and have an object with a method to encrypt and decrypt articles, such as the one in Listing 1. News articles containing offensive material are often encrypted with this algorithm, called “rot13,” in which letters are shifted halfway through the alphabet.

Listing 1  Text encryption method

- (NSString *)rotateLettersInString:(NSString *)aString
{
    NSString *newString;
    unsigned length;
    unichar *buf;
    unsigned i;
 
    length = [aString length];
    buf = malloc( (length + 1) * sizeof(unichar) );
    [aString getCharacters:buf];
    buf[length] = (unichar)0; // not really needed....
    for (i = 0; i < length; i++) {
        if (buf[i] >= (unichar)'a' && buf[i] <= (unichar) 'z') {
            buf[i] += 13;
            if (buf[i] > 'z') buf[i] -= 26;
        } else if (buf[i] >= (unichar)'A' && buf[i] <= (unichar) 'Z') {
            buf[i] += 13;
            if (buf[i] > 'Z') buf[i] -= 26;
        }
    }
    newString = [NSString stringWithCharacters:buf length:length];
    free(buf);
    return newString;
}

Contents:

Implementing the Service Method
Registering the Service Provider
Advertising the Service
Installing the Service


Implementing the Service Method

Since this feature is generally useful as a simple encryption scheme, it can be exported to other applications. To offer this functionality as a service, write a method such as the one in Listing 2.

Listing 2  Service method

- (void)simpleEncrypt:(NSPasteboard *)pboard
            userData:(NSString *)userData
            error:(NSString **)error
{
    NSString *pboardString;
    NSString *newString;
    NSArray *types;
 
    types = [pboard types];
    if (![types containsObject:NSStringPboardType]) {
        *error = NSLocalizedString(@"Error: couldn't encrypt text.",
                        @"pboard couldn't give string.");
        return;
    }
    pboardString = [pboard stringForType:NSStringPboardType];
    if (!pboardString) {
        *error = NSLocalizedString(@"Error: couldn't encrypt text.",
                        @"pboard couldn't give string.");
        return;
    }
    newString = [self rotateLettersInString:pboardString];
    if (!newString) {
        *error = NSLocalizedString(@"Error: couldn't encrypt text.",
                        @"self couldn't rotate letters.");
        return;
    }
    types = [NSArray arrayWithObject:NSStringPboardType];
    [pboard declareTypes:types owner:nil];
    [pboard setString:newString forType:NSStringPboardType];
    return;
}

The method providing the service is of the form messageName:userData:error: and takes arguments as shown in Listing 2. The method itself takes data from the pasteboard as needed, operates on it, and writes any results back to the pasteboard. In case of an error, the method simply sets the pointer given by the error argument to a non-nil NSString and returns. The error message is logged to the console. The userData argument is not used here.

If implementing the service in Java, the invoked method has the form

String messageName(NSPasteboard pboard, String userData)

This method returns null if successful; otherwise, it returns the error message string.

Registering the Service Provider

Now you have an object with methods that allow it to perform a service for another application. Next, you need to register the object at run time so the services facility knows which object to have perform the service. You create and register your object in the applicationDidFinishLaunching: application delegate method (or equivalent) with NSApplication’s setServicesProvider: method. If your object is called encryptor you create and register it with this code fragment:

EncryptoClass *encryptor;
encryptor = [[EncryptoClass alloc] init];
[NSApp setServicesProvider:encryptor];

If you are writing a plain Foundation tool, which lacks an NSApplication object, register the service object with the NSRegisterServicesProvider function. Its declaration is:

void NSRegisterServicesProvider(id provider, NSString *portName)

provider is the object that provides the services, and portName is the same value you specify for the NSPortName entry in the services specification. After making this function call, you must enter the run loop to respond to service requests.

You can register only one service provider per application. If you have more than one service to provide, a single object must provide the interface to all of the services.

Service requests can arrive immediately after registering the object, in some circumstances even before exiting applicationDidFinishLaunching:. Therefore, register your service provider only when you are completely ready to process requests.

Advertising the Service

For the system to know that your application provides a service, you must advertise that fact. You do this by adding an entry to your application project’s Info.plist file. The entry you add is called the service specification. In our example, the service specification looks like this:

NSServices = (
    {
        NSPortName = NewsReader;
        NSMessage = simpleEncrypt;
        NSSendTypes = (NSStringPboardType);
        NSReturnTypes = (NSStringPboardType);
        NSMenuItem = {
            default = "Encrypt Text";
        };
        NSKeyEquivalent = {
            default = E;
        };
    }
);

Installing the Service

A service can be offered as part of an application, such as Mail, or as a standalone service—one without a user interface that is intended for use only in the Services menu. Applications that offer services should be built with the .app extension and installed in the Applications folder (or a subfolder) in one of the four file-system domains—System, Network, Local, and User. (See “File-System Domains” in System Overview for details.) A standalone service should be built with the .service extension and stored in the Library/Services folder in one of these domains.

The list of available services on the computer is built each time a user logs in. After installing your service in either an Applications or Library/Services directory, you need to log out and back in again before the service becomes available. You can force an update of the list of services without logging out by calling the following function:

void NSUpdateDynamicServices(void)

Running applications are not affected, but applications launched afterwards get the new list of services.



< Previous PageNext Page > Hide TOC


© 2003, 2002 Apple Computer, Inc. All Rights Reserved. (Last updated: 2002-11-12)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.