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;
}