Primus-Objc is an implementation of the Primus javascript client layer.
The library is fully unit tested using Specta, Expecta and OCMockito.
Currently supported real-time frameworks:
pod 'Primus'
pod 'SocketRocket'
# or pod 'socket.IO'
- Introduction
- Installation
- Getting Started
- Connecting
- Authorization
- Broadcasting
- Disconnecting
- Events
- Heartbeats and latency
- Plugins
- Tests
- License
Here is a quick example of establishing a connection to a Primus server and listening on the various events we emit.
Since we haven't specified a transformer, Primus will connect to then /spec
endpoint and attempt to determine the appropriate real-time framework.
#import <Primus/Primus.h>
- (void)start
{
NSURL *url = [NSURL URLWithString:@"http://localhost:9090/primus"];
Primus *primus = [[Primus alloc] initWithURL:url];
[primus on:@"reconnect" listener:^(PrimusReconnectOptions *options) {
NSLog(@"[reconnect] - We are scheduling a new reconnect attempt");
}];
[primus on:@"online" listener:^{
NSLog(@"[network] - We have regained control over our internet connection.");
}];
[primus on:@"offline" listener:^{
NSLog(@"[network] - We lost our internet connection.");
}];
[primus on:@"open" listener:^{
NSLog(@"[open] - The connection has been established.");
}];
[primus on:@"error" listener:^(NSError *error) {
NSLog(@"[error] - Error: %@", error);
}];
[primus on:@"data" listener:^(NSDictionary *data, id raw) {
NSLog(@"[data] - Received data: %@", data);
}];
[primus on:@"end" listener:^{
NSLog(@"[end] - The connection has ended.");
}];
[primus on:@"close" listener:^{
NSLog(@"[close] - We've lost the connection to the server.");
}];
}
Primus can also be initialized with an NSURLRequest class, so that you can add your own HTTP headers, for example:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:9090/primus"]];
[request setValue:@"Bearer <your-oauth-token-here>" forHTTPHeaderField:@"Authorization"];
Primus *primus = [[Primus alloc] initWithURLRequest:request];
Further customization can also be achieved by passing an instance of a PrimusConnectOptions object:
NSURL *url = [NSURL URLWithString:@"http://localhost:9090/primus"];
PrimusConnectOptions *options = [[PrimusConnectOptions alloc] init];
options.transformerClass = SocketRocketClient.class;
options.strategy = @[@(kPrimusReconnectionStrategyTimeout)];
options.timeout = 200;
options.manual = YES;
Primus *primus = [[Primus alloc] initWithURL:url options:options];
When you initialize a Primus instance with the default options, it will automatically connect. This is done for compatibility with the original Primus library.
However, if you'd prefer to connect manually, you must pass a custom PrimusConnectOptions to the initializer and call the open
method:
NSURL *url = [NSURL URLWithString:@"http://localhost:9090/primus"];
PrimusConnectOptions *options = [[PrimusConnectOptions alloc] init];
options.manual = YES;
Primus *primus = [[Primus alloc] initWithURL:url options:options];
// Calling 'open' will start the connection
[primus open];
The open
event will be emitted when the connection is successfully established.
If you have made use of the server-side authorization hook, Primus will emit an error
event if it fails authorization.
You can then retrieve the error code as per your authorization logic.
Broadcasting allows you to write a message to the server. You can currently broadcast instances of NSDictionary
, NSString
or NSData
.
[primus write:@{
@"my-data": @(123)
}];
[primus write:@"example-data"];
You can easily disconnect from the server by calling the end
method. Primus will also emit an end
event when it has successfully disconnected.
[primus end];
Primus is build upon the EventEmitter pattern using the Emitter library. This is a summary of the events emitted by Primus.
Event | Usage | Location | Description |
---|---|---|---|
reconnecting |
public | client | We're scheduling a reconnect. |
reconnect |
public | client | Reconnect attempt is about to be made. |
outgoing::open |
private | client | Transformer should connect. |
incoming::open |
private | client | Transformer has connected. |
open |
public | client | Connection is open. |
incoming::error |
private | client | Transformer received error. |
error |
public | client | An error happened. |
incoming::data |
private | client | Transformer received data. |
outgoing::data |
private | client | Transformer should write data. |
data |
public | client | We received data. |
incoming::end |
private | client | Transformer closed the connection. |
outgoing::end |
private | client | Transformer should close connection. |
end |
public | client | Primus has ended. |
close |
public | client | The underlying connection is closed, we might retry. |
initialised |
public | client | The server is initialised. |
incoming::pong |
private | client | We received a pong message. |
outgoing::ping |
private | client | We're sending a ping message. |
online |
public | client | We've regained a network connection |
offline |
public | client | We've lost our internet connection |
The Primus heartbeat mechanism has been implemented as described in the original framework.
In the case of iOS, Primus supports staying connected in the background. This option is available in the PrimusConnectOptions object. It is automatically set to true
if your application is configured with the VOIP UIBackgroundMode, as described in Apple's documentation.
In order to keep the connection alive, we need to send a primus::ping::<timestamp>
message which means we need some CPU time every now and again. The maximum interval that Apple allows for background tasks is 10 minutes, which means that you must configure your server-side Primus with a timeout of at least 10 minutes.
Here's an example of how to configure your server, in javascript:
var primus = new Primus(server, {
transformer: 'websockets',
timeout: 630000 // 10 minutes and 30 secs
});
The Primus javascript framework allows for plugins on the server-side and the client-side.
Primus-Objc supports client-side plugins as well as message transformers.
You can intercept and transform incoming
or outgoing
data by registering a transform block:
[self transform:@"incoming" fn:^BOOL(NSMutableDictionary *data) {
data[@"foo"] = @"bar";
return YES;
}];
You can also prevent the data
event from being emitted by returning NO
on the transformer block. This will effectively discard the message.
These are plugins created by our community. If you created a module, please submit a pull request and add it to this section.
A module that adds emitter capabilities to Primus.
Most of the functionality is covered by tests. For the real-time clients, we start up a small node server with the respective real-time frameworks and test against them.
Before running the tests, please run:
pod install
npm install
You can then open the project with Xcode and choose Product → Test (⌘U).
MIT