FatFractal customer forums



Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - gkc

Pages: [1]
1
NoServer Questions and Answers / Release 1.4.2 iOS 8
« on: September 10, 2014, 04:12:53 PM »
New release 1.4.2 http://fatfractal.com/downloads : includes fix for serialization bug in iOS SDK which appeared with iOS 8 http://fatfractal.com/documentation/#document-release-notes-1-4-2

2
NoServer Questions and Answers / Oops
« on: March 30, 2014, 10:09:15 AM »
I made a couple of unstable releases of the iOS SDK in the past 24 hours ...

If you've downloaded and are using FF_IOS_SDK_R1.3.3_R2712 or FF_IOS_SDK_R1.3.3_R2713 please download and use the latest SDK FF_IOS_SDK_R1.3.3_R2714 instead - available either here http://system.fatfractal.com/console/ff/ext/Releases/Latest/FF_IOS_SDK or here https://system.fatfractal.com/repo/artifact/Releases/R1.3.3/FF_IOS_SDK_R1.3.3_R2714.zip

Sorry for any inconvenience caused!


NOTE: Please always check here for latest releases : http://fatfractal.com/downloads

3
As of last weekend's release, the iOS and Android SDKs now provide client-side caching support (for offline behaviours).

I am preparing a blog post about this feature, but I was ill for a couple of days and am still playing catch-up, so thought I'd better post this :-)

Latest downloads are at http://fatfractal.com/support/downloads/

The example below is for IOS but the Android SDK works in pretty much exactly the same way. It's pretty straightforward to use; here's a little test harness which
  • sets up local storage
  • logs in
  • creates some objects
  • Retrieves some objects using a query, & store to cache
  • simulates being offline
  • Issue the query but don't ask to use the cache - should be an error
  • Issue the same query again, asking to use the cache if offline - should work fine

Code: [Select]
#import <SenTestingKit/SenTestingKit.h>
#import <FFEF/FatFractal.h>

static FatFractal * ff;
static id<FFLocalStorage> localStorage;

@interface Basic : NSObject
@property (nonatomic) int anInt;
@end

@implementation Basic
@end

@interface BasicCachingTest : SenTestCase

@end

@implementation BasicCachingTest

- (void)setUp
{
    [super setUp];
   
    if (! ff) {
        ff = [[FatFractal alloc] initWithBaseUrl:@"https://localhost:8443/TestApp"];
        // Do any calls to registerClass:ForClazz: here, BEFORE setting the localStorage property
        // This is essential when one is subclassing FFUser for example

        localStorage = [[FFLocalStorageSQLite alloc] initWithDatabaseKey:@"CachingTests"];
        [localStorage setDebug:NO];
        [localStorage wipeAllData];
       
        ff.localStorage = localStorage;
    }
    ff.debug = NO;
    ff.simulatingOffline = NO;
}

- (void)tearDown
{
    [super tearDown];
}

- (void) pause:(NSTimeInterval)pauseInterval
{
    NSDate* cycle = [NSDate dateWithTimeIntervalSinceNow:pauseInterval];
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                             beforeDate:cycle];
}

- (NSError *) deleteObjsForQuery:(NSString *)query {
    NSError *crudError;
   
    NSArray * objs = [ff getArrayFromUri:query error:&crudError];
   
    if (! crudError) {
        for (id obj in objs) {
            crudError = nil;
            [ff deleteObj:obj error:&crudError];
            STAssertNil(crudError, @"deleteObjectsForQuery %@ : Failed to delete %@", query, obj);
            if (crudError)
                break;
        }
    }
    return crudError;
}

