Skip to content
This repository has been archived by the owner on Dec 3, 2019. It is now read-only.

Post Processors

Nick Entin edited this page Aug 7, 2016 · 1 revision

How Post Processors Work

After the data source loads contacts from the data providers, it runs the contacts through any post processors attached to it. The post processors can add, modify, or remove contacts passed through it. Ohana comes with a set of post processors that are useful in many situations, as well as a set of composite post processors that enable logical operations between post processors. If you don't see a post processor for what you're trying to do, it's easy to write your own.

Existing Post Processors

Ohana provides a set of post processors to help you get started. The existing post processors can be found under the PostProcessors directory.

OHAlphabeticalSortPostProcessor

The OHAlphabeticalSortPostProcessor sorts contacts by either full name, first name, or last name, depending on the sort mode provided at initialization. If the contact does not have any value for the specified sort field, it will be placed at the end.

// Sort contacts by full name
OHAlphabeticalSortPostProcessor *processor = [[OHAlphabeticalSortPostProcessor alloc] initWithSortMode:OHAlphabeticalSortPostProcessorSortModeFullName];

The OHAlphabeticalSortPostProcessor sorts contacts in ascending order. If you want to have contacts sorted in descending order, simply add an OHReverseOrderPostProcessor after this one.

OHPhoneNumberFormattingPostProcessor

The OHPhoneNumberFormattingPostProcessor formats the phone numbers of the contacts it processes and stores the formatted values in the customProperties dictionary of each of the phone number contact fields. The formats that should be used can be set at initialization or by modifying the formats property. The following formats are available:

  • OHPhoneNumberFormatE164 +15551234567
  • OHPhoneNumberFormatInternational +1 555-123-4567
  • OHPhoneNumberFormatNational (555) 123-4567
  • OHPhoneNumberFormatRFC3966 tel:+1-555-123-4567
// Format phone numbers in E164 and National formats
OHPhoneNumberFormattingPostProcessor *processor = [[OHPhoneNumberFormattingPostProcessor alloc] initWithFormats:OHPhoneNumberFormatE164|OHPhoneNumberFormatNational];

// Get the E164 format for contactField
id formattedE164 = [contactField.customProperties objectForKey:kOHFormattedPhoneNumberE164];
if (formattedE164 && [formattedE164 isKindOfClass:[NSString class]]) {
    NSLog(@"Formatted phone number: %@", formattedE164);
}

OHRequiredFieldPostProcessor

The OHRequiredFieldPostProcessor filters out contacts that do not have a field matching the required field type for which the post processor is initialized.

// Filter out contacts that do not have a phone number
OHRequiredFieldPostProcessor *processor = [[OHRequiredFieldPostProcessor alloc] initWithFieldType:OHContactFieldTypePhoneNumber];

This post processor filters out contacts before they are added to the data source. If you want to filter on selection (i.e. to display all contacts but only allows ones with a phone number to be selected), use the OHRequiredFieldSelectionFilter instead.

OHRequiredPostalAddressPostProcessor

The OHRequiredPostalAddressPostProcessor filters out contacts that do not have a postal address.

// Filter out contacts that do not have a postal address
OHRequiredPostalAddressPostProcessor *processor = [[OHRequiredPostalAddressPostProcessor alloc] init];

OHReverseOrderPostProcessor

The OHReverseOrderPostProcessor reverses the order of the contacts passed into it.

// Reverse the order of contacts
OHReverseOrderPostProcessor *processor = [[OHReverseOrderPostProcessor alloc] init];

OHSplitOnFieldTypePostProcessor

The OHSplitOnFieldTypePostProcessor splits a contact with multiple contact fields of a given type into separate contacts, each with one of the contact fields.

// Split contacts by phone number
OHSplitOnFieldTypePostProcessor *processor = [[OHSplitOnFieldTypePostProcessor alloc] initWithFieldType:OHContactFieldTypePhoneNumber];

OHStatisticsPostProcessor

The OHStatisticsPostProcessor generates statistics on the contacts it processes and stores the results in the customProperties dictionary of each contact. For each of the contacts it generates the following statistics:

  • Total number of contact fields
  • Number of phone number fields
  • Number of email address fields
  • Whether or not the contact has a mobile number
// Create the statistics post processor
OHStatisticsPostProcessor *processor = [[OHStatisticsPostProcessor alloc] init];

// Check how many phone number a contact has
NSUInteger numPhoneNumbers = [(NSNumber *)[contact.customProperties objectForKey:kOHStatisticsNumberOfPhoneNumbers] unsignedIntegerValue];

