Run Flutter app on iOS 2021 edition

You need an apple developer account and XCode

Open the Flutter app in XCode

In Android Studio, go to Tools / Flutter / Open iOS module in XCode. I know, using ANDROID studio to make apps for an iPhone…

Prepare XCode

Connect your phone to the computer. On the top left part, click Runner > and select your phone. If you don’t see your device you’ll need to troubleshoot, don’t select “Any iOS device”

Go to XCode / Preferences / Account tab, press the plus button, select Apple ID and login to your Developer account. You’ll be prompted for keychain access, allow all. After you are done, make sure a team is available. Some tutorials made you do this step by manually creating a CSR key on your computer and upload it to Apple, following the wizard in XCode is much easier

Next, select runner on the left pane, and select signing & capabilities, make sure automatically manage signing is selected

Remotely debug your app

  • In XCode, select Windows / Device and simulators
  • Click on your phone, check the connect via network button

Wait until there’s a globe next to your phone’s name. That’s it, you can now disconnect your phone from the computer and deploy your app remotely to your phone by clicking the Run button

The run button

Javascript idioms

From Zoltan Kochan, author of pnpm


Double exclamation

Prefixing anything with !! converts it to a boolean.

var foo = 0
//> false

Essentially it is a shorter way to write Boolean(foo).

Converting arguments to array

The arguments object can be used to access the arguments passed to the function. However, it is not an Array so it doesn’t have Array properties except length. The idiom is used very frequently to convert the argument’s object to an actual array.

