Making your first Criollo App

This is the first in a longer series of guides into using and developing apps with the Criollo framework.

When learning a new skill, be it playing an instrument, learning a new programming language, a new development stack, I’ve found that it helps to have a specific goal in sight. As I am sure that this is no news to you - and as playing Flight of the Bumblebee as fast as humanly possible is out of the scope of this article - let’s go ahead and make that most elusive and complicated of apps: the dreaded to do list app.

This series will cover how we create the app and how to deploy it to a server.

Background and Prerequisites

I made Criollo because I wanted to be able to write server-side stuff using Objective-C. Selfish, I know. I prefer Objective-C but all that do here should work with Swift as well. The cool thing about using Cocoa to make web stuff is that you have a ton of cool APIs ready-made, so that the learning curve is relatively small.

That being said, here are some things you should have more than general idea about:

  • macOS application lifecycle
  • The HTTP protocol
  • macOS/iOS development patterns
  • Concurrency

No need to be an expert at these, but a decent grasp thereof should help you on your journey.

The Daemon, the Application and the RunLoop

TL;DR Daemon style launchd apps cannot derive from NSApplication, so Criollo provides its own application base-class: CRApplication.

We will make something that’s meant to run on a macOS machine, in the background, for an extended period of time. In short a daemon style app. macOS already has a mechanism for creating daemons: launchd. As opposed to classical X’s you don’t actually have to do any fork-ing and setsuid-ing it’s all taken care of by launchd. (man launchd and man launchd.plist if you’re curious)

There is a problem however: if you’ve ever built a console app, you know that they have a tendency to do their work and exit when they’re done. That’s not what we need. We need an app that will hang around listening for HTTP connections and respond to them as they come. You know, like daemons do.

What about NSAplication? Well, NSApplication is meant to interact with the desktop and the currently logged in user. That’s why Criollo provides CRApplication, a class that mimics all the behaviors of NSApplication but is suitable for a launchd daemon.

All the familiar patterns are there: CRApplicationDelegate, CRApp and CRApplicationMain. CRApplication maintains its own runloop and also takes care of processing the most common Unix signals for you, so that you don’t need to worry about that stuff.

Scaffolding

As with any macOS app, we start by making an Xcode Cocoa Application project, and we add the Criollo CocoaPod. I am working under the assumption that you know how to do all that and that there’s a lot of information available on the net on how to make Cocoa app projects in Xcode and how to get started using CocoaPods. If you prefer another dependency manager or just want to manually manage, feel free to do so.

Once we’ve done this, I usually start by removing the stuff I don’t need from the project, like xibs/storyboards and whatever view controllers the Xcode template has. We don’t need them.

There are three things we will need to do next, in order to make this app a Criollo app:

  1. Use CRApplicationMain instead of NSApplicationMain as the launcher function.
  2. Change the delegate to conform to the CRApplicationDelegate protocol instead of NSApplicationDelegate.
  3. Change the principal class of the app to CRApplication.

CRApplicationMain

In the main.m file we will call CRApplicationMain as the initialization function instead of the default.

Note: if you went with a Swift based project, you will not have this file and you will need to make it yourself. Check out this SO answer for more details.

This is what your file should look like, assuming that AppDelegate is your delegate class.

#import <Criollo/Criollo.h>
#import "AppDelegate.h"

int main(int argc, const char * argv[]) {
    return CRApplicationMain(argc, argv, [AppDelegate new]);
}

Eagle-eyed readers will notice the third parameter to the function. That’s to instantiate the delegate and set it as the app’s delegate.

CRAppDelegate

We must make the app delegate conform to the CRApplicationDelegate protocol.

The Appdelegate.h file should look like this:

#import <Criollo/Criollo.h>

@interface AppDelegate : NSObject <CRApplicationDelegate>
@end

In the implementation you will no doubt notice that we have a stray IBOutlet, which we’ll need to remove. The default method implementations for applicationDidFinishLaunching: and applicationWillTerminate: should remain there since we’ll be using them a bit later. Your AppDelegate.m file should look like this now:

#import "AppDelegate.h"

@interface AppDelegate ()
@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}

@end

NSPrincipalClass

This is a quick one. In the Info.plist file, you should find the Principal Class key (NSPrincipalClass) and change the value to CRApplication.


That’s it for the scaffolding; if you run the app now, you will notice that it does absolutely nothing 😉. Those of you with unbelievable powers of observation will also notice that it does not exit either: it just stays there waiting and that’s a good thing.

Let’s Serve Something

All we have at this point is a shell, an empty long-lived daemon that does nothing except reacting to basic Unix signals. You could choose to create any other type of daemon at this point, for instance an app that monitors a specific folder and uploads it’s contents somewhere, or logs the changes to the system log, but we are building a web app, so we’ll stick to that line of thought.

The Criollo Server Classes

The core of Criollo is it’s HTTP implementation. The base class for accessing that is the CRServer class. This class should not be used directly, but through one of its subclasses: CRHTTPServer or CRFCGIServer. Yes, there is also a FastCGI implementation, should you find yourself in need of such a setup. There is no significant difference between the two server classes as far as the application logic is concerned, so you can write you app and decide the communication protocol at deployment time.

So let’s get started and declare a strong CRHTTPServer property, just to avoid it being released right after we initialize it.

@property (nonatomic, nonnull, strong) CRHTTPServer *server;

We will initialize it in the applicationDidFinishLaunching: method, and start listening for connections. Also, I like handling these errors even in tutorials. 😊

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
        self.server = [[CRHTTPServer alloc] initWithDelegate:self];
    
    NSError *error;
    if ( ! [self.server startListening:&error] ) {
        [CRApp logErrorFormat:@" Error starting server: %@", error];
        [CRApp terminate:nil];
        return;
    }
    
    [CRApp log:@"Successfully started server"];
}

The startListening: family of methods allows you to specify an interface and port, as well as handling any error encountered while starting the server. By default, CRServer instances will bind to all interfaces on port 10781.

As you may notice, the app delegate is also the server delegate, so let’s make sure to declare it as such, to avoid those pesky compiler warnings.

@interface AppDelegate () <CRServerDelegate>

Let’s leave the delegate protocol aside for now. We will get back to it later.

Your AppDelegate.m should look something like this now:

#import "AppDelegate.h"

@interface AppDelegate () <CRServerDelegate>

@property (nonatomic, nonnull, strong) CRHTTPServer *server;

@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    self.server = [[CRHTTPServer alloc] initWithDelegate:self];
    
    NSError *error;
    if ( ! [self.server startListening:&error] ) {
        [CRApp logErrorFormat:@"Error starting server: %@", error];
        [CRApp terminate:nil];
        return;
    }
    
    [CRApp log:@"Successfully started server"];
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}

@end

Routing and Route Blocks

TL;DR When a request is received, Criollo will execute all blocks associated with the requested HTTP method and path, in the order in which they were added.

Understanding routing is the key to developing Criollo web apps. A route is defined by an HTTP method, a path and an execution block. You can add as many block you want to a path and method combination. They will be executed in the order in which you added them. Once a request is received, the Criollo router will match the path and HTTP method and execute all the blocks it finds.

CRRouteBlock objects are defined as follows:

typedef void(^CRRouteBlock)(CRRequest * request, CRResponse * response, CRRouteCompletionBlock completionHandler);

Inside the execution block, you get a CRRequest object that represents the HTTP request, after it has been parsed and a CRResponse object that you can use to send data back to the client. You also get a reference to a block object that you must call when the current block has done its work and you are ready to pass execution along to the next block.

Adding the First Route Blocks

To start with we will add two blocks:

  1. A block that sets the Server HTTP header for all paths and methods. (some people like to call this middleware)
  2. A block that we will add to the GET /ping path

We’ll set the Server HTTP header field to the bundle identifier, just to keep things simple. In order to add this block we use CRRouter’s add: method. Inside applicationDidFinishLaunching:, before starting the server, we will add:

[self.server add:^(CRRequest * _Nonnull request, CRResponse * _Nonnull response, CRRouteCompletionBlock  _Nonnull completionHandler) {     
    [response setValue:[NSBundle mainBundle].bundleIdentifier forHTTPHeaderField:@"Server"];
    completionHandler();        
}];

Notice that we explicitly call the completionHandler, otherwise execution will stall after we set the header and the connection will eventually time out.

Now let’s add the second block, the one that responds with a JSON object. We want to add this block only to GET requests made to the /ping path. In order to do this we use CRRouter’s get:block: method. Inside applicationDidFinishLaunching:, right after the previously added block, we will add:

[self.server get:@"/ping" block:^(CRRequest * _Nonnull request, CRResponse * _Nonnull response, CRRouteCompletionBlock  _Nonnull completionHandler) {
    [response setValue:@"application/json" forHTTPHeaderField:@"Content-type"];
    [response send:@{ @"status": @YES }];
}];

There’s two things that we’re doing here. First, we’re setting the Content-type HTTP header to JSON. You might notice the same pattern from NSHTTPURLRequest. The second thing is we send a dictionary object with the actual payload.

So let’s run and make a request:

curl -s -D - http://127.0.0.1:10781/ping

outputs ...

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json
Connection: keep-alive
Server: io.criollo.CriolloToDoServer
Date: Sat, 07 Jan 2017 14:41:58 GMT

{"status":true}

Great success!

The ‘Not Found’ Block

I want to say a couple of things about this. If we try to make a request to /, we will get a response like this one:

HTTP/1.1 404 Not Found
Connection: keep-alive
Content-Type: text/plain; charset=utf-8
Content-Length: 1846
Server: io.criollo.CriolloToDoServer
Date: Sat, 07 Jan 2017 15:01:21 GMT

CRServerErrorDomain 404
No routes defined for “GET/”

User Info
NSLocalizedDescription: No routes defined for “GET/”
NSErrorFailingURLKey: http://127.0.0.1:10781/

Stack Trace
0   Criollo                             0x00000001001511a3 __47+[CRRouter errorHandlingBlockWithStatus:error:]_block_invoke + 1827
1   Criollo                             0x00000001001537db __64-[CRRouter executeRoutes:forRequest:response:withNotFoundBlock:]_block_invoke.165 + 299
2   CriolloToDoServer                   0x0000000100001604 __45-[AppDelegate applicationDidFinishLaunching:]_block_invoke + 244
3   Criollo                             0x00000001001533c1 -[CRRouter executeRoutes:forRequest:response:withNotFoundBlock:] + 1777
4   Criollo                             0x0000000100156477 __50-[CRServer connection:didReceiveRequest:response:]_block_invoke + 359
5   Foundation                          0x00007fff9ef0f2d9 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7
6   Foundation                          0x00007fff9ef0efbc -[NSBlockOperation main] + 101
7   Foundation                          0x00007fff9ef0d6e4 -[__NSOperationInternal _start:] + 672
8   Foundation                          0x00007fff9ef0959b __NSOQSchedule_f + 201
9   libdispatch.dylib                   0x00000001001b1f5c _dispatch_client_callout + 8
10  libdispatch.dylib                   0x00000001001c9a59 _dispatch_queue_serial_drain + 205
11  libdispatch.dylib                   0x00000001001bb489 _dispatch_queue_invoke + 1174
12  libdispatch.dylib                   0x00000001001b41d7 _dispatch_root_queue_drain + 671
13  libdispatch.dylib                   0x00000001001b3ee8 _dispatch_worker_thread3 + 114
14  libsystem_pthread.dylib             0x000000010022989a _pthread_wqthread + 1299
15  libsystem_pthread.dylib             0x0000000100229375 start_wqthre

This is generated by the default ‘Not Found’ block of the server. This output is generated only when in debug mode. In release mode we get a much shorter message: Cannot GET / 😉.

This block can be changed simply by setting the notFoundBlock property of CRRouter. For the purpose of this article, we’ll leave it as is.

Logging

As with any web app, one tends to need a bit of logging at some point. There are multiple ways to implement this. We could add a block at the end of all routes and do it there but, somehow I don’t like it that much. Plus it introduces the added task of calling completionHandler at the end of every route, otherwise we might lose some calls. I prefer to implement this kind of feature in a more global place.

Enter CRServerDelegate’s server:didFinishRequest: method, which get’s called on the server delegate queue, when a request has finished processing and sending its response. This is a great place to do stuff that requires access to both the request and response objects.

Let’s log some basic info about the request using the delegate method. Also in AppDelegate.m, as we’ve already declared our AppDelegate as the the server delegate.

- (void)server:(CRServer *)server didFinishRequest:(CRRequest *)request {
    [CRApp logFormat:@"%@ %@ - %lu", [NSDate date], request, request.response.statusCode);
}

If we now make our two previous requests again, we should see something like this in the Xcode console.

2017-01-08 18:30:31 +0000 GET / HTTP/1.1 - 404
2017-01-08 18:30:51 +0000 GET /ping HTTP/1.1 - 200

I’ll let you dig in and get the User Agent and other info you might want to log.

One final thing I should mention is that, for high concurrency applications, it is better in terms of performance to send the logging to a background queue. Usually I would create a serial background dispatch_queue to which I post the logging code.

- (void)server:(CRServer *)server didFinishRequest:(CRRequest *)request {
    static dispatch_queue_t loggingQueue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        loggingQueue = dispatch_queue_create("loggingQueue", DISPATCH_QUEUE_SERIAL);
        dispatch_set_target_queue(loggingQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
    });
    dispatch_async(loggingQueue, ^{
        [CRApp logFormat:@"%@ %@ - %lu", [NSDate date], request, request.response.statusCode];
    });
}

Shutting Things Down

Since CRApplication mimics the behavior of NSApplication, the shutdown sequence is pretty much the same as with any macOS application. At this point we have only one task to perform upon shutdown: closing down the server’s listening socket.

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    [CRApp logFormat:@"%@ Sutting down server.", [NSDate date]];
    [self.server stopListening];
}

That would be pretty much it, unless we were building one of those apps that many people would use at the same time. Oh wait: we are. So let’s make sure that we close down cleanly. Fortunately, like any macOS app a Criollo application also implements the delegate method applicationShouldTerminate:, thus giving up the opportunity to delay the shutdown of the application temporarily, until we’ve had a chance to finish all requests and close down the connection.

- (CRApplicationTerminateReply)applicationShouldTerminate:(CRApplication *)sender {
    static CRApplicationTerminateReply reply;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // Delay the shutdown for a bit
        reply = CRTerminateLater;
        
        // Close server connections
        [CRApp logFormat:@"%@ Closing server connections.", [NSDate date]];
        
        [self.server closeAllConnections:^{
            // We can now stop the server and close the socket cleanly
            reply = CRTerminateNow;
        }];
    });
    return reply;
}

The one extra thing we’re doing here is that we’re closing down all the connections to the server and only when all of them are closed do we allow the shutdown sequence to proceed. You can find out more details on the behavior of this delegate method in Apple’s documentation.


Next Steps

You can download the code for this project here.

In the next article we’ll take a look at how to start making this into an actual usable app, by adding some models and storage.

Hope you found this useful!