Wednesday, June 24, 2015

iOS Development : Using Custom Fonts



Using custom fonts on iOS is very simple, but there are few catches to watch out. This post provides details on using custom fonts on iOS and what are all precaution required to take while using them.


Adding fonts to project


Before adding...


a. Check file format

Only .ttf and .otf formats are supported as of now.



b. Is font file valid ?

Double click font file on OS X, it should open popup for font installation, click install. If it gets installed properly it will open OS X default font application named "Font Book" with your font listed in it. Most of the fonts which get installed on OS X also work fine on iOS.

So what happens when font file is invalid ?

When you double click and click the install button on a popup, this dialog comes up.




And if you checked "Select all fonts" and then clicked a button "Install Checked" even after that your font will not get installed. You will not see it inside "Font Book" either. If there is only warning of the duplicate font then there is no issue, the font already exists. For duplicate font files, you can install that font and then go to "Edit >  Look for Enabled Duplicate" to get existing font name for the same file.There is one more way to check font validation programmatically, which is explained in further point.


c. License

The license can usually be found with your font download or on the site where you bought/downloaded it. Taking a minute to check it ensures you won’t get into legal trouble down the road. Some fonts are sold through originals sites only. So, any site that wants to sell it to you for cheap or free is not legit.



Adding font files...



a. Copying to project

There is no different process for adding to the project, just drag-drop files to your project or put them in some folder for maintaining project file structure. 

b. Updating .plist 

Now go to project property list file (.plist), add key "Fonts provided by application", value for this key is of Type array add font file names as array items.



c. Bundle resource

These fonts must go along with bundle resource. To check, select project file > Select appropriate target of project > Select tab "Build Phases"  > Expand "Copy Bundle Resources". Make sure fonts you added are listed here, if not add them.

What is a name of the font ?

The tricky part, most of the times font file names are not actual font name. 


ways to check font name...


1. Open font file in default font application "Font Book" available on OS X. This will provide font name along with many details about the font.

2. After adding font to your project, file names in .plist and copying them as bundle resource

Execute the following code, this will enlist all fonts.

Check your console and use name coming in console line "Actual font name : ". This is one more way to validate font file. If font name appears in this list, then font file is valid.


NOTE : This check is possible only if your font file has some name close to the actual font, otherwise you will have to add fonts one by one, copy console log before and after adding the font to some text file. Use file merge application available on mac to check the difference in two files. 



Using custom fonts

You can use the font with name method.

    UIFont *font = [UIFont fontWithName:@"CharlotteSansMediumPlain" size:13];


Providing the incorrect name and using it will either crash an application or will show default Helvetica font, so double check by adding breakpoints / console logs if the font is not nil.

Tuesday, June 9, 2015

iOS Development : App Extension - Creating Custom Keyboard




#Goal



1. Create custom keyboard using iOS 8 App extension, use xib to render keyboard view.
2. Show two buttons "¯\_(ツ)_/¯" & "(⌐■_■)" over it, clicking on them will add respective text on textviews.




#Source







#Steps




STEP 1: 

Create single view application using objective-c.



STEP 2: 

Add new scheme : Application Extension > Custom Keyboard










STEP 3: 

Name the keyboard extension as "ADKeyboard", and click finish.





 STEP 4:

Just cancel activate "ADKeyboard" scheme, as we are not going to build this scheme right now.




STEP 5:

You will see new three new files added for extension view controller .h, .m and .plist for configuration. KeyboardViewController is default name used for keyboard view's controller file.




STEP 6: 

Check .plist it contains many important configuration settings, including setting view controller file name for keyboard view ( by default it is "KeyboardViewController" ).  





STEP 7: 

Go to Main.storyboard, there should be only single view as app was created single view app, add textView to this view, set 0 margins on all four side just to use keyboard inside application.


STEP 8:

Set some background colour to recognise added text view. 


STEP 9:

Run the application, wait for minute. ( When you run for first time, iOS shows this new keyboard option in settings. It is observed that sometimes adding option to settings takes time. )

NOTE : While running on simulator make sure you are using simulator keyboard and not your hardware keyboard ( XCode 6 comes with default option hardware keyboard enabled, so you will not see simulator keyboard when typing ). 
To enable simulator keyboard, Select simulator, Go To Hardware > Keyboard > Uncheck "Connect Hardware Keyboard". 


STEP 10: 

Now to go iOS Settings > General > Keyboard > Keyboards > Add New Keyboard






 You will see your keyboard listed under THIRD-PARTY KEYBOARD, select that





Once selected, you will see keyboard listed under enabled keyboard list.




STEP 11: 

Go to running application try to type something in TextView. This will show default top priority keyboard.


STEP 12: 

Now long press on Globe key on keyboard ( In some cases you will see smiley key instead of globe. Just click smiley key and then you will see Globe key ).





 STEP 13: 

Select your custom keyboard.





STEP 14:

This is how default custom keyboard looks like, just one button provided to move to next keyboard.




STEP 15: 

Now go to "KeyboardViewController", this is view controller for keyboard view and this is where we are going to code.  Remove all code related to "nextKeyboardButtoni.e 

a. Variable declaration 
b. all code in viewDidLoad 
c. title color code from textDidChange


STEP 16: 

Lets start customising keyboard by using .xib

Add new .xib named "KeyboardView" under same directory where "KeyboardViewController" exists.


STEP 17: 

Now changed its size to 320 x 216.


STEP 18:

Change its background colour to dark grey to recognise view.


STEP 19: 

Set .xib's custom class as "KeyboardViewController".


STEP 20:

 Now go to "KeyboardViewController" and add these 3 lines in viewDidLoad after [super viewDidLoad].

    UINib *viewFile = [UINib nibWithNibName:@"KeyboardView" bundle:nil];//xib instance
    NSArray *arrViews = [viewFile instantiateWithOwner:self options:nil];//array of view
    self.view = [arrViews objectAtIndex:0];//Top view

This will set .xib as its view.


STEP 21:

Run application, app should look similar to this with text view and keyboard background having respective background colour set.


STEP 22:

Now go to keyboard .xib file, add two buttons at centre with title as " ¯\_(ツ)_/¯" & "(⌐■_■)". 

As we have already set view controller for this xib as "KeyboardViewController" we can directly connect IBAction for buttons in viewController file. Set same IBAction method for both buttons.

Now update IBAction method code for buttons as below

- (IBAction)keyboardKeyPressed:(id)sender
 {    
    UIButton *btn = (UIButton*)sender;
    [self.textDocumentProxy insertText:btn.titleLabel.text];
}

This will take title text of buttons and will add to textDocumentProxy which will show text on TextView.


STEP 23:

 Run the application, click buttons over keyboard, respective texts will get added in textView




#END





Tuesday, June 2, 2015

iOS Development : Core Data



This post explains basics of core data with one sample tutorial application development guidelines.


Before we start, lets first clear some common misconception about core data. 


So what exactly is Core Data ?



  • Core data is not any kind of database, don’t mix up Core Data with database.
  • It was introduced in Mac OS X 10.4 Tiger and iOS with iPhone SDK 3.0. (2009)
  • It is a framework provided by apple, which lets you perform operations on data in the database in an object-oriented way. 
  • Core Data is much more than just a framework to load and save data.



Why should i use Core Data ?





1. Suitable for quick schema setup and modification.



2. Less memory overhead as it uses lazy loading techniques. When you fetch data, actual objects are fetched only when you try to access them ( aka "faulting").


3. Model Editor Interface : You can use this cool editor interface provided inside Xcode itself to setup schema. Create tables, setup relationships, writing queries and much more..

4. Less code

  •  No need to write create table queries.
  •  No need to write programmatic fetch queries.
  •  Fetched data is already in the form of model classes you need, plus you can save data using model classes directly. So layer which performs fetching and converting data into required model class is removed.

5. Easy to migrate to new schema, if you have proper initial code setup then you can perform light weight migration without writing single line of code.

6. Easy to visualise schema using Model Editor Interface




Basics of Core Data



The learning curve for Core Data is quite large. The goal of this post is to get you up to speed with the basics of Core Data quickly by very short description and small tutorial application.


The Stack


Traditional setup :


To create database and perform any operations on it you need,

1. Schema / design for your database
2. Database connection / handle
3. Custom layer providing methods to interact with database, which performs operations to provide you data received from DB in required model format, also saves your model objects into database in required format.


Core Data setup :


Core data provides similar stack with some complex looking terms at first glance,


1. Managed Object Model

This is your schema, you can design it inside Xcode itself, Xcode provides cool interface for it.
It removes overhead of creating tables programmatically. Apart from just setting up tables and relationships it also provides many other features.
File extension for this is .xcdatamodeld.


2. Persistent Store Coordinator

This is something similar to database connection / handle.


3. Managed Object Context 

Think of it as a “scratch pad” containing objects that interacts with data in persistent store. Its job is to manage objects created and returned using Core Data.




Tutorial


#Download :

Link Source Download : Core Data Tutorial


#Goal :

Create simple Contact table with attributes 
a. name 
b. phone number
c. email

Programatically add some contacts.

Fetch added contacts and print on console.



#Steps :

0. Create single view application, all coding should be performed in ViewController.m file


1. Import Core Data Framework header


#import <CoreData/CoreData.h>


2.  Declare core data stack essential properties


@property (strong, nonatomic) NSManagedObjectContext        *managedObjectContext;

@property (strong, nonatomic) NSManagedObjectModel          *managedObjectModel;

@property (strong, nonatomic) NSPersistentStoreCoordinator  *persistentStoreCoordinator;


3. Copy all following methods in pragmas "Core Data Stack & Core Data Save" as it is, these are common methods required work with core data. Each method has comments for code understanding purpose.




#pragma mark - Core Data Stack

- (NSManagedObjectModel *)managedObjectModel
{
    //1. Create only if nil
    if (_managedObjectModel != nil)
        return _managedObjectModel;
    
    //2. Set schema file name ( .xcdatamodeld file)
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CoreData" withExtension:@"momd"];
    
    //3. Initialize managedObjectModel with url
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    //1. Create only if nil
    if (_persistentStoreCoordinator != nil)
        return _persistentStoreCoordinator;
    
    //2. Init with ManagedObjectModel
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    
    //3. Create .sqlite file path url
    NSURL *documentDirPath = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    NSURL *storeURL = [documentDirPath URLByAppendingPathComponent:@"CoreData.sqlite"];
    
    //4. Add SQLLite as persistent store using .sqlite file url
    NSError *error = nil;
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
    {
        NSLog(@"Failed to initialize persistent store");
    }
    
    return _persistentStoreCoordinator;
}


- (NSManagedObjectContext *)managedObjectContext
{
    //1. Create only if nil
    if (_managedObjectContext != nil)
        return _managedObjectContext;
    
    //2. Get store coordinator
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (!coordinator)
        return nil;
    
    //3. Initialize managedObjectContext & set perstent store for managedObjectContext
    _managedObjectContext = [[NSManagedObjectContext alloc] init];
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    
    return _managedObjectContext;
}

#pragma mark - Core Data Save

- (void)saveContext
{
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    
    if (managedObjectContext != nil)
    {
        NSError *error = nil;
        
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])//save only if there are changes by checking "hasChanges"
        {
            NSLog(@"Failed to save context data");
        }
    }


}

4. Setup Schema using Model Editor Interface


4a. Add new file to project

    add new "Data Model" file from "Core Data" group name it as "CoreData".
    IMPORTANT : 
    No other name will work as we have set this name in code in method managedObjectModel.




4b. This is how empty model file looks..




4c. Adding "Contact" entity and "name" attribute



4d. Use steps provided in 4c. image to add remaining attributes "email" and "phoneNumber". This is how final schema will look..




5. Create "Contact" class as subclass of NSManagedObject using Model Editor Interface
   Select entity "Contact" > Go to Editor > Create NSManagedObject Subclass

   This will add Contact.h & Contact.m file to project with attributes provided in schema as property names with appropriate data types.




6.  Import NSManagedObject subclass "Contact" to "ViewController.m"

#import "Contact.h"

7. Copy methods addContacts and fetchContacts as it is in code..


#pragma mark - Add and Fetch Contacts

-(void)addContacts
{
    //1. Get managedObjectContext
    NSManagedObjectContext *context = [self managedObjectContext];
    
    //2. Create Entity with name and context
    Contact *contact1 = [NSEntityDescription
                         insertNewObjectForEntityForName:@"Contact"
                         inManagedObjectContext:context];
    
    //3. Set attribute values
    contact1.name = @"aaaa";
    contact1.email = @"aaaa@aaaa.com";
    contact1.phoneNumber = [NSNumber numberWithInt:1111];
    
    Contact *contact2 = [NSEntityDescription
                         insertNewObjectForEntityForName:@"Contact"
                         inManagedObjectContext:context];
    
    contact2.name = @"bbbb";
    contact2.email = @"bbbb@bbbb.com";
    contact2.phoneNumber = [NSNumber numberWithInt:2222];
    
    Contact *contact3 = [NSEntityDescription
                         insertNewObjectForEntityForName:@"Contact"
                         inManagedObjectContext:context];
    
    contact3.name = @"cccc";
    contact3.email = @"cccc@cccc.com";
    contact3.phoneNumber = [NSNumber numberWithInt:3333];
    
    //4. Save attributes by saving context
    NSError *error;
    if (![context save:&error])
    {
        NSLog(@"Failed to save: %@", [error localizedDescription]);
    }
}

-(void)fetchContacts
{
    //1. Get entity by using name and context
    NSManagedObjectContext *context = [self managedObjectContext];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Contact"
                                              inManagedObjectContext:context];
    
    //2. Create fetch request by setting entity
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [fetchRequest setEntity:entity];
    
    //3. Fetch objects
    NSError *error;
    NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
    
    //4. Print fetched objects
    for (Contact *contact in fetchedObjects)
    {
        NSLog(@"Name: %@", contact.name);
        NSLog(@"Email: %@", contact.email);
        NSLog(@"Phone: %@", contact.phoneNumber);
        NSLog(@"----------------------");
    }
}



8. Call methods addContacts and then fetchContacts from viewDidLoad


    [self addContacts];
    [self fetchContacts];


9. RUN ( Delete and Run, otherwise same contact will appear twice on second run )



10. Check console for output


/*
 Expected output

 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Name: aaaa
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Email: aaaa@aaaa.com
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Phone: 1111
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] ----------------------
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Name: bbbb
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Email: bbbb@bbbb.com
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Phone: 2222
 2015-06-03 02:01:41.444 CoreDataTutorial[2861:26823] ----------------------
 2015-06-03 02:01:41.444 CoreDataTutorial[2861:26823] Name: cccc
 2015-06-03 02:01:41.444 CoreDataTutorial[2861:26823] Email: cccc@cccc.com
 2015-06-03 02:01:41.444 CoreDataTutorial[2861:26823] Phone: 3333
 2015-06-03 02:01:41.444 CoreDataTutorial[2861:26823] ----------------------

 */



What should you learn next ?


As learning curve is huge, listing some pointers for important topics...

1. Relationship setup by using multiple tables (entities)
2. NSFetchResultController, Binding your data from database with some view ( showing subset of fields from DB on view ), any operation on database e.g update, add, remove etc reflects data on view automatically.
3. Using Model Editor Interface for writing queries
4. Light weight migration
5. Debugging Select the ‘Run’ scheme and select the ‘Arguments’ tab. Add the following argument: “-com.apple.CoreData.SQLDebug 1”.
6. Common errors and crash resolving
7. Working with multiple threads  
8. Faulting