Commit 672c450d authored by Alf Watt's avatar Alf Watt

First!

parents
# osx noise
.DS_Store
profile
# xcode noise
build/*
*.mode1
*.mode1v3
*.mode2v3
*.perspective
*.perspectivev3
*.pbxuser
*.xcworkspace
xcuserdata
# svn & cvs
.svn
CVS
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>net.istumbler.labs.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2015 iStumbler. All rights reserved.</string>
<key>ILLiveBundleURLKey</key>
<string>https://istumbler.net/bundles/</string>
</dict>
</plist>
#import <LiveBundle/NSBundle+LiveBundle.h>
// TODO: <NSDictionary+LiveBundle.h> for plists loaded via livePlistFor
// TODO: <NSImage+LiveBundle.h> for images loaded via liveImageFor
/* Copyright 2015, Alf Watt (alf@istumbler.net) All rights reserved. */
#import <Foundation/Foundation.h>
/** @const the base URL for this live bundle, which will be checked for updates of live files */
extern NSString* const ILLiveBundleURLKey;
/** @const notification sent when a given plugin resource has been updated */
extern NSString* const ILLiveBundleResourceUpdateNote;
extern NSString* const NSBundlePlistType;
@interface NSBundle (LiveBundle) <NSURLDownloadDelegate>
/** @returns the NSBundle in the current application with the named resource of the type provided */
+ (NSBundle*) bundleWithResource:(NSString*) name ofType:(NSString*) extension;
/** @returns the NSBundle of a Framework in the current applicaiton with the named resource of the type provided */
+ (NSBundle*) frameworkWithResource:(NSString*) name ofType:(NSString*) extension;
/** @returns the local path for the live bundle in the user's Application Support directory */
- (NSString*) liveBundlePath;
/** @returns the temp path for downloading a particular URL */
- (NSString*) tempPathForResourceURL:(NSURL*) download;
/** @returns the remote URL for the resource specified */
- (NSURL*) liveURLForResource:(NSString*) resource ofType:(NSString*) type;
/** @returns the live path for the remote URL specified */
- (NSString*) livePathForResourceURL:(NSURL*) download;
/** @returns the live path for the resource specified, and initiates the check process
! NB: only call this method once per launch per resource to prevent exessive network traffic !
Map a 'static' filename from the Resources directory of the bundle and place a link in the in the user's library folder,
then check ILLiveBundleURLKey for an updated version of the resource, download it to the live path and notify any
listeners with ILLiveBundleResourceUpdateNote if a new version is avaliable.
The updatede version of the resource is cahced locally and will be loaded preferentially on the next app load.
e.g. for an example app with the ILLiveBundleURLKey of: https://example.com/livebundle
Example.app/Contents/Resources/example.plist
-> ~/Library/Application Support/LiveBundles/com.example.app/example.plist
-> https://example.com/livebundle/example.plist
*/
- (NSString*) livePathForResource:(NSString*) resrouces ofType:(NSString*) type;
@end
This diff is collapsed.
#import <Foundation/Foundation.h>
/**
Category on NSDate to support rfc1123 formatted date strings.
http://blog.mro.name/2009/08/nsdateformatter-http-header/ and
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
*/
@interface NSDate (NSDateRFC1123)
/**
Convert a RFC1123 'Full-Date' string
(http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1)
into NSDate.
*/
+ (NSDate*) dateFromRFC1123:(NSString*) value;
/**
Convert NSDate into a RFC1123 'Full-Date' string
(http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1).
*/
- (NSString*) rfc1123String;
@end
#import "NSDate+RFC1123.h"
@implementation NSDate (NSDateRFC1123)
+ (NSDate*) dateFromRFC1123:(NSString*)value
{
if(value == nil)
return nil;
static NSDateFormatter *rfc1123 = nil;
if(rfc1123 == nil)
{
rfc1123 = [[NSDateFormatter alloc] init];
rfc1123.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
rfc1123.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
rfc1123.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss z";
}
NSDate *ret = [rfc1123 dateFromString:value];
if(ret != nil)
return ret;
static NSDateFormatter *rfc850 = nil;
if(rfc850 == nil)
{
rfc850 = [[NSDateFormatter alloc] init];
rfc850.locale = rfc1123.locale;
rfc850.timeZone = rfc1123.timeZone;
rfc850.dateFormat = @"EEEE',' dd'-'MMM'-'yy HH':'mm':'ss z";
}
ret = [rfc850 dateFromString:value];
if(ret != nil)
return ret;
static NSDateFormatter *asctime = nil;
if(asctime == nil)
{
asctime = [[NSDateFormatter alloc] init];
asctime.locale = rfc1123.locale;
asctime.timeZone = rfc1123.timeZone;
asctime.dateFormat = @"EEE MMM d HH':'mm':'ss yyyy";
}
return [asctime dateFromString:value];
}
-(NSString*)rfc1123String
{
static NSDateFormatter *df = nil;
if(df == nil)
{
df = [[NSDateFormatter alloc] init];
df.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
df.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
df.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
}
return [df stringFromDate:self];
}
@end
# LiveBundle
## Keep your Resources Fresh
NSBundle category to provide dynamic updating of resources from your web server.
Let's say you have a resource, any resource will do, but for e.g. this product_table.plist which displays
a lis of your companies products in a custom view inside MyApplication:
MyApplication.app/Contents/Resources/product_table.plist
And let's say you want to discontinue a product, or change a price, but don't want to release
a new version of your app or write a web server that understand your product line and hosts an API.
LiveBundle provides infrastructure for hosting bundle resources on your web server at a URL that you specify,
so that your app can check to see if there is an updated version of the resource avaliable for download:
https://exaple.com/support/livebundle/com.example.myapplication/products_table.plist
LiveBundle makes a If-Modified-Since request to your server when the resource is requested. It then downloads
the resource, and copies it into the users's library folder:
~/Library/Application Support/MyApplication/LiveBundle/com.example.myapplication/products_table.plist
And that's the path that your code sees when it asks for the resource:
NSString* live_path = [[NSBundle mainBundle] livePathForResource:@"products_table" of type:@"plist"];
NSDictionary* products_table = [NSDictionary dictionaryWithContentsOfFile:livePath];
And that's the object that you can subscribe to be notified for updates to (or just watch the file path yourelf):
[[NSNotificationCenter defaultCenter] addObserverForName:ILLiveBundleResourceUpdateNote
object:live_path
queue:nil
usingBlock:^(NSNotification *note)
{
NSLog(@"%@ was updated!", [note object]);
products_table = [NSDictionary dictionaryWithContentsOfFile:livePath];
// update the ui...
}];
This works well for data that changes on a weekly or monthly basis, the app should only check a URL once per launch.
## Using LiveBundle in your App
- include the `LiveBundle.framework` in your applications `Resources/Frameworks` directory
- link the `LiveBundle.framework` to all the targets which produce bundles you would like to update
- add a key to the `Info.plist` of all the `NSBundle`s you want to update pionting to the web server
- `ILLiveBundleURLKey` is the base url for the bundles resources
- e.g. `https://example.com/livebundle/`
- when calling `livePathForResource:` the URL is prepended to the resource requested
- e.g. `livePathForResource:@"resource.plist"` is `https://example.com/livebundle/resource.plist`
- add a UI element to disable LiveBundle if the user doesn't want updates:
- bind the element's value to the `NSUserDefaults` key: `ILLiveBundleDisableUpdates`
- checkboxes work well if you use `NSNegateBoolean` with the phrase `Automatically Update Resources on Startup`
- it will be selected by default, and the user disabling will set the value to YES
- deploy resources to your web server and test before you ship!
## License
The MIT License (MIT)
Copyright (c) 2015 Alf Watt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment