TestsTested | ✗ |
LangLanguage | Obj-CObjective C |
License | BSD |
ReleasedLast Release | Dec 2014 |
Maintained by Unclaimed.
Cumulus for Objective C provides a simple, low-level interface to REST services through methods that mirror the HTTP requests associated with creating, fetching, updating, and deleting a remote resource. It is so simple to use that it makes connecting an iOS or Mac app to a Web service stupidly easy.
Cumulus was imagined from the ground up to use Grand Central Dispatch (CGD), allowing for massive concurrency—how fast and how wide you can go is limited by nothing but the hardware and system.
By design, Cumulus does one thing only, and one thing well: interact with REST resources and encode/decode the data coming across the wire to/from native, generic types automatically. Because of this, you can drop Cumulus in to any system and it can fill this role without requiring a whole lot of changes to your existing code. It does not provide any kind of caching or persistence layer, no higher-level interface to resources and no UI, but it's so interoperable (via the ubiquitous use of blocks) that you can connect it to these services in a few minutes; in this context, blocks are used much as you would use UNIX pipes to connect different tools. Because Cumulus is so easy to integrate with, it is also an excellent foundation to build higher-level frameworks on top of.
Cumulus was also created to be a joy to use. The primary interfaces are all implemented with blocks, so you can just forget about writing a thousand implementations of a delegate protocol or adding pointers to everything everywhere—define your intentions in a block where you intend them and enjoy the rest of your day. All the blocks used in the public interface are typed, so you can define them once and reuse them if you find that convenient.
Configuring Cumulus is, well, you don't configure Cumulus. You simply set up a base resource for each service you wish to use and nested resources will automatically inherit the information from their parents. You don't need to configure anything to create those nested resources either. By passing in an object or format string and arguments to any resource, you can create a child resource ready to go. If you need to, you can override any inherited setting in a child resource.
Cumulus handles the most common cases for you, but provides well defined interfaces to easily extend its functionality if you need to. There are protocols for authentication that will allow you to plug in to any kind of auth system, and for mime-type handling to let you serialize/deserialize resources to and from any kind of encoding.
Cumulus was implemented with automatic reference counting (ARC). It requires iOS 6 or greater and/or Mac OS 10.8 or greater.
The easiest way to use Cumulus in your project is to include pod 'Cumulus'
in your CocoaPods Podfile. Make sure you are targeting iOS 6 and/or OS X 10.8 (e.g. platform :ios, '6.0'
)
The most low tech way to use Cumulus in your Xcode project is to just copy the files in "Source" to your own project.
To track a development branch of Cumulus, set it up as a git submodule and drag the project into your workspace, then add either libCumulus.a (iOS) or Cumulus.framework (Mac OS) to your main target's link phase.
If you are using the Mac OS framework, the headers are automatically in your header search path. To add them for iOS, add "${SRCROOT}/relative/path/to/Cumulus/Source" to your HEADER_SEARCH_PATHS build setting and check the recursive box.
On Mac OS, import Cumulus like this in your source:
#import <Cumulus/Cumulus.h>
On iOS, use:
#import "Cumulus.h"
Make sure the Security framework (to handle certificate based auth) and MobileCoreServices framework are linked (for iOS), or CoreServices framework (for Mac OS).
You must use the -ObjC linker flag (at least—you could also use -force_load="${BUILT_PRODUCTS_DIR}/libCumulus.a" or -all_load if you wanted to be more agressive) in order to link in the categories defined in Cumulus.
If you plan on running the tests, make sure you use git clone --recursive
to get the repository (or if you are adding Cumulus as a submodule, git submodule update --recursive
) to be sure to fetch Cumulus's own externals as these are required for the specs to run.
That's it. There is detailed help in the Cumulus Programming Guide if you need more information about how to set up your workspace.
The most common way to use Cumulus is to set up a base resource and some children, define a couple blocks and start making requests.
CMResource *site = [CMResource withURL:@"http://example.com"];
site.timeout = 20;
[site setValue:@"foo" forHeaderField:@"X-MyCustomHeader"];
site.contentType = CMContentTypeJSON;
site.username = @"foo";
site.password = @"bar";
Usually, you would store this somewhere you could access from anywhere in your app, like your app delegate or a context object.
Get stuff
CMResource *posts = [site resource:@"posts"];
[posts getWithCompletionBlock:^(CMResponse *response) {
postsController.posts = response.result;
}];
[posts getWithCompletionBlock:^(CMResponse *response) {
recentPostsController.posts = response.result;
} query:[NSDictionary dictionaryWithObject:@"today" forKey:@"postDate"]];
Create stuff
CMProgressBlock postProgressBlock = ^(CMProgressInfo *progressInfo) {
postProgressView.progress = [progressInfo.progress floatValue];
};
NSDictionary *postData = ...;
CMResource *firstPost = [posts resource:@(1)];
[firstPost post:postData withProgressBlock:postProgressBlock completionBlock:^(CMResponse *response) {
if (response.wasSuccessful) {
[postsController addPost:postData];
}
}];
Map stuff
CMResource *user123 = [site resourceWithFormat:@"users/%@",@(123)];
__block MyUserClass *user;
[user123 getWithCompletionBlock:^(CMResponse *response) {
if (response.wasSuccessful) {
user = [MyUserClass withUserData:response.result];
}
}];
Protect stuff
CMResource *admin = [site resource:@"admin"];
admin.preflightBlock = ^(CMRequest * request){
if (NO == [accountController isAuthorized:request]) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"You need to log in to see this" forKey:NSLocalizedDescriptionKey];
[errorReporter report:[NSError errorWithDomain:@"MyDomain" code:-1 userInfo:userInfo]];
return NO;
}
return YES;
};
Download stuff directly to disk
CMResource *images = [site resource:@"images"];
[images downloadWithProgressBlock:nil completionBlock:^(CMResponse *response) {
NSURL *downloadedFile = [(CMProgressInfo *)response.result tempFileURL];
// Move the file to where you want it
}
Test stuff—even before your services are ready!
CMResource *posts = [site resource:@"posts.json"];
[posts setFixture:[NSArray arrayWithObjects:postOne,postTwo,nil] forHTTPMethod:kCumulusHTTPMethodGET];
[posts getWithCompletionBlock:^(CMResponse *response) {
postsController.posts = response.result;
}];
// the array from the fixture is turned into data and decoded & postprocessed normally into response.result
// You can even set your fixtures all in one place, or load them from a plist:
[Cumulus loadFixturesNamed:@"MyService"];
[Cumulus useFixtures:YES];
// now all resources will also check in with Cumulus to see if they have a fixture, yay!
Group a whole bunch of requests together using blocks
CMResourceContext *context = [CMResourceContext withName:@"Posts Context"];
[context performRequestsAndWait:^() {
CMResource *users = [site resource:@"users.json"];
CMResponse *users = [users get];
// Do something with users
CMResource *posts = [site resource:@"test/get/item"];
[posts getWithCompletionBlock:^(CMResponse *response) {
// Do something with posts
}];
} withCompletionBlock:^(BOOL success, NSSet *responses) {
NSLog(@"All my updates were %@",success ? @"successful" : @"not successful");
}];
Automatically cancel groups of requests when a scope object deallocates
// PostsController.m
- (void) viewDidLoad {
CMResourceContext *context = [CMResourceContext withName:@"Posts Context"];
[context performRequests:^{
for (int i = 0; i < 10; i++) {
[[self.posts resource:@(i)] getWithCompletionBlock:...];
}
} inScope:self];
}
// When the posts controller deallocates, any requests still running will cancel automatically
// There is also a convenience category on NSObject that lets you scope requests directly on any object
[self performRequestsInScope:^{...}];
Cumulus does even more, like direct from disk uploads, chunked downloads, OAuth2 and S3 authentication, automatic queueing and canceling of requests, automatic concurrent request optimization, and post-processing on a background thread (great for Core Data mapping in a child context), See more detailed examples in the Cumulus Programming Guide.
You can install the Cumulus Xcode docset by running the "Install Cumulus Docs" scheme in Xcode.
Cumulus Programming Guide
Example Apps
Running the Tests
Cumulus is released under the BSD license
Cumulus was originally created by and is maintained by John Clayton [email protected]
Check out the contributors graph to see all the great people making Cumulus work.
Patches are welcome, pull requests get us all excited.
Lots of inspiration in the interface design of Cumulus was taken from Ruby's rest-client
Cumulus uses Appledoc for generating API documentation