Publishing data from iOS devices to Azure Event Hub

Luis Delgado
Apr 9, 2016 · 4 min read

Here is some code samples that will help you post event data (whatever it might be) from an iOS device to Azure Event Hub.

1. Generating a SAS Token

To post data to an Event Hub endpoint, a device has to provide a SAS token (Shared Access Signature) to authenticate and authorize itself. For a comprehensive article on how to use SAS authentication with Event Hubs, refer to this guide.

In my scenario, I have simply configured the Event Hub within the Services Bus admin portal with a Senders Shared Access Policy. This policy gives devices authorization to post data, but nothing else.

Although you can generate the SAS token directly on the device, you will have to share your Shared Access Policy secret key with the devices themselves. This is, from a security and key management point of view, a bad idea. Instead, deploy an API that will provide clients with their SAS tokens. In my case, I spun-up an Azure WebAPI instance running NodeJS code to do this. It will literally take you no more than 3 minutes to do:

app.js

var express = require(‘express’);var app = express();
require(‘./routes.js’)(app);
app.listen(process.env.PORT || 3000, function() {
console.log(‘express app started’);
});

routes.js

var sasHandler = require(‘./handlers/sas.js’);module.exports = function(app){ 
app.get(‘/sas’, sasHandler.getSASToken);
}

sas.js

var crypto = require(‘crypto’);
var util = require(‘util’);
var request = require(‘request’);
var sbNamespace = process.env.SBNAMESPACE || ‘yournamespace’;
var sbEntityPath = process.env.SBENTITYPATH || ‘yourEventHubName’;
var sharedAccessKey = process.env.SENDERS_SHARED_ACCESS_KEY_1 || ‘yourSAPolicyKey’;
var sharedAccessKeyName = process.env.SENDER_SHARED_ACCESS_NAME_1 || ‘yourSAPolicyName’;
module.exports.getSASToken = function getSASToken(req, res) {
var uri = “https://” + sbNamespace +
“.servicebus.windows.net/” + sbEntityPath + “/publishers/{yourPublisherId}/messages”;
var encodedResourceUri = encodeURIComponent(uri);var expireInSeconds = Math.round(minutesFromNow(30) / 1000);var plainSignature = encodedResourceUri + “\n” + expireInSeconds;var signature = crypto.createHmac(‘sha256’, sharedAccessKey)
.update(plainSignature)
.digest(‘base64’);
var sas = util.format(‘SharedAccessSignature sig=%s&se=%s&skn=%s&sr=%s’,
encodeURIComponent(signature), expireInSeconds, sharedAccessKeyName, encodedResourceUri);;
console.log(sas);
res.send(sas);
function minutesFromNow(minutes) {
var date = new Date();
date.setMinutes(date.getMinutes() + minutes);
return date;
}
}

(note: I found this code in a GH repo, but I can’t find it anymore to give the author credit for it. If you are the author, ping me with the link to your repo/Gist)

2. Serialize your data

When posting data to Event Hub, you have to deliver a JSON-compliant payload. In my iOS app, the custom type I use to store event is serialized into a [[String : Double]] type. Then, I use NSJSONSerialization to convert the payload to an NSData object:

let serializedInformation = try! NSJSONSerialization.dataWithJSONObject(item, options: NSJSONWritingOptions.init(rawValue: 0))

3. Networking considerations

If you paid careful attention to the type above, you noted that I serialize my data as an Array of [String : Double]. This is because I do not post each event individually to Azure. My iOS devices generate a data observation every half a second. Although Event Hub supports the AMQP protocol, AFAIK iOS does not support that protocol natively. Making a full HTTP request every half a second from an iPhone is way too expensive, so I collect my observations in an array and then post a batch upload to Event Hub. This is something specific to my scenario. You should carefully evaluate how often you will be posting events and decide whether batching them together is a better option.

4. Posting the event batch to Event Hub

As of this writing, Microsoft does not provide an iOS SDK for Event Hub. So you will have to rely on the Event Hub REST API do get the job done. I use Alamofire to handle my networking:

(note: I am using NSJSONSerialization twice, which is not elegant. You can optimize this and keep one NSJSONSerialization call for the HTTPBody only)

You should get an HTTP 201 message back.

If you get an HTTP 401, there is something wrong with your authorization token. Bear in mind that the URL within the signed SAS token needs to match the URL to which you are posting data. For example, if your SAS token has an url of /{yourEventHubName}/messages but you are trying to post to /{yourEventHubName}/publisher/{yourPublisherId}/messages, you will get an authorization error.

If you get an HTTP 400 error, there is something wrong in how you are constructing your payload.

When posting events in batches, keep in mind that your JSON must comply with the following syntax:

[{“Body”: “Message1”}, {“Body”: “Message2"},…]

That means, all your elements in the array need to be introduced by the property “Body”, whose value should be the JSON object you want to post. This syntax rule is not very clear in Microsoft’s documentation and it took me about 2 hours to figure it out by trial and error.

For more information about how to interact with the REST API, including examples, check this page.

Comments, code improvements, feedback, complaints or jokes to share? Comment below.

Luis is the Services Manager for Codit Switzerland. Codit is Europe’s largest integration consultancy company. We help customer with Enterprise Application Integration needs, developing IoT-powered business models and with rapid-prototyping of IoT initiatives. Follow us on Twitter or LinkedIn.

Luis Delgado

Written by

Passionate about technology and finance. What an explosive mixture!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade