Tuesday, August 5, 2014

Proxy design pattern using core data

Normally Core data models extend from NSManaged objects. So the model objects we use are tightly coupled with core data. What if we need to keep our models independent over core data? Such an approach will be more flexible since the model objects can be easily reused even when we do not want to use core data. We can use protocols to achieve this effect. We can create a protocol and our core data models categories will conform to that protcol. Here is an example This is a message Entity which can be used to represent a chat message
Message.h

#import 
#import 

@interface Message : NSManagedObject

@property (nonatomic, retain) NSDate * date;
@property (nonatomic, retain) NSNumber * dirty;
@property (nonatomic, retain) NSString * entityID;
@property (nonatomic, retain) NSData * resource;
@property (nonatomic, retain) NSString * resourcePath;
@property (nonatomic, retain) NSString * text;
@property (nonatomic, retain) NSNumber * type;
@property (nonatomic, retain) NSNumber * didRecieve;

@end
Message.m
#import "Message.h"

@implementation Message

@dynamic date;
@dynamic dirty;
@dynamic entityID;
@dynamic resource;
@dynamic resourcePath;
@dynamic text;
@dynamic type;
@dynamic didRecieve;

@end
The protocol for generic messages
@protocol PMessage 

-(NSNumber *) type;
-(NSString *) text;
-(NSString *) fontName;
-(NSNumber *) fontSize;
-(NSString *) textColor;
-(UIImage *) textAsImage;
-(BOOL) isMine;
-(NSString *) color;
-(void) setColor:(NSString*)color;
-(NSDate *) date;
-(UIImage *) thumbnail;
-(NSString *) entityID;
-(BThread *) thread;
-(NSNumber*)didRecieve;

@end
this is the header file of the NSManagedObject category which confirms to the protocol
#import "Message.h"
#import "PEntity.h"
#import "PMessage.h"

@interface Message(Additions)

-(BOOL) sameDayAsMessage: (Message *) message;
-(NSComparisonResult) compare: (Message *) message;
-(BOOL) isMine;
-(float) getTextHeightWithFont: (UIFont *) font withWidth: (float) width;
-(UIImage *) textAsImage;
/**
Other presentation specific methods
****/

@end
The implementation of those methods resides within the .m file of the Message category. Through out the code I can refer to the message entity via PMessage protocol. The actual object whether it is a Message entity or some other message entity with some other non-core data storage mechanism, is determined at the run time. Assume you have developed a new storage mechanism that far exceeds the capabilities of core data. Now it is the time for use it. These are the steps you will have to preform
  • Create the model class based on the new storage mechanism.
  • Create a category of the model class based on the new storage mechanism that conforms to PMessage protocol

in the code I am always referring to the PMessage protocol. Because of that despite the actual object which encapsulates the actual data persistence mechanism all the object of PMessage type can be accesed with a uniform interface. For any query, feel free to contact me via my linkedin profile.

Monday, August 4, 2014

iOS date picker with a done button

I had to create a date picker with a done button. I am sharing that experience with this post. These are the steps I used.
  1. Create a UIView and add UIDatePicker to it
  2. Add a done buttton
  3. Add ability to assign a selector to done button. So we can respond to the click event

This is the code for the implementation
DatePicker.h file
//
//  DatePicker.h

#import 

@interface DatePicker : UIView

@property (nonatomic, assign, readonly) UIDatePicker *datePicker;
@property (nonatomic, readwrite) float pickerHeight;

- (void) setDatePickerMode: (UIDatePickerMode) mode;
- (void) addTargetAndSelectorForDoneButton: (id) target action: (SEL) action;

- (id) initWithFrame: (CGRect) frame withHeight:(float)height;

@end


DatePicker.m file

#import "DatePicker.h"

@interface DatePicker ()

@property (nonatomic, readwrite) UIDatePicker *datePicker;
@property (nonatomic, assign) id doneTarget;
@property (nonatomic, assign) SEL doneHandler;

@end

@implementation DatePicker

@synthesize datePicker = _datePicker;
@synthesize pickerHeight = _pickerHeight;
@synthesize doneTarget = _doneTarget;
@synthesize doneHandler = _doneHandler;

