CocoaPods trunk is moving to be read-only. Read more on the blog, there are 19 months to go.

ResourceManager-smorel 1.2.0

ResourceManager-smorel 1.2.0

TestsTested
LangLanguage Obj-CObjective C
License Custom
ReleasedLast Release Mar 2015

Maintained by Sebastien Morel.



  • By
  • Sebastien Morel

Are you tired of waiting for your app to compile and sync to your device when you just want to update an image or some text? Wouldn’t it be wonderful if you could distribute your application over the air and the copywriter could edit the localization files directly? Imagine if you could give your app to a designer who could tweak the layouts, fonts, margins and colours, without needing an Xcode installation or any knowledge of Objective-C?

With the ResourceManager framework, combined with AppCoreKit and a few lines of code, all this is possible.

How does it work?

ResourceManager is a framework designed to improve your productivity while developing iOS applications. The main objective is to synchronize multiple resource repositories and work with the most recent version of each. By resources, we mean everything that you'd like to embed into your application, such as images, nibs, strings files, config files, json, AppcoreKit stylesheets and layouts, sounds.

ResourceManager provides simple APIs similar to NSBundle to get the paths of the most recent resources from the various resource repositories.

Repositories can be defined as bundles, as well as a Dropbox folder. The Resource Manager provides APIs similar to NSNotificationCenter to register for notifications when files are updated. As soon as a file gets saved in one or several remote repositories, the files will get downloaded to the application's cache directory if needed and a notification is sent to your application to update. You can also get notified of file additions and changes by observing file extensions.

This framework should be used during the development process as you do not want to impact your user experience with files updating at runtime! You can easily disconnect sync mechanism but still use the APIs to get the resources from the application main bundle by not setting a shared RMResourceManager.

ResourceManager supports ios version 7 and more as well as the following architectures: armv7, armv7s, arm64 and i386 for the simulator.

Sample Usage

1. Initializing the resource manager:

1.1. Synchronizing Resources from Dropbox

Create your app on Dropbox:

https://www.dropbox.com/developers/apps

In your target plist:

The Dropbox authentication mechanism requires application redirect via Url Scheme. "From the DropboxSDK documentation" at https://www.dropbox.com/developers/core/start/ios

Once authenticated, your app will be re-launched with the URL scheme db-APP_KEY. The easiest way to register for this scheme is to right-click on your app's plist file and select Open As → Source Code, and add the following code below the first tag, substituting APP_KEY with your app's key:

 CFBundleURLTypes CFBundleURLSchemes db-APP_KEY

In your application delegate:

#import < ResourceManager/ResourceManager.h >

- (id)init{
   self = [super init];

    //Comment or remove the following code when you want to deactivate the sync mechanism
    RMDropboxResourceRepository* rp = [[RMDropboxResourceRepository alloc]initWithAppKey:@"APP_KEY" 
                                                                                  secret:@"APP_SECRET" 
                                                                           rootDirectory:@"ROOT_DIRECTORY"];
    RMResourceManager* rm = [[RMResourceManager alloc]initWithRepositories:@[rp]];
    [RMResourceManager setSharedManager:rm];


    //Do your stuff
}

1.2. Synchronizing your resource from your XCode project's directory (Working in Simulator Only)

In your target plist:

add a string entry with key "SRC_ROOT" and value "$SRCROOT/$PROJECT/"

In your application delegate:

#import < ResourceManager/ResourceManager.h >

- (id)init{
   self = [super init];

    //Comment or remove the following code when you want to deactivate the sync mechanism
    NSString* projectPath = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SRC_ROOT"];
    RMBundleResourceRepository* lr = [[RMBundleResourceRepository alloc]initWithPath:projectPath];
    lr.pullingTimeInterval = 1;

    RMResourceManager* rm = [[RMResourceManager alloc]initWithRepositories:@[lr]];
    [RMResourceManager setSharedManager:rm];

    //Do your stuff
}

2. Forward requiered callback to the resource manager:

In your application delegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
    //Do your stuff ...

    //After :
    [self.window makeKeyAndVisible];

    [RMResourceManager handleApplication:application 
           didFinishLaunchingWithOptions:launchOptions];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url 
                                       sourceApplication:(NSString *)sourceApplication 
                                              annotation:(id)annotation {

    [RMResourceManager handleApplication:application 
                                 openURL:url];

    //Do your stuff
}

3. Retrieving The most recent path for resource:

NSString* resourcePath = [RMResourceManager pathForResource:@"MyResource" 
                                                     ofType:@"myExtension"];
//Load the resource here using the "resourcePath" ...

4. Getting notified when this resource gets updated:

[RMResourceManager addObserverForPath:resourcePath 
                               object:self 
                           usingBlock:^(id observer, NSString *path) {
    //Load the resource here by using the last updated "path" ...
}];

As the manager keeps some weak references on the observer object, you must unregister from the resource manager when needed or at least when you object ("self" here) gets deallocated.

[RMResourceManager removeObserver:self];

5. Retrieving The most recent paths for resources with a specific extension:

NSArray* paths = [CKResourceManager pathsForResourcesWithExtension:@"mp3"];
//Do something with those files

6. Getting notified when these resource are updated or a new file is added with the specified extension:

[CKResourceManager addObserverForResourcesWithExtension:@"mp3" 
                                                 object:self 
                                             usingBlock:^(id observer, NSArray *paths) {
    //Do something with the up to date paths for all mp3 files.
}];

Like for a single path observation, do not forget to unregister your observer.

Installation

The recommended approach for installing ResourceManager is via the CocoaPods package manager, as it provides flexible dependency management and dead simple installation. For best results, it is recommended that you install via CocoaPods >= 0.19.1 using Git >= 1.8.0 installed via Homebrew.

As a framework

Using ResourceManager in your own App (Pre-Compiled)

You can find a pre-compiled version of ResourceManager and sample integration in our sample repository at https://github.com/smorel/appcorekit-samples

  • Drag'n'drop the ResourceManager and the DropBoxSDK frameworks in your XCode project.

  • Add the following frameworks and libraries dependencies to your project in the build phases settings:

    Security, 
    QuartzCore.
    
  • Adds the following link flags in your build settings (OTHER_LDFLAGS):

    -ObjC -all_load -weak_library /usr/lib/libstdc++.dylib
    

Compiling the framework

ResourceManager is built as a Static Framework. Static Frameworks are not natively supported by Xcode 5 or less and require some additional specifications to be compiled properly. You can skip this setup if using XCode 6 and more.

Copy the following file:

./static Frameworks.xcspec

To:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Specifications

And

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications

IMPORTANT: You will have to copy this file each time you update Xcode to a newer version.

Using ResourceManager in your own App (Sources)

  • Drag'n'drop the ResourceManager project as subproject in Xcode.

  • Adds the ResourceManager.framework link dependency to your target in the build phases settings.

  • Add the following frameworks and libraries dependencies to your project in the build phases settings:

    DropboxSDK, (that can be found in this repository) 
    Security, 
    QuartzCore.
    
  • Adds the following link flags in your build settings (OTHER_LDFLAGS):

    -ObjC -all_load -weak_library /usr/lib/libstdc++.dylib
    

Compiling the API Documentation

ResourceManager provides a "Documentation" target that generate a docset using the public header files and the additional programming guides in the Documentation folder. This target is a script base on "appledoc". To install appledoc, follow the installation procedure here : https://github.com/tomaz/appledoc

Your Dropbox Resource Repository

Folders

Resource manager supports resource in folders. It will flatten the file hierarchy when downloading the files into the application's cache directory to match the hierarchy it would have in the application bundle: 1 root directory and localization folders.

Restrictions/Permissions (For Advanced Users)

An advantage of organizing your resources into folders is that the Resource Manager framework provides a mechanism to specify per user restrictions on folders or file extensions.

This feature allows you to give controlled access to your client or colleagues. For instance, let's say you want your designer to customize some resources files for a specific release of your app. But meanwhile, you want to continue working on the next version of your app, customizing resources in the process. By working within a folder that only you have access to, their version of the app will not be impacted by the changes you make in your folder.

At the same time, as soon as you give access to a new user, their app will be notified and their version will update automatically. As soon as you remove this person's access, the files that are newly restricted will be removed from their cache directory and their version of the app will use the main bundle resource again.

This could also be used to test two different versions of an app, by giving users access to two different versions of the application resources.

Here is a sample ResourceManager.permissions file that you could place into your Dropbox folder for user restrictions to update when you save this file. Users are the Dropbox "Display Name" for the user account for which you want to specify the restrictions.

[
   { "folder" : "folder1", //and its subfolders
     "users" : [
         "Sebastien Morel"
      ]
   },

   { "folder" : "folder2",
     "users" : [
         "Sebastien Morel",
         "Frederic Brunel"
     ]
   },


   { "folder" : "folder1/nextedFolder1",
     "users" : [
         "Frederic Brunel"
     ]
   },

   { "extension" : "mp3",
     "users" : [
         "Sebastien Morel",
         "Frederic Brunel",
         "Martin Dufort"
     ]
   }
]

By default, if we do not find a ResourceManager.permissions file or if a folder/file extension on dropbox has no entry in the permissions file, we consider that everybody can access the files in this folder/extension.

Fully integrated with AppCoreKit

It has been designed to update directly to your device and not only the simulator like it was previously implemented in AppCoreKit. Several changes have been made to the AppCoreKit to fully integrate this new framework as a weak dependency and in a very efficient way. AppCoreKit provides the mechanism to automatically reload stylesheets and layouts, images, mappings, mock, localization files (.strings) and updates the UI as a consequence. Go to the AppCoreKit github repository and get the master branch to try the integration:

https://github.com/smorel/AppCoreKit

Credits

If you have any comments, suggestions, question or information request, please contact us at [email protected].

License

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.