Objective C Email Parsing and Email Signature Extraction

If you use Objective C and need to parse emails then this guide is for you.

We’ll cover:

Why parsing emails is hard

Splitting Email Chains

If you want to split emails on the headers with Objective C, that is tough. No two email clients seem to produce the same header format and over time the email clients change the way they format headers.

Signature and Contact Detail Detection

Parsing email signatures with Objective C is also difficult. Many think they can just use a couple regex expressions and they’ll be done. But the more you start working the problem the harder it becomes. Here are some of the major things you’d need to handle.

  • No signature is formatted the same
  • Phone numbers can have many different formats
  • Need to attribute the type of phone number (Fax vs Mobile vs Work phones).
  • The phone type indicator has lots of variations. For example, Mobile vs M: vs Cell vs C: and many others
  • Titles can be incredibly difficult to capture without getting too much wrong information.
  • Locations are tough. Very few people put full addresses. Often they’ll only put the city and state but no country. Even street addresses are massively different by country.

Then there is identifying where the email signature is in the email which is really hard. We use a machine learning algorithm with lots of labeled validation emails. We’ve been labeling our test set for years across many organizations.

This is why we expose a simple email parsing API for use with Objective C

All of the above is why we suggest using our email parsing service to parse emails from Objective C. As we improve you’ll automatically get any improvements without the need to redeploy any of your code. If an email client like Gmail starts using a new reply header format, we’ll have a fix deployed with days and you won’t have to do anything.

Get Your API Key

Try the SigParser API. Signup and get an API key with 1,500 free emails per month. Upgrade or downgrade at any time. Our API is entirely serverless and stateless.
Get Your API Key Now!

The SigParser Email Parsing API

The SigParser Email Parsing API is a serverless, stateless email parsing API which is easy to call from Objective C. It can extract contacts and split emails into sections. It can find phone numbers, titles, addresses and attribute them to the correct contact. It even takes care of deduping contacts for you if the same email address appears in the email.

Stateless means we store none of the email contents. It is a processing only service. We only store some high level statistics like length of the email or how long it took to process.

Example: Parse an Email with Objective C

Here is how to call the SigParser API with Objective C and convert the data to nice model. The model definition is below.

-(void)fetchData {
    
    NSDictionary *parameters = @{@"subject" : @"mr.John",
                                 @"from_address": @"jsmith@example.com",
                                 @"from_name": @"John Smith",
                                 @"htmlbody": @"description",
                                 @"plainbody": @"This is the body of the email",
                                 @"date": @"Current date"};
    
    NSData *postBodyData = [NSJSONSerialization dataWithJSONObject:parameters options:NSJSONWritingPrettyPrinted error:nil];


    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:@"https://api.sigparser.com/api/Email"]];
    [request setHTTPMethod:@"POST"];
    [request addValue:@"p1rrIkuP6v6famS623AGs1hA8MpBJ13b6VeMNkmz" forHTTPHeaderField:@"x-api-key"];
    [request addValue:@"no-cache" forHTTPHeaderField:@"cache-control"];
    [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:postBodyData];
    
    NSURLSession *session = [NSURLSession sharedSession];
    
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData * _Nullable data,
                                                                NSURLResponse * _Nullable response,
                                                                NSError * _Nullable error) {
                                                
                                                NSHTTPURLResponse *asHTTPResponse = (NSHTTPURLResponse *) response;
                                                NSLog(@"The response is: %@", asHTTPResponse);
                                                
                                                Model *model = [self createModel:data]; // got object model, here you can use it
                                            }];
    [task resume];
}

- (Model *)createModel:(NSData *) jsonData {
    NSError *error = nil;
    id response = [NSJSONSerialization
                   JSONObjectWithData:jsonData
                   options:NSJSONReadingMutableContainers
                   error:&error];
    
    if (error) {
        NSLog(@"data == nil");
        return [Model new];
    }
    Model *model = [Model new];
    
    if ([response isKindOfClass:[NSDictionary class]]) {
        model.contacts = [response valueForKey:@"contacts"];
        model.duration = [response valueForKey:@"duration"];
        model.emails = [self createContactsModel:[response valueForKey:@"emails"]];
        model.error = [response valueForKey:@"error"];
        model.fromAddress = [response valueForKey:@"from_Address"];
        model.fromEmailAddress = [response valueForKey:@"from_EmailAddress"];
        model.fromFax = [response valueForKey:@"from_Fax"];
        model.fromFirstName = [response valueForKey:@"from_FirstName"];
        model.fromLastName = [response valueForKey:@"from_LastName"];
        model.fromLinkedInHandle = [response valueForKey:@"from_LinkedInHandle"];
        model.fromLinkedInUrl = [response valueForKey:@"from_LinkedInUrl"];
        model.fromMobilePhone = [response valueForKey:@"from_MobilePhone"];
        model.fromOfficePhone = [response valueForKey:@"from_OfficePhone"];
        model.fromPhone = [response valueForKey:@"from_Phone"];
        model.fromTitle = [response valueForKey:@"from_Title"];
        model.fromTwitterHandle = [response valueForKey:@"from_TwitterHandle"];
        model.fromTwitterUrl = [response valueForKey:@"from_TwitterUrl"];
        model.isSpam = [response valueForKey:@"isSpam"];
        model.isSpammyLookingEmailMessage = [response valueForKey:@"isSpammyLookingEmailMessage"];
        model.isSpammyLookingSender = [response valueForKey:@"isSpammyLookingSender"];
        model.cleanedeMailBody = [response valueForKey:@"cleanedemailbody"];
        model.cleanedeMailBodyIsHtml = [response valueForKey:@"cleanedemailbody_ishtml"];
        model.cleanedeMailBodyPlain = [response valueForKey:@"cleanedemailbody_plain"];
    }
    return model;
}

-(NSArray *)createContactsModel:(NSArray *)emails {
    
    NSMutableArray *tempContacts = [NSMutableArray new];
    Email *email = [Email new];
    
    for (Email *tempEmail in emails) {
        email.fromEmailAddress = [tempEmail valueForKey:@"from_EmailAddress"];
        email.fromName = [tempEmail valueForKey:@"from_Name"];
        email.textBody = [tempEmail valueForKey:@"textBody"];
        email.htmlLines = [tempEmail valueForKey:@"htmlLines"];
        email.date = [tempEmail valueForKey:@"date"];
        email.didParseCorrectly = [tempEmail valueForKey:@"didParseCorrectly"];
        email.to = [self createUserModel:[tempEmail valueForKey:@"to"]];
        email.cc = [self createUserModel:[tempEmail valueForKey:@"cc"]];
        email.htmlBody = [tempEmail valueForKey:@"htmlBody"];
        email.spammyLookingEmail = [tempEmail valueForKey:@"spammyLookingEmail"];
        email.subject = [tempEmail valueForKey:@"subject"];
        
        [tempContacts addObject:email];
    }
    return tempContacts;
}

-(NSArray *)createUserModel:(NSArray *)users {
    
    NSMutableArray *tempUsers = [NSMutableArray new];
    User *user = [User new];
    
    for (User *tempUser in users) {
        user.name = [tempUser valueForKey:@"name"];
        user.emailAddress = [tempUser valueForKey:@"emailAddress"];
        
        [tempUsers addObject:user];
    }
    return tempUsers;
}

The Objective C classes which you can bind the response to looks like


//  Contacts.h

#import <Foundation/Foundation.h>

@interface Contact : NSObject

@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSString *emailAddress;
@property (nonatomic, strong) NSString *phoneNumber;
@property (nonatomic, strong) NSString *mobilePhone;
@property (nonatomic, strong) NSString *voipPhone;
@property (nonatomic, strong) NSString *officePhone;
@property (nonatomic, strong) NSString *fax;
@property (nonatomic, strong) NSString *address;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *twitterUrl;
@property (nonatomic, strong) NSString *twitterHandle;
@property (nonatomic, strong) NSString *linkedInUrl;
@property (nonatomic, strong) NSString *linkedInHandle;

@end

//  Email.h

#import <Foundation/Foundation.h>
#import "User.h"

@interface Email : NSObject

@property (nonatomic, strong) NSString *fromEmailAddress;
@property (nonatomic, strong) NSString *fromName;
@property (nonatomic, strong) NSString *textBody;
@property (nonatomic, strong) NSString *htmlLines;
@property (nonatomic, strong) NSString *date;
@property (nonatomic, assign) BOOL didParseCorrectly;
@property (nonatomic, strong) NSArray<User *> *to;
@property (nonatomic, strong) NSArray<User *> *cc;
@property (nonatomic, strong) NSString *htmlBody;
@property (nonatomic, assign) BOOL spammyLookingEmail;
@property (nonatomic, strong) NSString *subject;

