Skip to content
This repository was archived by the owner on Mar 23, 2021. It is now read-only.

Cookbook

dingbat edited this page Jul 23, 2012 · 7 revisions

Class setup

Non-standard property types

Sending & retrieving properties

Relationships & nesting

@implementation User
     
+ (NSString *) remoteModelName
{
  return @"subscriber";
}

+ (NSString *) remoteControllerName
{
  return @"subscriberz";
}

@end
@interface MyClass : NSRRemoteObject

@property (nonatomic, strong) NSArray *csvArray;  //on the server this is a comma-separated string

@end
@implementation MyClass
@synthesize csvArray;

- (id) encodeValueForProperty:(NSString *)property remoteKey:(NSString **)remoteKey
{
   //return the array as a comma-separated string to send out to remote
   if ([property isEqualToString:@"csvArray"])
      return [csvArray componentsJoinedByString:@","];
 
   return [super encodeValueForProperty:property];
}

- (void) decodeRemoteValue:(id)remoteObject forRemoteKey:(NSString *)remoteKey
{
   if ([remoteKey isEqualToString:@"csv_array"])
   {
      //set our array from the "array string"
      self.csvArray = [remoteObject componentsSeparatedByString:@","];
   }
   else
   {
      [super decodeRemoteValue:remoteObject forRemoteKey:remoteKey];
   }
}
@end
@interface MyClass : NSRRemoteObject

@property (nonatomic, strong) NSURL *URL;   //on the server this is just a plain string

@end
@implementation MyClass
@synthesize URL;

- (id) encodeValueForProperty:(NSString *)property remoteKey:(NSString **)remoteKey
{
   //return the URL as a string to send out to remote
   if ([property isEqualToString:@"URL"])
      return [URL absoluteString];
 
   return [super encodeValueForProperty:property];
}

- (void) decodeRemoteValue:(id)remoteObject forRemoteKey:(NSString *)remoteKey
{
   if ([remoteKey isEqualToString:@"url"])
   {
      self.URL = [NSURL URLWithString:remoteObject];
   }
   else
   {
      [super decodeRemoteValue:remoteObject forRemoteKey:remoteKey];
   }
}
@end
@implementation User
@synthesize objcProperty;

- (NSString *) propertyForRemoteKey:(NSString *)remoteKey
{
   if ([remoteKey isEqualToString:@"rails_property"])
      return @"objcProperty";
 
   return [super propertyForRemoteKey:remoteKey];
}

- (id) encodeValueForProperty:(NSString *)property remoteKey:(NSString **)remoteKey
{
   if ([property isEqualToString:@"objcProperty"])
      *remoteKey = @"rails_property";
  
   return [super encodeValueForProperty:property remoteKey:remoteKey];
}

@end

Ruby implementation of encodeValueForProperty:remoteKey::

def encodeValueForProperty(property, remoteKey:key)
  if (property == "objcProperty")
    key[0] = "rails_property"
  end

  super
end
@implementation User

- (NSMutableArray *) remoteProperties
{
   NSMutableArray *props = [super remoteProperties];
   [props addObject:@"uniqueDeviceID"];
   return props;
}

- (id) encodeValueForProperty:(NSString *)property remoteKey:(NSString **)remoteKey
{
   if ([property isEqualToString:@"uniqueDeviceID"])
      return [[UIDevice currentDevice] uniqueIdentifier];
 
   return [super encodeValueForProperty:property remoteKey:remoteKey];
}

//Ignore it if your server sends it back...
- (void) decodeRemoteValue:(id)remoteObject forRemoteKey:(NSString *)remoteKey
{
    if (![remoteKey isEqualToString:@"unique_device_id"])
       [super decodeRemoteValue:remoteObject forRemoteKey:remoteKey];
}

@end
@implementation User
@synthesize retrieveOnlyProperty;

- (BOOL) shouldSendProperty:(NSString *)property whenNested:(BOOL)nested
{
  if ([property isEqualToString:@"retrieveOnlyProperty"])
     return NO;

  return [super shouldSendProperty:property whenNested:nested];
}

@end
@implementation User
@synthesize totallyLocal;

- (NSMutableArray *) remoteProperties
{
   NSMutableArray *props = [super remoteProperties];
   [props removeObject:@"totallyLocal"];
   return props;
}

@end
@implementation User
@synthesize retrieveOnlyProperty;

- (BOOL) shouldSendProperty:(NSString *)property whenNested:(BOOL)nested
{
  if ([self nestedClassForProperty:property])
     return NO;

  return [super shouldSendProperty:property whenNested:nested];
}

@end

Deep-nesting refers to sending a nested attribute when the object itself is nested. In other words, let's say a User is being sent. A User has many Posts, and a Post has many Responses. When the User is sent, all of its Posts will be nested as well. However, by default, this is as far as the representation will go - it is only shallow. If you want the posts to also include their nested properties (responses), shouldSendProperty:whenNested: has to be overridden.

@implementation Post
@synthesize user, responses;

- (Class) nestedClassForProperty:(NSString *)property
{
  if ([property isEqualToString:@"responses"])
    return [Post class];

  return [super nestedClassForProperty:property];
}

- (BOOL) shouldSendProperty:(NSString *)property whenNested:(BOOL)nested
{
  if ([property isEqualToString:@"responses"] && nested)
    return YES;

  return [super shouldSendProperty:property whenNested:nested];
}

@end

The reason nested objects only send shallow representations is to preempt infinite loops that could occur when a two-way relationship exists (User has many Post, so send the Post, which belongs to User, so send the User, so send the Post, so send the User, etc etc).

Nothing needed! If you have a to-one relationship in your class,

@interface Employee : NSRRemoteObject

@property (nonatomic, strong) Boss *boss;

@end

NSRails will automatically assume that an "Employee has one Boss". (It's in the base class implementation of nestedClassForProperty: to just return the property type if it is an NSRRemoteObject subclass.)

  • If, on your server, this class holds the foreign key (ie, if Employee has boss_id on the server and you want to send boss_id instead of boss_attributes) it requires a method override. See belongs-to.

  • Make sure your Rails controller includes boss when sending the JSON for Employee:

render :json => @employee.to_json(:include => [:boss])
  • And if you want to be able to send Boss attributes when updating your Employee, add this to your model:
class Employee < ActiveRecord::Base
  has_one :boss
  accepts_nested_attributes_for :boss, :allow_destroy => true
end
@implementation User
@synthesize posts;

- (Class) nestedClassForProperty:(NSString *)property
{
  if ([property isEqualToString:@"posts"])
    return [Post class];

  return [super nestedClassForProperty:property];
}

@end
  • Make sure your Rails controller includes posts when sending the JSON for User:
render :json => @user.to_json(:include => [:posts])
  • And if you want to be able to send Post attributes when updating your User, add this to your model:
class User < ActiveRecord::Base
  has_many :posts
  accepts_nested_attributes_for :posts, :allow_destroy => true
end

By default, when sending a nested object, NSRails will send the object's dictionary in a key like <property>_attributes. However, this can't be done if your model is defined on your Rails server as belongs-to (ie, it has a foreign key - <property>_id). In this case, return YES for shouldOnlySendIDKeyForNestedObjectProperty::

@implementation User
@synthesize group;

- (BOOL) shouldOnlySendIDKeyForNestedObjectProperty:(NSString *)property
{
   return [property isEqualToString:"group"];
}

@end
  • When sending "group", NSRails will now send only its ID in a group_id key instead of its contents in a group_attributes key
  • This allows you to have a real Group object as a property (instead of just a foreign key integer), but still send it as a foreign key
  • Warning: Of course, this means that modifications on the user.group object will not be updated to the server when calling [user remoteUpdate:] since the property will be replaced with group_id. It can be argued that modifications to the user.group object should be called with [user.group remoteUpdate:] anyway.