- (id) initWithFrame: (CGRect) frame withHeight:(float)height{
    if ((self = [super initWithFrame: frame])) {
        self.backgroundColor = [UIColor clearColor];
        _pickerHeight = height;
        
        UIDatePicker *picker = [[UIDatePicker alloc] initWithFrame: CGRectMake(0, _pickerHeight, frame.size.width, frame.size.height - _pickerHeight)];
        [self addSubview: picker];
        
        UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame: CGRectMake(0, 0, frame.size.width, _pickerHeight)];
        toolbar.barStyle = UIBarStyleBlackOpaque;
        toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
        
        UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle: @"Done" style: UIBarButtonItemStyleBordered target: self action: @selector(donePressed)];
        UIBarButtonItem* flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
        toolbar.items = [NSArray arrayWithObjects:flexibleSpace, doneButton, nil];
        
        [self addSubview: toolbar];
        
        self.datePicker = picker;
        picker.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin;
        
        self.autoresizesSubviews = YES;
        self.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin;
    }
    return self;
}

- (void) setDatePickerMode: (UIDatePickerMode) mode {
    self.datePicker.datePickerMode = mode;
}

- (void) donePressed {
    if (self.doneTarget) {
        [self.doneTarget performSelector:self.doneHandler withObject:nil afterDelay:0];
    }
}

- (void) addTargetAndSelectorForDoneButton: (id) target action: (SEL) action {
    self.doneTarget = target;
    self.doneHandler = action;
}


Thursday, June 5, 2014

Tips for efficient objective c code - I

Before thinking about code optimizations it is better to refresh about how an objective C code would run.
The preprocessor
Before the C compiler the preprocessor takes over. This is invoked automatically by the compiler before the compilation process. Here the preprocesssor modifies your source file. compiler uses this modified file for the compilation process. Preprocessor converts the prepocessor commands in to the meaningful source code. For example consider define command. This is mainly used to define constants. For instance,
If you have
    int no = NUMBER;
prepocessor converts this as follows.
    int no = 100;

#include 
Here the preprocessor pasts the content of header file to the at the location of the #include directive.
Compiler
Next the compiler takes over and converts the source file into an object file. The file ends in .o. This file is not directly executable. It lacks sources code for library functions that were included by #include directive.
Linker
Just like the preprocessor linker is a separate program. What linker does is link together several object files into a single binary executable. Here these several object files means following files
  1. object file created in the compilation process
  2. object files that are pre compiled and collected as library files
Also when we talk about optimization, it is better to know more about objective c. Objective C unlike Java or C++ uses messaging. NOT FUNCTION CALLING. When messaging is used the runtime decides which code to execute. But in function calling it's compiler. If you are familiar with C++, you should know about the virtual table, which is used for run time look ups. It was used to support the polymorphism feature. But in objective c, since we are using messaging, the look up is always at the runtime. In objective c, bulk of the work is done by the runtime. The runtime contains all the data structures for OOP features, memory management functions etc. because of this, unlike other languages, when the runtime is updates and optimized, you app will get more faster.
minimizing header imports
if you want to import something, except it is super class header or a protocol you can use forward declaration. In other words if your header file only needs that the class to be imported exist, always use a forward declaration. And in the .m file use the header file import. As I pointed out if you are using protocols, you need to import the header file anyway. So make sure that the protocol contains only the method needed by the the particular class. If it contains methods that are not currently used by the current class, it is better to split the protocol in to few files.
Read instance variables directly but write them using the property
When you directly access the the variable you can bypass the getter code generated by the compiler. So it is much faster. directly setting the variable without using the property is also the same. But if you are not careful it may lead to bugs that are very hard to debug. Since when you directly access, the code including the memory management code generated by the compiler will be bypassed. For example if your property is declared as copy, if you use the direct access to modify the variable, the variable will not be copied. The new value will be retained and the old value will be released. In addition if you are using KVO, KVO will not be fired in direct access. So for the modifying it is safe not to use direct accessing.
Overiding -(NSUIInteger)hash methods correctly.
This method and -(BOOL)isEqual method plays a very important part when determining equality of two objects. If isEqual is returning true for two objects, it should always return the same value in the hash method. But objects that return the same hash value, may not return true in isEqual. This should be understandable if you are familiar with hashing. If there is a collision different objects goes to the same hash bucket, then we have to use something like double hashing or linear probing to correctly index the item. According to Apple WWDC 2013 Apple uses linear probing in their hashing algorithms. Assume you have a hash method as follows
-(NSUIInteger)hash {
    returns 1345435;
}
This is an absolutely correct implementation and hash code computation is super fast. But there is a huge drawback. If these objects are inserted in to a collection which employes hashing, it will lead to big performance issues. Because hash is used as an index within the hash table that the collection uses. For example assume you are usng a set. And you are using above mentioned hash function. Here set uses the hash function to put each objects in the internal representation. Since the hash code is a constant, all objects will end up in the same hash bucket. So you will not gain any performance gain by hashing. So hard coding the hash is not the best approach. consider the following implementation.
-(NSUInteger)hash {
     NSString *stringHash = [NSString stringWithFormat:@"%@:%@:%i",_firstName, _lastName, _age];
     return [stringHash hash];
 }
