TestsTested | ✗ |
LangLanguage | Obj-CObjective C |
License | MIT |
ReleasedLast Release | Dec 2014 |
Maintained by Unclaimed.
Depends on: | |
OCFWebServer | ~> 0.0.2 |
SOCKit | ~> 1.1 |
GRMustache | ~> 6.7 |
OCFWeb is a web application framework written in Objective-C. You can use OCFWeb to create web applications with just a few lines of code. Although OCFWeb is developed and used by Objective-Cloud.com it does not depend on Objective-Cloud.com. You can use OCFWeb on your own servers (although we prefer you to use Objective-Cloud.com for that :)) and/or in your own OS X/iOS apps. In fact OCFWeb was designed to be embedded in an existing application written in Objective-C. Sinatra, a web application framework/DSL written in Ruby, has inspired the development of OCFWeb.
The following code snippet shows you how to create a web application that responds to GET requests made to /
.
@interface AppDelegate ()
@property (nonatomic, strong) OCFWebApplication *app;
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)n {
// Create a instance of OCFWebApplication
self.app = [OCFWebApplication new];
// Add a handler for GET requests
self.app[@"GET"][@"/"] = ^(OCFRequest *request) {
// request contains a lot of properties which describe the incoming request.
// Respond to the request:
request.respondWith(@"Hello World");
};
// Run your app on port 8080
[self.app runOnPort:8080];
}
@end
Opening http://127.0.0.1:8080/ in your browser should show a web site with the words "Hello World" on it. Nice isn't it? Let's examine the code a little bit more:
OCFWebApplication
by using the +new
class method. This creates a blank web application for you that does nothing useful.OCFWebApplication
. Each handler consists of three parts (HTTP method, path, handler block). The individual parts of the handler added in the example are as follows:
[@"GET"]
). This associates your handler with GET-HTTP requests and nothing else.[@"/"]
). This associates your handler with HTTP requests made to http://127.0.0.1/
(no additional characters behind the trailing /
).=
and is a simple C block with the following prototype: void^(OCFRequest *request)
(no return type and only a single parameter which is the request object).+runOnPort:
. This creates a HTTP server on localhost automatically. Implementation detail: The used HTTP server is called OCFWebServer. Basically OCFWebApplication is just a nice wrapper around OCFWebServer.A couple of notes:
Once +runOnPort:
is called incoming requests are inspected by OCFWebApplication
. If it finds a handler that is matching the HTTP method and path of the request your handler block is called. OCFWebApplication
is passing the actual request to your handler block. Then it is your turn: You have to create a response. This can be done synchronously or asynchronously. No matter how you create your response, once you have it your should let OCFWebApplication
know about your response so that it can be delivered to the client. In the example above the response is just @"Hello World"
. The request object has a property called respondWith
. respondWith
is a block that takes a single argument (of type id
). You execute this block and pass it the response. The moment this happens OCFWebApplication
knows about your response and delivers it to the client.
OCFWeb is using CocoaPods. You can install CocoaPods by running the following commands:
$ [sudo] gem install cocoapods
$ pod setup
Then clone or download OCFWeb from GitHub and cd
into the OCFWeb directory. The directory contains a file called Podfile
. Now execute the following command:
$ pod install
This downloads all of the dependencies. Now open the OCFWeb workspace and select the scheme "OCFWeb Mac Example". Click build and run. The example application automatically opens the web app in your default browser.
Developing a web application framework is hard. There are a lot of things that have to be considered and taken care of. This is our first attempt of a web application framework so we tried to concentrate on only a handful of things:
The first example shows how easy it is to create a response for an incoming request. As you can see the handler block assigned to the GET /
route. The moment the handler block has created/computed a response object it is passed to your instance of OCFWebApplication
by executing the respondWith-block: request.respondWith(response)
. It has already been mentioned that the only parameter of the respondWith-block is typed with id
. This allows you to pass different kinds of response objects: In some situations a simple string is a good response and in some other situations you might need something more sophisticated. These are the different kind of response objects you can return:
@{@"status" : @201, @"headers": @{"Content-type" : @"image/tiff" }, @"body" : [image TIFFRepresentation]}
OCFResponse
object: OCFResponse
is a class which represents a response. You can create and pass a OCFResponse
which gives you full control.OCFMustache
object: OCFMustache
is a class that represents a Mustache template response. At the moment Mustache is the only template engine supported by OCFWeb. You can have template files in your application bundle and then create a OCFMustache
object by using +newMustacheWithName:object:
which renders the template so that it is ready to be delivered to the client.The following example shows how to create a response by returning a simple string.
self.app = [OCFWebApplication new];
self.app[@"GET"][@"/"] = ^(OCFRequest *request) {
request.respondWith(@{ @"Hello World. I am a string." });
};
[self.application runOnPort:8080];
This creates a plain/text
response with a status code of 201. If you don't like the content type or status code for string responses you can change it on a per application basis.
The following example shows how to create a response by returning a dictionary. If you run this example you should be able to use your browser to access the web application which should display your application icon.
self.app = [OCFWebApplication new];
self.app[@"GET"][@"/"] = ^(OCFRequest *request) {
NSImage *image = [NSImage imageNamed:@"NSApplicationIcon"];
request.respondWith(@{ @"status" : @201,
@"body" : [image TIFFRepresentation],
@"headers" : @{ @"Content-Type" : @"image/tiff" }});
};
[self.application runOnPort:8080];
For the following example to work there must be a file called Detail.mustache
in the resources of your application. Before using a mustache response you should read the documentation of the mustache library used by OCFWeb. A mustache file basically contains text with placeholders and the underlying mustache engine can automatically fill in the details for you.
self.app = [OCFWebApplication new];
self.app[@"GET"][@"/"] = ^(OCFRequest *request) {
NSDictionary *person = @{ @"id" : @1,
@"firstName" : @"Christian",
@"lastName" : @"Kienle" };
OCFMustache *response = [OCFMustache newMustacheWithName:@"Detail"
object:person];
request.respondWith(response);
};
[self.app runOnPort:8080];
When adding a request handler you have to specify a HTTP method and a path. In the examples above we used GET
as the HTTP method and /
as the path. OCFWeb let's you do more sophisticated things though.
The HTTP method you specify can be a regular expression. For example: This allows you to route all requests - no matter the request method - to a single handler (by using @"^.+$"
) as the method. It should be mentioned that doing this is considered bad practice. If you have the exact same handler for different HTTP methods you should rethink your architecture.
The path can be a pattern with placeholders. A placeholder begins with a :
. This is useful if you have hierarchical URLs/paths like this:
GET /countries/
: Lists all countries.GET /countries/Germany/
: Lists only the country called Germany.GET /countries/Germany/states/Berlin/
: Lists only the german state Berlin.Let's assume you want to add and implement a handler that displays a specific state. You would do that by using the path pattern /countries/:country/states/:state/
.
The following example shows you how to register a handler that is only executed if the request path is matching a specific pattern.
self.app = [OCFWebApplication new];
self.app[@"GET"][@"/countries/:country/states/:state/"] = ^(OCFRequest *request) {
request.respondWith([request.parameters description]);
};
[self.application run];
The pattern used (/countries/:country/states/:state/
) has two placeholders:
If a request with a matching path comes in (GET /countries/Germany/states/Berlin
) then the specified handler is executed. The parameters
property of the request passed to the handler is a dictionary which contains two key/value-pairs:
country = Germany
state = Berlin
The request handler simply returns a description of the dictionary in this case. So you don't have to parse the path of the request yourself.
There is no limit regarding the number of handlers you add. If a request comes in then one out of many handlers has to be picked and executed. OCFWebApplication finds a handler for an incoming request in two steps:
OCFWeb runs on
At the moment OCFWeb has the following dependencies (also listed in it's Podfile):
OCFWeb is used by Objective-Cloud.com. We plan to expose parts of OCFWeb to developers who are running applications hosted on Objective-Cloud.com.
Development of OCFWebServer takes place on GitHub. If you find a bug, suspect a bug or have a question feel free to open an issue. Pull requests are very welcome and will be accepted as fast as possible.
OCFWeb is licensed under the MIT license (MIT).