Using the Composite Post Processors

Ohana provides two composite post processors that allow you to perform AND and OR logic on contacts returned from post processors. You can stack composite post processors to get more complicated logical systems.

OHCompositeAndPostProcessor

The OHCompositeAndPostProcessor performs an AND logic operation on the contacts returned by the post processors it is initialized with.

// Return contacts that have a phone number
OHRequiredFieldPostProcessor *phoneRequiredProcessor = [[OHRequiredFieldPostProcessor alloc] initWithFieldType:OHContactFieldTypePhoneNumber];

// Return contacts that have an email address
OHRequiredFieldPostProcessor *emailRequiredProcessor = [[OHRequiredFieldPostProcessor alloc] initWithFieldType:OHContactFieldTypeEmailAddress];

// Return contacts that have both a phone number and email address
OHCompositeAndPostProcessor *processor = [[OHCompositeAndPostProcessor alloc] initWithPostProcessors:[NSOrderedSet orderedSetWithObjects:phoneRequiredProcessor, emailRequiredProcessor, nil]];

The data source performs an AND operation on the results of top level post processors. The OHCompositeAndPostProcessor should be used only for nesting, not as a top level post processor.

OHCompositeOrPostProcessor

The OHCompositeOrPostProcessor performs an OR logic operation on the contacts returned by the post processors it is initialized with.

// Return contacts that have a phone number
OHRequiredFieldPostProcessor *phoneRequiredProcessor = [[OHRequiredFieldPostProcessor alloc] initWithFieldType:OHContactFieldTypePhoneNumber];

// Return contacts that have an email address
OHRequiredFieldPostProcessor *emailRequiredProcessor = [[OHRequiredFieldPostProcessor alloc] initWithFieldType:OHContactFieldTypeEmailAddress];

// Return contacts that have either a phone number or email address or both
OHCompositeOrPostProcessor *processor = [[OHCompositeOrPostProcessor alloc] initWithPostProcessors:[NSOrderedSet orderedSetWithObjects:phoneRequiredProcessor, emailRequiredProcessor, nil]];

OHCompositeXorPostProcessor

The OHCompositeXorPostProcessor performs an XOR logic operation on the contacts returned by the post processors it is initialized with.

// Return contacts that have a phone number
OHRequiredFieldPostProcessor *phoneRequiredProcessor = [[OHRequiredFieldPostProcessor alloc] initWithFieldType:OHContactFieldTypePhoneNumber];

// Return contacts that have an email address
OHRequiredFieldPostProcessor *emailRequiredProcessor = [[OHRequiredFieldPostProcessor alloc] initWithFieldType:OHContactFieldTypeEmailAddress];

// Return contacts that have either a phone number or email address (but not both)
OHCompositeXorPostProcessor *processor = [[OHCompositeXorPostProcessor alloc] initWithPostProcessors:[NSOrderedSet orderedSetWithObjects:phoneRequiredProcessor, emailRequiredProcessor, nil]];

Creating Your Own Post Processor

All post processors must conform to the OHContactsPostProcessorProtocol. A custom post processor will look something like this:

@interface MyCustomPostProcessor : NSObject <OHContactsPostProcessorProtocol>

@end

@implementation MyCustomPostProcessor

// Synthesize the finished signal (required by protocol)
@synthesize onContactsPostProcessorFinishedSignal = _onContactsPostProcessorFinishedSignal;

- (instancetype)init
{
    if (self = [super init]) {
        // Initialize the finished signal (required by protocol)
        _onContactsPostProcessorFinishedSignal = [[OHContactsPostProcessorFinishedSignal alloc] init];
    }
    return self;
}

#pragma mark - OHContactsPostProcessorProtocol

- (NSOrderedSet<OHContact *> *)processContacts:(NSOrderedSet<OHContact *> *)preProcessedContacts
{
    // Initialize a new ordered set to store processed contacts
    NSMutableOrderedSet<OHContact *> *processedContacts = [[NSMutableOrderedSet<OHContact *> alloc] init];

    // Process contacts in preProcessedContacts and add to processedContacts
    // ...

    // Fire the finished signal with the processed contacts
    _onContactsPostProcessorFinishedSignal.fire(processedContacts, self);
    return processedContacts;
}

@end

Post processors can do very simple actions like sorting contacts alphabetically or filtering contacts without a phone number, or perform much more complex actions like filtering contacts that have a postal address within a certain proximity of the user's current location. The possibilities are limitless!