Brian Coleman

  • Home
  • ABOUT
  • SERVICES
  • PORTFOLIO
  • BLOG
  • Contact

Objective-c 16

Framework: Flurry Analytics

With every app that you release you should include an analytics framework. It’s important for you to know how many people downloaded your app, how many are actually using it, how often they are using it and what they are using in it. You spend all this time developing killer features but do your users care? One of the most important strategies around apps is to constantly improve your product. Your app may not be a #1 app the first time you release it, but if you constantly improve the usability based on what users are doing in your app then it’ll start climb the charts and get better word of mouth. Focus on enhancements to the areas of your app that users like the most.
omniture_550b
When you’re testing your app yourself and with your family and friends you get a little feedback, but until you have a user base of at least a thousand users it’s tough to know for sure, you don’t have enough data to conclude how most people will use your app. By tagging the right events in your app you can get a good sense of the funnel users are taking and optimize the users experience based on that data.

Analytics Tools

There are three major companies for App Analytics. The first is Omniture. It’s been around for a very long time and has been a best in class suite for the biggest websites. If you are working on an app for a top brand and their website is currently being tracked using Omniture, this is probably the best way to go since the investment has already been made. Omniture is a very expensive suite and typically only used by big business.

Another popular way to track analytics in apps is using Google Mobile App Analytics. For many users they already have for web and mobile web analytics, it makes total sense to keep using the same tool they’re used to for in-app usage analytics. The best thing about it is it’s FREE!

The tool that I use in all of my personal apps and the one we’re going to focus on is Flurry Analytics. Flurry is a complete tool for your mobile app analytics. With Flurry, you can filter via segments (age, first session, usage, country, etc), app versions and dates. Because Flurry offers lots of other services (like ads), this tool is free yet provides a lot of analytics.

Here’s the kind of info you can find or use in the dashboards:
– Usage: active users, sessions, session lengths, frequency, retention, etc.
– Audience: interest of users(your other apps + category), personas (type of your users – defined by Flurry), demographic
– Events: define events, see user paths, create funnels
– Technical: devices, carriers, firmware versions, errors

Setting up Flurry Analytics

Follow the steps below to get started with Flurry:
1. Visit www.flurry.com and sign up, don’t worry it’s FREE!
2. Click the “Applications Tab” then “Create a New Application” and Select the Platform your app is on (iPhone or iPad).
3. Enter the Name and Category your application will be submitted to within the App Store.
4. Make sure you copy the unique application key (API KEY), you’re going to need it soon.
5. Download the Flurry SDK.
6. Copy the Flurry Library (Flurry.h and libFlurry.a) files into your project.
7. Add the following code into your App Delegate.

#import "Flurry.h"
- (void)applicationDidFinishLaunching:(UIApplication *)application 
{
     [Flurry startSession:@"YOUR_API_KEY"];
     //your code
}

8. Start tagging the events within your application

[FlurryAnalytics logEvent:@"Start_Button_Pressed"];

You can track up to 300 Events for each application so track everything you can. Add the event tag above to every method you have in your project. You should be tracking every button pressed by the user, every page view, every completed stage, every ad clicked and every step to in-app purchases. Once you have that done, run your app a couple times and look within Flurry Reports in an hour or so to see if you’re capturing data. Once you launch your next app check back with Flurry to see how many active users are using your app, what kind of device they have, what iOS version and what country they are from. With all of this data plus all of the events you’ve tagged you will get a holistic view into how users are using your app, then you can optimize and continue to improve in your next update.

March 13, 2013 Frameworksframework, ios, objective-c

Framework: MTLabel

Not too long ago I needed to build a table of contents using a table view controller. It displayed a page title and a description. Our designer asked if I could change the leading in the line spacing for each. My initial response was no that can’t be done using a UILabel because the leading is automatic based on the size and font. I started to look for solutions and the only one I could come up with was to calculate the number of characters then break the title out into multiple UILabels. After more thought it would be more difficult to detect where to break it since it would have to break after a word, not just in the middle of a word, so I continued to search for a solution. Then I found MTLabel.

Change the Leading of your Label

MTLabel is a great framework built by Michal Tuszynski. It allows you to customize line spacing, specify if you want the label to resize itself based on text height and supports almost all features of UILabel. It actually is a UIView that you sub-class with type MTLabel. This was very useful and now I’m using it all the time when I need to make wrapping text look good with the right amount of line spacing.

First, you’ll need to integrate the framework into your project.
1. Download the source code from the MTLabel GitHub.
2. Add the following files into your project:
– MTLabel.h
– MTLabel.m
3. Add the “CoreText.framework” framework to your project. To do this, access your target settings, click on “Summary”, scroll down to the “Linked Frameworks and Libraries” group and click the + button to add a new framework to your project. Search for “CoreText.framework”.
4. Include the following header at the top of your class:

#import "MTLabel.h"

4. Start coding!

MTLabel *titleLabelView = [MTLabel labelWithFrame:CGRectMake(15, 4, 252, 100) andText:@"This is where you place the text you need to wrap"];
[titleLabelView setFontColor:[UIColor blackColor]];
[titleLabelView setFont:[UIFont fontWithName:@"Helvetica-Bold" size:19.0]];
[titleLabelView setLineHeight:18];
[self.view addSubview:titleLabelView];

The code above shows a basic implementation of MTLabel. Define the frame of your label and the text, then style the font color and finally set the line height to the space you would like between the lines. Look at that, a lot nicer than UILabel.

Resizing MTLabels and Table View Cells

The example above works well if you know how much text you have and the exact space you have to place the label, but what if you need dynamic text like my table of contents example. Implementing this in a table view controller is a little tougher because each title and description can be different. The method below will calculate the height of the text view needed based on the font and width provided.

-(CGSize) calcLabelSize:(NSString *)string withFont:(UIFont *)font  maxSize:(CGSize)maxSize{
    return [string sizeWithFont:font constrainedToSize:maxSize];
}

Let’s see this method in action below. We’ll calculate the expected height that we need the MTLabel to be based on what is returned from the method above.

CGSize titlemax = CGSizeMake(250, 100);
CGSize titleexpected = [self calcLabelSize:[toc name] withFont:[UIFont fontWithName:@"Helvetica-Bold" size:19.0] maxSize:titlemax];

MTLabel *titleLabelView = [MTLabel labelWithFrame:CGRectMake(15, 4, 252, titleexpected.height) andText:[toc name]];
[titleLabelView setFontColor:[UIColor blackColor]];
[titleLabelView setFont:[UIFont fontWithName:@"Helvetica-Bold" size:19.0]];
[titleLabelView setLineHeight:18];
[cell addSubview:titleLabelView];

Notice that the height of the MTLabel is being set by “titleexpected.height” which was calculated using the “calcLabelSize” method. If you are using a table view controller you’ll also need to calculate and resize the cell height based on the font as well.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
  NSString *key = [[_sections objectAtIndex:[indexPath section]] sectionName];
  NSArray *items = [_titles objectForKey:key];
  ClassName *toc = [items objectAtIndex:[indexPath row]];
  CGSize max = CGSizeMake(250, 100);
  CGSize expected = [self calcLabelSize:[toc name] withFont:[UIFont fontWithName:@"Helvetica-Bold" size:19.0] maxSize:max];
  return expected.height;
}

I hope this short tutorial helped you dynamically set the height of a text label and make it look nice with the proper line spacing.

March 12, 2013 Frameworksframework, ios, objective-c

Tutorial: Local Notifications

I know Push Notifications is all the rage lately, but let’s not forget about Local Notifications. They are much easier to implement and can sometimes be just as effective in getting Users to come back to your app. The short tutorial below will describe the steps needed to setup Local Notifications in your app.

Setup your Local Notification

The method below will set the notification to fire at a specific time. It doesn’t matter if the user has your app open or not. The notification will show up like most do on the device, usually a banner at the top of the screen that includes your app icon and a message. Users can change the alert style within “General Settings > Notifications > Your App Name”. Your notification will also appear in the device Notification Center.

-(void) setLocalNotification {
    
    NSDateComponents *comps = [[NSDateComponents alloc] init];
    [comps setYear:2013];
    [comps setMonth:05];
    [comps setDay:01];
    [comps setHour:12];
    [comps setMinute:0];
    [comps setTimeZone:[NSTimeZone systemTimeZone]];
    NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDate *setTime = [cal dateFromComponents:comps];
    
    UILocalNotification *localNotif = [[UILocalNotification alloc] init];
    if (localNotif == nil)
        return;
    localNotif.fireDate = setTime;
    localNotif.timeZone = [NSTimeZone defaultTimeZone];
	
    // Notification details
    localNotif.alertBody = @"Don't forget to come back to the app name today for your bonus!";
    // Set the action button
    localNotif.alertAction = @"View";
	
    localNotif.soundName = UILocalNotificationDefaultSoundName;
    localNotif.applicationIconBadgeNumber = 1;
	
    // Schedule the notification
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
}

To customize the code above change the NSDate entry to the date and time you would like your notification to fire. This method could easily be modified to take an NSDate parameter to allow you to call this method based on a dynamic date and time. Next you’ll want to change the alertBody to the message you want the user to see. It should contain a call to action so the user will be compelled to tap it to return to your app. SoundName can be changed to to accept an mp3 or wav if you wish. The example above uses the default sound setup on the users device. The application icon red badge bubble is then set to one to notify the user that their is a notification for you app that has not yet been tapped by them.

Cancelling All Notifications

You may want to include a toggle within your app to allow the user to turn off/on notifications. If the user turns notification off you should remove all notifications set so the user doesn’t receive them. Use the method below to remove all local notifications. If the user turns notifications back on then you’ll need to loop through your data recursively using the method above.

    // Cancel all Notifications
    UIApplication* app = [UIApplication sharedApplication];
    NSArray *notifications = [app scheduledLocalNotifications];
    if ([notifications count] > 0)
       [app presentLocalNotificationNow:notifications[0]];

Receiving the Notification

When a user taps on the notification they receive your app needs to handle it. irst the red bubble indicator on the app icon needs to be reset to zero. Then your app needs to handle the notification. This could be showing a specific view within your app (i.e. Give a user something free for coming back to your app if it’s a game). If you just want your app to open as normal, use the method below. This is handled in your app delegate.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    
    self.mainVC = [[MainViewController alloc] initWithNibName:@"MainViewController" bundle:nil];
    self.window.rootViewController = self.mainVC;
    [self.window makeKeyAndVisible];
    
    // Reset the Icon Alert Number back to Zero
    application.applicationIconBadgeNumber = 0;
    
    // Detect the Notification after a user taps it
    UILocalNotification *localNotif =
    [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    if (localNotif) 
    {
	NSLog(@"Recieved Notification %@",localNotif);
    }
    
    return YES;
}

- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif {
	// Handle the notification when the app is running
	NSLog(@"Recieved Notification %@",notif);
}
March 8, 2013 Tutorialsios, objective-c, tutorial

Tutorial: Setting an Event in iCal

If you would need to programmatically set an event in the users Calendar you’ll need to leverage the EventKit framework API. The EventKit framework provides classes for accessing and manipulating calendar events and reminders.

First, access your target settings, click on “Summary”, scroll down to the “Linked Frameworks and Libraries” group and click the + button to add a new framework to your project. Search for ‘EventKit.Framework’.

Then you will be able to add a method like the one below to your class.

First add the EventKit framework to the top of your *.m file.

#import 

Next use the method below to set the iCal event.

-(void) setiCalEvent {

   // Set the Date and Time for the Event
    NSDateComponents *comps = [[NSDateComponents alloc] init];
    [comps setYear:2013];
    [comps setMonth:3];
    [comps setDay:5];
    [comps setHour:9];
    [comps setMinute:0];
    [comps setTimeZone:[NSTimeZone systemTimeZone]];
    NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDate *eventDateAndTime = [cal dateFromComponents:comps];

    // Set iCal Event
    EKEventStore *eventStore = [[EKEventStore alloc] init];

    EKEvent *event = [EKEvent eventWithEventStore:eventStore];
    event.title = @"EVENT TITLE";

    event.startDate = eventDateAndTime;
    event.endDate = [[NSDate alloc] initWithTimeInterval:600 sinceDate:event.startDate];

    // Check if App has Permission to Post to the Calendar
    [eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
        if (granted){
            //---- code here when user allows your app to access their calendar.
            [event setCalendar:[eventStore defaultCalendarForNewEvents]];
            NSError *err;
            [eventStore saveEvent:event span:EKSpanThisEvent error:&err];
        }else
        {
            //----- code here when user does NOT allow your app to access their calendar.
            [event setCalendar:[eventStore defaultCalendarForNewEvents]];
            NSError *err;
            [eventStore saveEvent:event span:EKSpanThisEvent error:&err];
        }
    }];
}

To customize it for your app, set the date and time of the iCal event, then change the event.title to what you want the user to see in their calendar, and finally the length of the event in seconds (i.e. 600 secs = 1 hour).

    [eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
        if (granted){
            //---- code here when user allows your app to access their calendar.

        }else
        {
            //----- code here when user does NOT allow your app to access their calendar.

        }
    }];

On iOS6, Apple introduced a new privacy control. The user can control permissions for the contact and calender by each app. So in the code side, you need to add some way to request the permission. In iOS5 or before, you can always call it without the user having to give permission.

March 5, 2013 Tutorialsios, objective-c, tutorial

Framework: Using FMDB to communicate with SQLite Databases

If you require a lot of data in your app there are many ways to store data in iOS. CoreData, NSDictionary and direct calls to an SQLite database. I usually choose to use SQLite databases because I’ve always used MySQL databases on previous web programming projects. The typical way to access an SQLite DB in iOS is using the code below.

sqlite3 *_database = 0;
NSString* dbPath = nil;
NSString *contentPath;
dbPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Database.sqlite"];

if (sqlite3_open([dbPath UTF8String], &_database) != SQLITE_OK) {
    NSLog(@"Failed to open database!");
}
else
{
    NSString *query = [NSString stringWithFormat:@"SELECT * FROM tableName"];
    sqlite3_stmt *statement;
    if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil) == SQLITE_OK) {
         while (sqlite3_step(statement) == SQLITE_ROW) {
            ClassName *object	= [[ClassName alloc] init];
            object.property = sqlite3_column_int(statement, 0);
            object.property = [NSString stringWithFormat:@"%s",sqlite3_column_text(statement, 1)];
            object.property = [NSString stringWithFormat:@"%s",sqlite3_column_text(statement, 2)];
            object.property = [NSString stringWithFormat:@"%s",sqlite3_column_text(statement, 3)];
            object.property = [NSString stringWithFormat:@"%s",sqlite3_column_text(statement, 4)];
            object.property = [NSString stringWithFormat:@"%s",sqlite3_column_text(statement, 5)];
            object.property = sqlite3_column_int(statement, 6);
		}
        sqlite3_finalize(statement);
    }
}

The code above works pretty well when you’re retreiving data from the database, but sometimes doesn’t work if you’re deleting, updating or inserting data. The problem is that the database can be locked when doing multiple statements at the same time, i.e. performing a select to get data then updating a record. I looked around for a framework that would make reading and writing to an SQLite database with a lot less code and handled the interface much easier, what I found was FMDB.

FMDB is an Objective-C wrapper for SQlite. Download the framework from the FMDB Github.

First, you’ll need to integrate the framework into your project.
1. Download the source code from the link above.
2. Add the following files into your project:
– FMDatabase.h
– FMDatabase.m
– FMDatabaseAdditions.h
– FMDatabaseAdditions.m
– FMResultSet.h
– FMResultSet.m
3. Add the “libsqlite3.dylib” framework to your project. To do this, access your target settings, click on “Summary”, scroll down to the “Linked Frameworks and Libraries” group and click the + button to add a new framework to your project. Search for “libsqlite3.dylib”.
4. Include the following headers at the top of your class:
– #import “sqlite3.h”
– #import “FMDatabase.h”
4. Start coding!

Below is some sample code to help you with the basic SQLite commands.

Create Table

NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [docPaths objectAtIndex:0];
NSString *dbPath = [documentsDir   stringByAppendingPathComponent:@"UserDatabase.sqlite"];

FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
[database executeUpdate:@"CREATE TABLE tablename (id INTEGER  PRIMARY KEY DEFAULT NULL,field1Name INTEGER DEFAULT NULL,field2Name INTEGER DEFAULT NULL,field3Name INTEGER DEFAULT NULL,field4Name TEXT DEFAULT NULL)"];
[database close];

The database is being created and stored in the documents directory. You can only create, update, delete and insert data into a database in your applications documents directory in the iOS. It’s the only location that allows you to edit the database. If you wanted to only access data and not modify it, you could store your database in the resources of your project. If you are starting with an existing database that you created, you need to copy the database from your resources directory to the application documents directory. I usually create my SQlite database using MesaSQLite. It’s a free GUI for the MAC.
DAAC42816E4E3328F09A0C088016B212F5BDB336C7B44_500_420

Retrieve Data From Table

NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [docPaths objectAtIndex:0];
NSString *dbPath = [documentsDir   stringByAppendingPathComponent:@"UserDatabase.sqlite"];

FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
FMResultSet *results = [database executeQuery:@"SELECT * FROM tableName"];
// Or with variables FMResultSet *results = [database executeQuery:@"SELECT * from tableName where fieldName= ?",[NSString stringWithFormat:@"%@", variableName]];
while([results next]) {
    ClassName *objectName	= [[ClassName alloc] init];
    objectName.propertyName = [results intForColumn:@"id"];
    objectName.propertyName = [results stringForColumn:@"field1Name"];
    objectName.propertyName = [results stringForColumn:@"field2Name"];
    objectName.propertyName = [results stringForColumn:@"field3Name"];
    objectName.propertyName = [results stringForColumn:@"field4Name"];
    objectName.propertyName = [results stringForColumn:@"field5Name"];
}
[database close];

It’s nice that FMDB makes you actually put in the table field name, this makes it much easier to ensure you are pulling the correct field. In the old way you’re pulling the fields in order. Be sure that you are using the correct type, FMDB can retrieve data in the following types:
-intForColumn:
-longForColumn:
-longLongIntForColumn:
-boolForColumn:
-doubleForColumn:
-stringForColumn:
-dateForColumn:
-dataForColumn:
-dataNoCopyForColumn:
-UTF8StringForColumnIndex:
-objectForColumn:

Insert Data into Table

NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [docPaths objectAtIndex:0];
NSString *dbPath = [documentsDir   stringByAppendingPathComponent:@"UserDatabase.sqlite"];

FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
[database executeUpdate:@"INSERT INTO tableName (fieldName, fieldName, fieldName, fieldName) VALUES (?, ?, ?, ?)", [NSNumber numberWithLong:fieldVariable], [NSNumber numberWithLong:fieldVariable], [NSString stringWithFormat:@"%@", fieldVariable], [NSNumber numberWithInt:fieldVariable], nil];
[database close];

All arguments provided to the -executeUpdate: method (or any of the variants that accept a va_list as a parameter) must be objects.The code above is insertng three NSNumbers and one NSString. If you didn’t convert your variables into an object the method will result in a crash or locking up your app.

Update Record in Table

NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [docPaths objectAtIndex:0];
NSString *dbPath = [documentsDir   stringByAppendingPathComponent:@"UserDatabase.sqlite"];

FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
[database executeUpdate:@"UPDATE tableName set fieldName= ? where fieldName= ?", [NSNumber numberWithLong:fieldVariable], [NSString stringWithFormat:@"%@", fieldVariable], nil];
[database close];

Delete Record in Table

NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *dbPath = [documentsDir   stringByAppendingPathComponent:@"UserDatabase.sqlite"];

FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
[database executeUpdate:@"DELETE FROM tableName WHERE fieldName= ? and fieldName= ?", [NSString stringWithFormat:@"%@", fieldVariable], [NSNumber numberWithLong:fieldVariable], nil];
[database close];

The code above should give you a good start on using SQLite databases using the FMDB framework in your app. If you want more information about FMDB including some advanced features including FMDatabaseQueue using Threads read the documentation on the FMDB Github.

March 5, 2013 Frameworksframework, objective-c
Page 3 of 4«1234»
Recent Posts
  • Classix for iPhone, iPad & Apple TV
  • Tutorial: How to test your app for IPv6 compatibility
  • Tutorial: Testing SSL using Charles Proxy on an iOS Device
  • Tutorial: 3D Touch – Quick Actions in Swift
  • tvOS Tutorial: Top Shelf in Swift
Featured Apps
Classix
Sportsnet
TAGS
tutorialswiftios8iosobjective-cvideogamesstrategynewsframeworkappsmonitizefacebookwatchappleios7toolstvosios9apiprovisionsocialtutorialsbooksdesignbookiapIPv6iTunes Connect
Search
TAGS
tutorialswiftios8iosobjective-cvideogamesstrategynewsframeworkappsmonitizefacebookwatchappleios7toolstvosios9apiprovisionsocialtutorialsbooksdesignbookiapIPv6iTunes Connect
ABOUT
Brian is a Lead iOS/tvOS Developer from Toronto with over 18 years of multifaceted experience including development, design, business analysis and project management.

FOLLOW ME
    
Email Subscription
Sign up for my newsletter to receive the latest news and tutorials posted.

Enter your email address:

2023 © Brian Coleman