- (void) testBasicCreateAndRetrieve
{
    // We're going to run this all synchronously but use the block methods which would be used in normal code
    __block BOOL blockComplete = NO;
    __block NSError *blockErr;
   
    // Login
    NSLog(@"***testBasicCreateAndRetrieve: logging in");
    [ff loginWithUserName:@"TestUser_1" andPassword:@"TestUser_1" onComplete:^(NSError *theErr, id theObj, NSHTTPURLResponse *theResponse) {
        STAssertNil(theErr, @"Failed to login - error was: %@", [theErr localizedDescription]);
        blockErr = theErr;
        blockComplete = YES;
    }];
    while (!blockComplete) [self pause:0.001];
    if (blockErr) return;
   
    // Delete existing test objects
    NSLog(@"***testBasicCreateAndRetrieve: deleting old test objects");
    NSError *deleteErr = [self deleteObjsForQuery:@"/Basic"];
    STAssertNil(deleteErr, @"Failed to delete existing objects");
    if (deleteErr) return;
   
    // Create 5 objects
    NSLog(@"***testBasicCreateAndRetrieve: creating new test objects");
    for (int i = 1; i <= 5; i++) {
        blockErr = nil;
        blockComplete = NO;
        Basic * basic = [[Basic alloc] init]; basic.anInt = i;
        [ff createObj:basic atUri:@"/Basic" onComplete:^(NSError *theErr, id theObj, NSHTTPURLResponse *theResponse) {
            blockErr = theErr;
            STAssertNil(blockErr, @"Failed to create object - error was: %@", [blockErr localizedDescription]);
            blockComplete = YES;
        }];
        while (!blockComplete) [self pause:0.001];
        if (blockErr) return;
    }
   
    // Retrieve 2 of the objects via a query, and ask for the response to be cached
    NSLog(@"***testBasicCreateAndRetrieve: retrieving objects - caching response");
    blockErr = nil;
    blockComplete = NO;
    [[[ff newReadRequest] prepareGetFromUri:@"/Basic/(anInt between 2 and 3)"] executeAsyncWithOptions:FFReadOptionCacheResponse andBlock:^(FFReadResponse *response)
     {
         blockErr = [response error];
         STAssertNil(blockErr, @"Failed to retrieve objects - error was: %@", [blockErr localizedDescription]);
         blockComplete = YES;
     }];
    while (!blockComplete) [self pause:0.001];
    if (blockErr) return;
   
    // Simulate being offline
    NSLog(@"***testBasicCreateAndRetrieve: simulating offline");
    [ff setSimulatingOffline:YES];
   
    // Issue the query again but don't ask to use the cache - should be an error
    NSLog(@"***testBasicCreateAndRetrieve: retrieving objects while offline - NOT USING cache - should get an error response");
    blockErr = nil;
    blockComplete = NO;
    [[[ff newReadRequest] prepareGetFromUri:@"/Basic/(anInt between 2 and 3)"] executeAsyncWithBlock:^(FFReadResponse *response)
     {
         blockErr = [response error];
         STAssertNotNil(blockErr, @"Should have failed to retrieve objects");
         blockComplete = YES;
     }];
    while (!blockComplete) [self pause:0.001];
    if (! blockErr) return;
   
    // Issue the same query again, asking to use the cache if offline - should work fine
    NSLog(@"***testBasicCreateAndRetrieve: retrieving objects while offline - USING cache");
    blockErr = nil;
    blockComplete = NO;
    [[[ff newReadRequest] prepareGetFromUri:@"/Basic/(anInt between 2 and 3)"] executeAsyncWithOptions:FFReadOptionUseCachedIfOffline andBlock:^(FFReadResponse *response)
     {
         blockErr = [response error];
         STAssertNil(blockErr, @"Failed to retrieve objects from cache - error was: %@", [blockErr localizedDescription]);
         blockComplete = YES;
     }];
    while (!blockComplete) [self pause:0.001];
    if (blockErr) return;
}

@end

FYI, the various read options which you can use (and of course you can use some of them together) are:
Code: [Select]
typedef NS_OPTIONS(NSInteger, FFReadOption) {
    FFReadOptionAutoLoadRefs        = (0x1 << 0), // When retrieving objects, automatically retrieve objects which they reference
    FFReadOptionAutoLoadBlobs       = (0x1 << 1), // When retrieving objects, automatically retrieve any BLOBs they contain
    FFReadOptionCacheResponse       = (0x1 << 2), // When retrieving, cache the response
    FFReadOptionUseCachedOnly       = (0x1 << 3), // When retrieving, ONLY try the cache - i.e. do not hit the network
    FFReadOptionUseCachedIfCached   = (0x1 << 4), // When retrieving, try the cache first - only hit the network if cache is empty
    FFReadOptionUseCachedIfOffline  = (0x1 << 5)  // When retrieving, try the network first - if offline, then try the cache
};

Personally I tend to use FFReadOptionCacheResponse | FFReadOptionUseCachedIfOffline for all of the queries that I wish to have available while offline.

One nice additional benefit - when using localStorage like this, the logged-in user is also cached - so if for example you log in and then the app is terminated, then, when the app restarts and the FatFractal object is created again, it will restore the logged-in user, and loggedIn will be true.

Give it a try, and let us know what you think!

- Gary

Pages: [1]
Copyright © FatFractal customer forums