Here the implementation is correct since when isEqual return true, hash function will return the same value. But the hash function is slow, since a string is created.
-(NSUInteger)hash {
    NSUInteger nameHash = [_firstName hash];
    NSUInteger lastNameHash = [_lastName hash];
    NSUInteger ageHash = _age;
    
    return nameHash^lastNameHash^ageHash;
 }
This function is much faster than the earlier approach. And have a good range of hashes as well. So this is a better compromise between the speed of the hash algorithm and the range of hashes.

Tuesday, January 7, 2014

Youtube iOS app like rotation for MPMoviePlayerViewController

Recently I had to implement youtube iOS app like rotation for MPMoviePlayerViewController. So the video can be correctly rotated. This is how I did it.
1 Listen for the UIDeviceOrientationDidChangeNotification notification.
2 Rotate the MPMoviePlayerViewController as needed.

Here is the code for rotation
- (void)detectOrientation {
    NSLog(@"handling orientation");
    [self setMoviePlayerViewOrientation:[[UIDevice currentDevice] orientation]];
}

- (void)setMoviePlayerViewOrientation:(UIDeviceOrientation)orientation {
    double height = [UIScreen mainScreen].bounds.size.height;
    //Rotate the view!
    CGFloat degree = 0;
    
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.5];
    switch (orientation) {
        case UIDeviceOrientationPortrait:
        case UIDeviceOrientationPortraitUpsideDown:
            degree = 0;
            moviePlayerController.view.bounds = CGRectMake(0, 0, 320, height);
            break;
        case UIDeviceOrientationLandscapeLeft:
            degree = 90;
            moviePlayerController.view.bounds = CGRectMake(0, 0, height, 320);
            break;
        case UIDeviceOrientationLandscapeRight:
            degree = -90;
            moviePlayerController.view.bounds = CGRectMake(0, 0, height, 320);
            break;
        case UIDeviceOrientationFaceUp:
            if (previousOrientation == UIDeviceOrientationLandscapeRight) {
                degree = -90;
                moviePlayerController.view.bounds = CGRectMake(0, 0, height, 320);
            } else if(previousOrientation == UIDeviceOrientationLandscapeLeft) {
                degree = 90;
                moviePlayerController.view.bounds = CGRectMake(0, 0, height, 320);
            } else if (previousOrientation == UIDeviceOrientationPortraitUpsideDown || previousOrientation == UIDeviceOrientationPortrait) {
                degree = 0;
                moviePlayerController.view.bounds = CGRectMake(0, 0, 320, height);
            }
            break;
        default:
            break;
    }
    //lastOrientation = orientation;
    CGAffineTransform cgCTM = CGAffineTransformMakeRotation((degree) * M_PI / 180);
    moviePlayerController.view.transform = cgCTM;
    [UIView commitAnimations];
    [[UIApplication sharedApplication] setStatusBarOrientation:orientation];
    previousOrientation = orientation;
}

In order fire this method we need to add an observer to the UIDeviceOrientationDidChangeNotification. So inside the viewDidLoad method, the following code need to be added.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(detectOrientation) name:@"UIDeviceOrientationDidChangeNotification" object:nil];

Now you will have a rotation similar to the Youtube iOS app. Happy coding!!!

UPDATE
 As for iOS 8, this is not needed. New SDK handles this by itself!!!! :D :D