Skip to content

Instantly share code, notes, and snippets.

@benbahrenburg
Last active August 13, 2016 17:28
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benbahrenburg/6586902 to your computer and use it in GitHub Desktop.
Save benbahrenburg/6586902 to your computer and use it in GitHub Desktop.
This gist outlines an approach for adding the fetch background mode to Titanium

This document details how to add the background mode of fetch into the Titanium SDK. It is important to note that once these updates are performed you will be able to attach a listener to be called when the fetch background mode is triggered.

After the below updates are made, you can clear your Titanium project and run this sample app.js to view how it works.

TiBase.h and TiBase.m updates

The first step in adding the fetch background mode is to create the const values that will later be used when adding or listening to notification center.

To do this first open the TiBase.h file and add the const of

extern NSString * const kTiFetchBackground;

I did this on line 572 right below the kTiLocalNotification value

Then open the TiBase.m file and add the const of

NSString * const kTiFetchBackground = @"TiFetchBackground";

I did this on line 148 right below the kTiLocalNotification value

TiApp.m Updates

The next step in the process is to add the UIApplicationDelegate method performFetchWithCompletionHandler. We only need to add one method in this file, but it is an important one.

To do this open the TiApp.m file and find the applicationWillEnterForeground method on 495. Under this method add the snippet below.


-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{

    int64_t delayInSeconds = 29;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){    
        completionHandler(UIBackgroundFetchResultNewData);
        
    });

    [[NSNotificationCenter defaultCenter] postNotificationName:kTiFetchBackground  object:self];
}

What the above does is send a notification that the fetch method has been called. This will make a call over to the TiAppiOSProxy covered in the next step.

###IMPORTANT### You will notice that we create a 30 second "timer" after we post our notification. Since we don't have anyway to verify that the JavaScript event has been processed we assume it will take the max amount of time allowed by app (30 seconds). Once the timer has elapsed we call completionHandler(UIBackgroundFetchResultNewData).

TiAppiOSProxy.m Update

The final step is to update TiAppiOSProxy.m.

This is really simple. First I add some const values for our properties and to be used in our methods.

int const TiFetchIntervalMin = 0;
int const TiFetchIntervalNever = -1;

Then I just update the _listenerAdded method to allow for the fetch event to be registered.

-(void)_listenerAdded:(NSString*)type count:(int)count
{
  if (count == 1 && [type isEqual:@"notification"])
  {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLocalNotification:) name:kTiLocalNotification object:nil];
  }
    
	if (count == 1 && [type isEqual:@"fetch"])
	{
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveFetchNotification:)
                                                     name:kTiFetchBackground object:nil];
	}
    
}

I then update _listenerRemoved to allow for the user to remove the fetch event.

-(void)_listenerRemoved:(NSString*)type count:(int)count
{
  if (count == 0 && [type isEqual:@"notification"])
	{
		[[NSNotificationCenter defaultCenter] removeObserver:self name:kTiLocalNotification object:nil];
	}
    
	if (count == 0 && [type isEqual:@"fetch"])
	{
		[[NSNotificationCenter defaultCenter] removeObserver:self name:kTiFetchBackground object:nil];
	}
}

Then you want to add the method that fires the listener as shown below.

-(void)didReceiveFetchNotification:(NSNotification*)note
{
  [self fireEvent:@"fetch" withObject:nil];
}

We now can add the method that lets us set the interval. I used the same name as iOS does. You can pass in either one of the property values or a double check the method will then use to create a NSInterval with. To turn this off you pass in the BACKGROUND_FETCH_INTERVAL_NEVER property.

-(void)setMinimumBackgroundFetchInterval:(id)value
{
    ENSURE_SINGLE_ARG(value, NSNumber);
    
    #if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_0
        if([value intValue] == TiFetchIntervalNever){
            [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalNever];
        }else{
            [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:[value doubleValue]];
        }
    #endif
   
}

The very last update is to add the new properties as shown below.

MAKE_SYSTEM_PROP(BACKGROUND_FETCH_INTERVAL_MINIMUM,TiFetchIntervalMin);
MAKE_SYSTEM_PROP(BACKGROUND_FETCH_INTERVAL_NEVER,TiFetchIntervalNever);
@srahim
Copy link

srahim commented Sep 24, 2013

Hey Ben this actually looks exactly what i would also do, to implement this feature. The only thing i would suggest is to conditionally compiling in the iOS7 code or using performselector:withArg: so that the compiler will not complain about the unkown methods if you try compiling the SDK from a older iOS version(i.e. anything lower that 7). Besides that this looks extremely good. Good job Ben. You can go ahead and make that minor change, and set up the PR for it.

NOTE: Do make the changes in master branch (3.2.0) and not on 3.1.2 as that it is already a released branch.

@benbahrenburg
Copy link
Author

@srahim updated the approach per our discussion

@rborn
Copy link

rborn commented Oct 19, 2013

Hey Ben, will this make it in the official SDK ? Thnx :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment