Viewing and Retrieving Content

After you obtain a feed object, you need to be able to retrieve the information from within it. Sometimes this information contains not only text, but also files in enclosures or extension XML. The following chapter describes what information is available in an entry and how to access it.

Where’s My Entry?

Most feeds contains one or more entries. To retrieve the entries from a feed object, use either the entries property (for an array of entry objects) or the entryEnumeratorSortedBy: method (for an enumerator of entry objects).

An entry object contains all the information specific to an entry, such as its title, its URL, its authors, its content, and, if necessary, its enclosures. Most of this information is a simple string or URL. However, three of components are a little more complicated.

In some feed standards, the author contains not only a name but also contains an email address and a homepage (see Feed Formats). The author information in Publication Subscription is stored in an author object. An author object represents a name, an email address, a homepage and a link to an ABPerson object.

The content of an entry is also more complex than a simple string. It often comes in one of two different forms: either plain text, or HTML formatted text. In Publication Subscription the content and the summary of an entry are stored as content objects. A content object contains methods for retrieving the content either as a plain text string or an HTML string. The content is returned in the specified format based on which accessor method is used.

An enclosure is a way to attach a file to an entry. If an entry contains enclosures, they will be linked to the entry object. For more information about enclosures, read Downloading Enclosures.

To print out the title and content of every entry in a feed, the code would look like Listing 4-1.

Listing 4-1  Accessing every entry in a feed

// Retrieve the entries as an unsorted enumerator
NSEnumerator *entries = [feed entryEnumeratorSortedBy: nil];
PSEntry *entry;
 
// Go through each entry and print out the title, authors, and content
while (entry = [entries nextObject]) {
    NSLog(@"Entry Title:%@", entry.title);
    NSLog(@"Entry Authors:%@", entry.authorsForDisplay);
    NSLog(@"Entry Content:%@", entry.content.plainTextString);
}

Since it follows an observer design pattern, Publication Subscription can also notify your application when any changes occur to a feed. Register with the notification center to be alerted when any change occurs (PSFeedEntriesChangedNotification). When your callback method is invoked, the changed entries are stored as a key-value pair in the user information dictionary of the notification.

Downloading Enclosures

Some entries may contain links to files as enclosures. Publication Subscription provides features that make it easy to download the files within the enclosure. Each enclosure is stored as a enclosure object in its associated entry object. Because there may be more than one enclosure in an entry, the entry object property enclosures returns an array of enclosure objects. In most cases, this array only contains one object. Each of these objects contains information about the enclosure’s size, URL and MIME type.

By default, enclosures are not automatically downloaded. You can change this setting on a per-feed basis so that any enclosure from a subscribed feed is downloaded with the entry. Listing 4-2 shows how to make a feed download its enclosures automatically.

Listing 4-2  Downloading enclosures automatically

PSFeedSettings *settings = feed.settings;
settings.downloadsEnclosures = YES;
feed.settings = settings;

If you want to download the file in the enclosures individually, send download: to the appropriate enclosure object. The download: method is asynchronous, so it rarely returns an error. Instead, check on the status of the download with the downloadState property. There are six possible states:

If the download failed, see what caused the failure by using the downloadError method. If the download is still active, you can check on its progress by using the downloadProgress method. Assuming the download finishes, the location of the downloaded file is available with the downloadedPath property.

Although the download status can be checked in a synchronous manner, it is recommended that you register for the notification PSEnclosureDownloadStateDidChangeNotification instead. When your callback method is invoked, you can determine the status of the download. Listing 4-3 shows how to start downloading the file in the enclosure and register for the appropriate notification. Listing 4-4 shows a callback method for the notification.

Listing 4-3  Downloading an enclosure

 
// Get the enclosures from the current entry, and retrieve the first one
NSArray *enclosureArray = entry.enclosures;
enclosure = [enclosureArray objectAtIndex: 0];
NSError *error;
 
// Download the enclosure
if (![enclosure download:&error]) {
    NSLog(@"Enclosure download failed: %@", error)
} else {
 
    // Register for any changes to the download's state
    [[NSNotificationCenter defaultCenter]
            addObserver:self
            selector:@selector(downloadStateChanged:)
            name: PSEnclosureDownloadStateDidChangeNotification
            object: enclosure];
}

Listing 4-4  Receiving download state changes through notifications

 
- (void) downloadStateChanged: (NSNotification *) sender {
 
    // See what state change cause the notification to be sent
    switch (enclosure.downloadState) {
 
        // If the download failed, log why and stop receiving notifications
        case PSEnclosureDownloadStateDidFail:
            NSLog(@"Enclosure download failed: %@", enclosure.downloadError);
            [[NSNotificationCenter defaultCenter]
                removeObserver: self
                name: PSEnclosureDownloadStateDidChangeNotification
                object: enclosure];
            break;
 
        // If the download succeeded, log the location of the file and stop
        // receiving notifications
        case PSEnclosureDownloadStateDidFinish:
            NSLog(@"Location of downloaded file is: %@", enclosure.downloadedPath);
            [[NSNotificationCenter defaultCenter]
                removeObserver: self
                name: PSEnclosureDownloadStateDidChangeNotification
                object: enclosure];
            break;
 
        case default:
            break;
    }
}

Extension XML

Many feeds may also contain extension XML elements. If additional namespaces are used in a feed, use the extensionXMLElementsUsingNamespace: method to return an array of NSXMLElement objects. Pass the namespace URL, not the prefix, to the extensionXMLElementsUsingNamespace: method. If your feed had an entry like that in Listing 1-3, extensionXMLElementsUsingNamespace: returns an array of four NSXMLElement objects.

Listing 4-5 shows how to find a specific element in a particular namespace in an entry. In this example, the namespace is the iTunes Podcast.

Listing 4-5  Retrieving extension XML from an entry

// Find each element using the iTunes Podcast namespace
for( NSXMLElement *elem in [entry extensionXMLElementsUsingNamespace: @"http://www.itunes.com/dtds/podcast-1.0.dtd"] ) {
 
    // Check if the element is called "keywords"
    if (NSOrderedSame == [[elem localName] isEqualToString:@"keywords"]) {
 
        // If it is, print the data from the element to the log
        NSLog(@"keywords:%@", [elem stringValue]);
    }
}