(function() {
  console.log(arguments instanceof Array)
  //> false

  var args =
  console.log(args instanceof Array)
  //> true

Assigning default values

function foo(opts) {
  var msg = opts.message || 'Hello world!'

// instead of
function foo(opts) {
  var msg = opts.message ? opts.message : 'Hello world!'

More examples of interesting || and && usages can be found in the 12 Simple (Yet Powerful) JavaScript Tips article.

Converting to array if not already

var totallyArray = [].concat(value)

//instead of
var totallyArray = value instanceof Array ? value : [value]

Converting strings to number

var foo = +'12.2'
var bar = +'12'

// instead of
var foo = parseFloat('12.2')
var bar = parseInt('12')

Checking if an array includes an element

if (~[1, 2, 3].indexOf(2)) { console.log('includes') }

// instead of
if ([1, 2, 3].indexOf(2) > -1) { console.log('includes') }

There are some other usage examples for the tilde operator as well in The Great Mystery of the Tilde(~).

Writing multi-line strings

var multiStr = [
  "This is the first line",
  "This is the second line",
  "This is more..."

// instead of
var multiStr = "This is the first line\n" +
  "This is the second line\n" +
  "This is more...";

Looping through an array

It can be used if order is not important

for (var i = arr.length; i--;) {
  // ...

// instead of
for (var i = 0; i < arr.length; i++) {
  // ...

setTimeout(func, 0)

JavaScript code runs on one thread. Calling setTimeout with 0 allows to schedule a function to run after the current event loop tick.

setTimeout(function() {
  console.log('log message from next tick')
}, 0)

console.log('Hello world!')
//> Hello world!
//> log message from next tick


There’s a little known REST API built into RabbitMQ, accessible via the management plugin. Its methods are as follows:

GET PUT DELETE POST Path Description
X /api/overview Various random bits of information that describe the whole system.
X X /api/cluster-name Name identifying this RabbitMQ cluster.
X /api/nodes A list of nodes in the RabbitMQ cluster.
X /api/nodes/name An individual node in the RabbitMQ cluster. Add “?memory=true” to get memory statistics, and “?binary=true” to get a breakdown of binary memory use (may be expensive if there are many small binaries in the system).
X /api/extensions A list of extensions to the management plugin.
X X /api/definitions
/api/all-configuration (deprecated)
The server definitions – exchanges, queues, bindings, users, virtual hosts, permissions and parameters. Everything apart from messages. POST to upload an existing set of definitions. Note that:

  • The definitions are merged. Anything already existing on the server but not in the uploaded definitions is untouched.
  • Conflicting definitions on immutable objects (exchanges, queues and bindings) will cause an error.
  • Conflicting definitions on mutable objects will cause the object in the server to be overwritten with the object from the definitions.
  • In the event of an error you will be left with a part-applied set of definitions.

For convenience you may upload a file from a browser to this URI (i.e. you can use multipart/form-data as well as application/json) in which case the definitions should be uploaded as a form field named “file”.

X /api/connections A list of all open connections.
X X /api/connections/name An individual connection. DELETEing it will close the connection. Optionally set the “X-Reason” header when DELETEing to provide a reason.
X /api/connections/name/channels List of all channels for a given connection.
X /api/channels A list of all open channels.
X /api/channels/channel Details about an individual channel.
X /api/consumers A list of all consumers.
X /api/consumers/vhost A list of all consumers in a given virtual host.
X /api/exchanges A list of all exchanges.
X /api/exchanges/vhost A list of all exchanges in a given virtual host.
X X X /api/exchanges/vhost/name An individual exchange. To PUT an exchange, you will need a body looking something like this:


The type key is mandatory; other keys are optional.When DELETEing an exchange you can add the query string parameter if-unused=true. This prevents the delete from succeeding if the exchange is bound to a queue or as a source to another exchange.

X /api/exchanges/vhost/name/bindings/source A list of all bindings in which a given exchange is the source.
X /api/exchanges/vhost/name/bindings/destination A list of all bindings in which a given exchange is the destination.
X /api/exchanges/vhost/name/publish Publish a message to a given exchange. You will need a body looking something like:

{"properties":{},"routing_key":"my key","payload":"my body","payload_encoding":"string"}

All keys are mandatory. The payload_encoding key should be either “string” (in which case the payload will be taken to be the UTF-8 encoding of the payload field) or “base64” (in which case the payload field is taken to be base64 encoded).
If the message is published successfully, the response will look like:

{"routed": true}

routed will be true if the message was sent to at least one queue.Please note that the HTTP API is not ideal for high performance publishing; the need to create a new TCP connection for each message published can limit message throughput compared to AMQP or other protocols using long-lived connections.

X /api/queues A list of all queues.
X /api/queues/vhost A list of all queues in a given virtual host.
X X X /api/queues/vhost/name An individual queue. To PUT a queue, you will need a body looking something like this:

{"auto_delete":false,"durable":true,"arguments":{},"node":"[email protected]"}

All keys are optional.When DELETEing a queue you can add the query string parameters if-empty=true and / or if-unused=true. These prevent the delete from succeeding if the queue contains messages, or has consumers, respectively.

X /api/queues/vhost/name/bindings A list of all bindings on a given queue.
X /api/queues/vhost/name/contents Contents of a queue. DELETE to purge. Note you can’t GET this.
X /api/queues/vhost/name/actions Actions that can be taken on a queue. POST a body like:


Currently the actions which are supported are sync and cancel_sync.

X /api/queues/vhost/name/get Get messages from a queue. (This is not an HTTP GET as it will alter the state of the queue.) You should post a body looking like:

  • count controls the maximum number of messages to get. You may get fewer messages than this if the queue cannot immediately provide them.
  • requeue determines whether the messages will be removed from the queue. If requeue is true they will be requeued – but their redelivered flag will be set.
  • encoding must be either “auto” (in which case the payload will be returned as a string if it is valid UTF-8, and base64 encoded otherwise), or “base64” (in which case the payload will always be base64 encoded).
  • If truncate is present it will truncate the message payload if it is larger than the size given (in bytes).

truncate is optional; all other keys are mandatory.

Please note that the get path in the HTTP API is intended for diagnostics etc – it does not implement reliable delivery and so should be treated as a sysadmin’s tool rather than a general API for messaging.

X /api/bindings A list of all bindings.
X /api/bindings/vhost A list of all bindings in a given virtual host.
X X /api/bindings/vhost/e/exchange/q/queue A list of all bindings between an exchange and a queue. Remember, an exchange and a queue can be bound together many times! To create a new binding, POST to this URI. You will need a body looking something like this:


All keys are optional. The response will contain a Location header telling you the URI of your new binding.

X X /api/bindings/vhost/e/exchange/q/queue/props An individual binding between an exchange and a queue. The props part of the URI is a “name” for the binding composed of its routing key and a hash of its arguments.
X X /api/bindings/vhost/e/source/e/destination A list of all bindings between two exchanges. Similar to the list of all bindings between an exchange and a queue, above.
X X /api/bindings/vhost/e/source/e/destination/props An individual binding between two exchanges. Similar to the individual binding between an exchange and a queue, above.
X /api/vhosts A list of all vhosts.
X X X /api/vhosts/name An individual virtual host. As a virtual host usually only has a name, you do not need an HTTP body when PUTing one of these. To enable / disable tracing, provide a body looking like:

X /api/vhosts/name/permissions A list of all permissions for a given virtual host.
X /api/users A list of all users.
X X X /api/users/name An individual user. To PUT a user, you will need a body looking something like this:



{"password_hash":"2lmoth8l4H0DViLaK9Fxi6l9ds8=", "tags":"administrator"}

The tags key is mandatory. Either password or password_hash must be set. Setting password_hash to “” will ensure the user cannot use a password to log in. tags is a comma-separated list of tags for the user. Currently recognised tags are “administrator”, “monitoring” and “management”.

X /api/users/user/permissions A list of all permissions for a given user.
X /api/whoami Details of the currently authenticated user.
X /api/permissions A list of all permissions for all users.
X X X /api/permissions/vhost/user An individual permission of a user and virtual host. To PUT a permission, you will need a body looking something like this:


All keys are mandatory.

X /api/parameters A list of all parameters.
X /api/parameters/component A list of all parameters for a given component.
X /api/parameters/component/vhost A list of all parameters for a given component and virtual host.
X X X /api/parameters/component/vhost/name An individual parameter. To PUT a parameter, you will need a body looking something like this:

{"vhost": "/","component":"federation","name":"local_username","value":"guest"}
X /api/policies A list of all policies.
X /api/policies/vhost A list of all policies in a given virtual host.
X X X /api/policies/vhost/name An individual policy. To PUT a policy, you will need a body looking something like this:

{"pattern":"^amq.", "definition": {"federation-upstream-set":"all"}, "priority":0, "apply-to": "all"}

pattern and definition are mandatory, priority and apply-to are optional.

X /api/aliveness-test/vhost Declares a test queue, then publishes and consumes a message. Intended for use by monitoring tools. If everything is working correctly, will return HTTP status 200 with body:


Note: the test queue will not be deleted (to to prevent queue churn if this is repeatedly pinged).

This can probably be used to build a library that monitor RabbitMQ nodes and automatically select the best node to connect to

Building a web application with Thing+ platform

These days buzzwords get thrown around a lot about the “4th industrial revolution”. While it’s simply a collection of upcoming technology to make our lives easier, the barrier of entry to new tech is always high.

Thing+ platform was developed as a way to lower that barrier, it’s a Platform as a service that provide you with ready-made embedded software for Internet of
Things devices and a web service that manages those devices.

This post is a starter’s guide to building a web application that use Thing+ from scratch. It’s assumed the reader is somewhat familiar with IoT terminologies, and have a basic grasp of programming.


Starting the repository

  • Name your app, let’s say thingplus-webapp
  • npm install express-generator -g
  • express --view=pug thingplus-webapp
  • cd thingplus-webapp
  • git init
  • vi .gitignore, paste the following
# Logs

# Runtime data

# Directory for instrumented libs generated by jscoverage/JSCover

# Coverage directory used by tools like istanbul

# nyc test coverage

# Grunt intermediate storage (

# Bower dependency directory (

# node-waf configuration

# Compiled binary addons (

# Dependency directories

# Typescript v1 declaration files

# Optional npm cache directory

# Optional eslint cache

# Optional REPL history

# Output of 'npm pack'

# Yarn Integrity file

# dotenv environment variables file
  • npm install

Register, login

Registration page

We don’t need to involve ourself in user management since we can use user information from Thing+ Let’s create a register
link first, add this line to app.js

app.use('/register', require('./routes/register'));

 Add register.js to the routes folder with the following content

'use strict';
const auth = require('../auth');
const express = require('express');
const router = express.Router();

/* GET register page */
router.get('/', function (req, res) {

module.exports = router;

This simply create a new page that will redirect to Thing+’s sign up page Let’s link to it from the index page, modify index.pug
to the layout folder

extends layout

block content
    if user
      p Welcome, #{user.fullName}
        a.btn.btn-primary(role="button", href="/gateways") Manage gateways
        a.btn.btn-primary(role="button", href="/rules") Manage rules
        a.btn.btn-primary(role="button", href="/trains") Manage trains
        a.btn.btn-primary(role="button", href="/logout") Logout
      p Welcome to #{appName}, ready to get started?
        a.btn.btn-primary(role="button", href="/login") Login now
      p Don't have an account?
        a.btn.btn-primary(href="/register") Register now

This will show various internal commands if you are logged in, and show the register link if you are not logged in.

Login and callback route

Add the following files to the routes folder
  • Login.js
'use strict';
const auth = require('../auth');
const querystring = require('querystring');

const express = require('express');
const session = require('../session');
const router = express.Router();


/* GET login page */
router.get('/', function (req, res, next) {
 if (req.session && req.session.token) {
 } else {
 auth.thingPlus.authorizationUri + '?' +
 client_id: auth.thingPlus.clientId,
 response_type: 'code',
 redirect_uri: auth.thingPlus.redirectUri(

module.exports = router;
  • Callback.js
        'use strict';
        const auth = require('../auth');
        const express = require('express');
        const http = require('https');
        const request = require('request');
        const session = require('../session');
        const router = express.Router();
        function saveUserName(token, req, res) {
            var options = {
                url: auth.thingPlus.userUri,
                auth: {
                    bearer: req.session.token
            request.get(options, function(error, mesage, body) {
                req.session.userName =;
        /* GET callback page */
        router.get('/', function (req, response) {
            var options = {
                'method': 'POST',
                'hostname': auth.thingPlus.apiHost,
                'port': null,
                'path': auth.thingPlus.accessTokenUri,
                'headers': {
                    'content-type': 'application/json'
            var postRequest = http.request(options, function (res) {
                var chunks = [];
                res.on('data', function (chunk) {
                res.on('end', function () {
                    var body = Buffer.concat(chunks).toString();
                    var json = JSON.parse(body);
                    req.session.token = json.access_token;
                    saveUserName(req.session.token, req, response);
                    // response.redirect('/');
                code: req.query.code,
                client_id: auth.thingPlus.clientId,
                client_secret: auth.thingPlus.clientSecret,
                redirect_uri: auth.thingPlus.redirectUri(,
                grant_type: 'authorization_code'
        module.exports = router;
  • Auth.js
            'use strict';
            const apiHost = '';
            const baseUri = 'https://' + apiHost + '/v2/';
            module.exports = {
                thingPlus: {
                    clientId: process.env.CLIENT_ID,
                    clientSecret: process.env.CLIENT_SECRET,
                    apiHost: apiHost,
                    accessTokenUri: '/v2/oauth2/token',
                    redirectUri: function (appAddress) {
                        return 'http://' + appAddress + '/callback';
                    baseUri: baseUri,
                    authorizationUri: baseUri + 'oauth2/authorize',        
                    gatewaysUri: baseUri + 'gateways',
                    userUri: baseUri + 'users/me',
                    signupUri: '',
                    scopes: [
 What does it do?
  • auth.js contains the address of Thing+ API
  • login.js uses Thing+’s login page, and redirects to callback.js after you are logged in
  • callback.js extracts the token from Thing+ and use it for your subsequent requests


Grab your jetpacks, we are going to work with Postman next

Register client ID and secret key

This step prepares your application for OAuth. This step does not involve user credentials, all credentials mentioned
are specific for your application. Normally you should only do this ONCE per application, unless
your application require complex ACL and roles management

  1. Open Chrome browser then Sign in to the Thing+ Portal

interceptor enable

  1. Launch Postman
  2. Enable interceptor

interceptor enable

  1. Install interceptor

interceptor enable

  1. Getting a Client ID and Secret

interceptor enable

  • Select Getting a client ID and secret on the collection
  • Select the Body tab
  • Select raw
  • Put your OAuth client ID in the reqId field. This should be unique across all applications connected
    to Thing+
  • Put your OAuth client secret in the clientSecret field. These two fields should not be exposed to anyone.
    Keep it secret
  • Put the name of your applicaiton into the name field. This is used to identify your application. You
    should put either your company or service name
  • Change the field scopes to determine the rights for your application. Read more about acceptable scopes
  • Click the Send button
  1. Result should display 201 Created

interceptor enable

  1. Disable interceptor

interceptor disable

Get OAuth Access token

This step authorizes the user with Thing+ via your application

An Access token Expires in 15 days. This may be changed later without prior notification. Please check back often

  1. Prepare login page for your application
  2. When the user logins, redirect them to this URL{CLIENT_ID}&redirect_uri={REDIRECT_URI}
  • Replace {CLIENT_ID} with Thing+ OAuth Client ID to received at Step 1-4
  • Replace {REDIRECT_URI} with your callback URL. This URL should we able to take a ‘code’ parameter. For
    example http://yoururl/?code={AUTHORIZATION_CODE}

OAuth authorization screen

  1. User should click the ‘Allow’ button
  2. Thing+ will redirectsback to your {REDIRECT_URI} with the “code” in query string interceptor enable
  3. Exchange code for an OAuth Access token

This step should be automated in your code, the method below is for demonstration purpose only

exchange code with postman

  • Select Exchange code for an OAuth Access token from the collection
  • Select Body tab
  • Select x-www-form-urlencoded
  • Add {AUTHORIZATION_CODE} you received from the last step to code field
  • Fill in the client ID for your appliction in client_id
  • Fill in the client secret for your application in client_secret
  • Fill in the redirect URL. This URL should be the same with the redirect URL from step 2
  • Click the ‘Send’ button
  1. Result should say “200 OK” and provide you with an access_token

interceptor enable

Use authorisation in your app

When you are sending requests to Thing+ API, be sure to include the token you acquired from step 2 into the header with

Authorization: Bearer {AccessToken}

Putting it all together

You can now open your app, and click register or login, you’ll see a different page according to your login status.

Congratulations, you have succeeded in using Thing+’s authorisation mechanism and called some of its API.

What’s next

Add a virtual (or real) gateway, read more about the API, and try some of it.


Once you are comfortable with that, go ahead and create some rules.

You can refer to a ready-made application in this repository: thingplus-webapp

Have fun IoT-ing!


Debugging the Galaxy Tab 10.1

When I first plug the tab in, my computer automatically install all the drivers, but it won’t show up in ADB and Eclipse. I did a little searching but people were pointing me to Samsung’s developer page with a USB driver for phones. Sadly that driver just isn’t for the tab, a search on their site found nothing interesting either. It looks like Samsung can’t keep information for developers updated while they are still battling legal issues around the tab.

Fortunately, I remembered I haven’t turned debugging mode on the tab on, so I went ahead and do it, this time… Success!

Where to turn on debugging

Device in Eclipse

Now I’m installing Android 3.1 platform and looking forward for a happy time debugging 🙂