@end

//  Model.h

#import <Foundation/Foundation.h>
#import "Contact.h"
#import "Email.h"

@interface Model : NSObject

@property (nonatomic, strong) NSString *error;
@property (nonatomic, strong) NSArray<Contact *>*contacts;
@property (nonatomic, assign) BOOL isSpammyLookingEmailMessage;
@property (nonatomic, assign) BOOL isSpammyLookingSender;
@property (nonatomic, assign) BOOL isSpam;
@property (nonatomic, strong) NSString *fromLastName;
@property (nonatomic, strong) NSString *fromFirstName;
@property (nonatomic, strong) NSString *fromFax;
@property (nonatomic, strong) NSString *fromPhone;
@property (nonatomic, strong) NSString *fromAddress;
@property (nonatomic, strong) NSString *fromTitle;
@property (nonatomic, strong) NSString *fromMobilePhone;
@property (nonatomic, strong) NSString *fromOfficePhone;
@property (nonatomic, strong) NSString *fromLinkedInUrl;
@property (nonatomic, strong) NSString *fromTwitterUrl;
@property (nonatomic, strong) NSString *fromTwitterHandle;
@property (nonatomic, strong) NSString *fromEmailAddress;
@property (nonatomic, strong) NSArray<Email *>*emails;
@property (nonatomic, strong) NSString *fromLinkedInHandle;
@property (nonatomic, strong) NSString *duration;
@property (nonatomic, strong) NSString *cleanedeMailBody;
@property (nonatomic, assign) BOOL cleanedeMailBodyIsHtml;
@property (nonatomic, strong) NSString *cleanedeMailBodyPlain;

@end

//  User.h

#import <Foundation/Foundation.h>

@interface User : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *emailAddress;

@end

The JSON looks like this.

{
    "error": "string",
    "contacts": [
      {
        "firstName": "string",
        "lastName": "string",
        "emailAddress": "string",
        "emailAddressDomain": "string",
        "emailAddressDomainWithoutTLD": "string",
        "phoneNumber": "string",
        "mobilePhone": "string",
        "voipPhone": "string",
        "officePhone": "string",
        "fax": "string",
        "address": "string",
        "title": "string",
        "twitterUrl": "string",
        "twitterHandle": "string",
        "linkedInUrl": "string",
        "linkedInHandle": "string",
        "companyName": "string",
        "website": "string"
      }
    ],
    "isSpammyLookingEmailMessage": true,
    "isSpammyLookingSender": true,
    "isSpam": true,
    "from_LastName": "string",
    "from_FirstName": "string",
    "from_Fax": "string",
    "from_Phone": "string",
    "from_Address": "string",
    "from_Title": "string",
    "from_MobilePhone": "string",
    "from_OfficePhone": "string",
    "from_LinkedInUrl": "string",
    "from_TwitterUrl": "string",
    "from_TwitterHandle": "string",
    "from_EmailAddress": "string",
    "emails": [
      {
        "from_EmailAddress": "string",
        "from_Name": "string",
        "textBody": "string",
        "htmlLines": [
          "string"
        ],
        "date": "2019-05-05T22:27:56.124Z",
        "didParseCorrectly": true,
        "to": [
          {
            "name": "string",
            "emailAddress": "string"
          }
        ],
        "cc": [
          {
            "name": "string",
            "emailAddress": "string"
          }
        ],
        "htmlBody": "string",
        "spammyLookingEmail": true,
        "subject": "string",
        "cleanedBodyHtml": "string",
        "cleanedBodyPlain": "string"
      }
    ],
    "from_LinkedInHandle": "string",
    "duration": 0,
    "cleanedemailbody": "string",
    "cleanedemailbody_ishtml": true,
    "cleanedemailbody_plain": "string",
    "from_CompanyName": "string",
    "from_Website": "string",
    "from_EmailAddressDomain": "string",
    "from_EmailAddressDomainWithoutTLD": "string"
  }

Get Your API Key

Try the SigParser API. Signup and get an API key with 1,500 free emails per month. Upgrade or downgrade at any time. Our API is entirely serverless and stateless.
Get Your API Key Now!