• iOS
  • Android
  • Server-to-Server
  • Networks
  • Usage

    The call to start the Mira SDK should be done as early as possible in your application lifecycle. The most common location will be in your UIApplicationDelegate instance. By default, the SDK will log interesting beacon events to the console and post a notification named "MiraEvent" with the log string. You can turn these off by calling [MiraSDK setLoggingEnabled:NO] or MiraSDK.setLoggingEnabled(false) as shown below.

    // Objective-C
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
        [MiraSDK startWithAPIKey:@"YOUR API KEY HERE"];
    
        // Turn off logging
        // [MiraSDK setLoggingEnabled:NO];
    
        ...
    
        return YES;
    }
    // Swift
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    
        MiraSDK.startWithAPIKey("YOUR API KEY HERE")
    
        // Turn off logging
        // MiraSDK.setLoggingEnabled(false)
    
        ...
    
        return true;
    }

    Info.plist

    You will need to add the following two UIBackgroundModes to your Info.plist file:

    <key>UIBackgroundModes</key>
    <array>
      <string>bluetooth-central</string>
      <string>location</string>
    </array>

    The location mode is to allow background updates when a user comes within range of a beacon. The bluetooth-central mode is to allow the SDK to write a small packet of information called a “genotype” to proprietary Mira-controlled beacons found in elevators, taxi cabs, subways, etc. where there is little to no WiFi or cellular connectivity.

    In addition, you will need to add the NSLocationAlwaysUsageDescription and NSLocationUsageDescription keys to allow your app to receive beacon events in the background. Otherwise, beacon events will only be recorded when your app is active.

    <key>NSLocationAlwaysUsageDescription</key>
    <string>We monitor your iBeacon interactions for advertising purposes.</string>
    <key>NSLocationUsageDescription</key>
    <string>We monitor your iBeacon interactions for advertising purposes.</string>

    You may change the descriptions to better suite your application’s needs.

    Installation

    The SDK targets iOS 7.0+ and is published as a CocoaPod. To use it, simply add the following line to your Podfile:

    pod "MiraSDK"

    Although not required, it is suggested to add the use_frameworks! tag to your Podfile when integrating the Mira SDK into a iOS project built with Swift.

    You can also view/copy the code in our Github repo. In that case, you will need to link to the following frameworks:

    AdSupport
    CoreBluetooth
    CoreLocation
    SystemConfiguration
    Foundation
    UIKit
  • Installation

    The SDK effectively requires API level 18 (Android 4.3/Jelly Bean), but will compile on API level 10 and later. On systems earlier than API level 18 or those without Bluetooth LE functionality, the SDK will effectively be inert. We perform runtime checks of system compatibility, and as such most developers won’t have to change their minSdkLevel

    For those using a Gradle-based build system, the SDK can be included as a dependency and is available from the jcenter repository. jcenter is the default repository in Android Studio projects, but can be manually included as well.

    repositories {
      jcenter() // Should be included by default
    }
    dependencies {
      compile 'co.mira:mira-android:1.1.+'
    }

    If you are using Android Studio with Gradle, the AndroidManifest.xml requirements from above will be automatically merged into your project manifest. For other IDEs/build systems, the Mira SDK and its dependencies can be added as JAR files, but the manifest file must be updated manually. Add the following dependencies to your project:

    Permissions

    These permissions are included in the library’s manifest:

    • INTERNET
    • RECEIVE_BOOT_COMPLETED
    • BLUETOOTH
    • BLUETOOTH_ADMIN
    • ACCESS_COARSE_LOCATION (for >23)

    Runtime (for 23 and greater):

    • ACCESS_COARSE_LOCATION

    AndroidManifest.xml

    In addition to the above permissions, the application requires that the following service and receiver be declared in the <application> element.

    <service android:name="co.mira.android.AdvertisingService"/>
    <receiver android:name="co.mira.android.MiraReceiver"
        android:enabled="false"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>

    Most IDEs will perform an automatic manifest merger (union of all AndroidManifest.xml files) between your modules while compiling your application. If this is not the case for your environment, use the Mira SDK Manifest as a guide.

    Note: Some optional functionality makes use of the ACCESS_FINE_LOCATION permission, but we don’t require it and we won’t ask for it, even if the host app asks Mira to handle runtime permissions (see app2).

    Configuration

    Most developers will simply want to start the Mira SDK in the onCreate() method of your Application subclass or main Activity subclass. However, with the addition of runtime permissions in Android Marshmallow (6.0; SDK Level 23), the function you call may depend on the permissions your app currently uses, or how you want to handle runtime permissions.

    We’ve included two application modules in our repository that illustrate two ways of dealing with permissions - both manifest/install and runtime - for the Mira SDK. Runtime permissions are only relevant to apps targeting Android Marshmallow (API Level 23) and above, which now requires applications to not only declare permissions in the manifest (presented to user at install time), but also to obtain on-the-fly user approval of certain “dangerous” permissions at runtime (see Android 6.0 changes for details). The difference between the two configurations boils down to which component is declaring manifest permissions and obtaining runtime permissions: the SDK or the host application?

    Host App Handles Permissions (app)

    This application implements the case in which the host application (you) handles obtaining runtime permissions. In this case, the host application already has some functionality that uses the ACCESS_COARSE_LOCATION permission. In the sample, Mira.start() is called in the onCreate() method of the Activity. This will start the necessary components of the Mira SDK if the permissions already exist. If the permissions do not exist, it does nothing.

    The host application must already have some logic to handle when permissions are granted if it is already handling runtime permissions. You can optionally add Mira.start() again in the permission request result handler, to guarantee that it runs.

    public class MainActivity extends Activity {
      private static final String API_KEY = "YOUR API KEY HERE";
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
    
        ...
    
        Mira.start(this, API_KEY);
      }
    
      ...
    
      @Override
      public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
          switch (requestCode) {
              case PERMISSION_REQUEST_COARSE_LOCATION: {
                  if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                      Log.d(TAG, "Permissions granted!");
                      doHostAppLocationTask();
                      Mira.start(this, API_KEY);
                  } else {
                      Log.w(TAG, "Permissions rejected!");
                  }
                  return;
              }
          }
      }
    }

    This is the preferred method of integrating as it gives the app developer more control over timing and messaging (what the dialog says).

    Mira Handles Permissions (app2)

    This application demonstrates how to run Mira if the host application doesn’t currently do anything with permissions, runtime or manifest.

    This application does not do anything within its own scope that requires runtime permissions, so it lets Mira ask for them. In this case, the function that is called is Mira.startOrPermissions, which either starts the Mira SDK if the runtime permissions are already granted, or prompts the user to give the current activity runtime permissions.

    In order to work properly, Mira needs to get notification that the permission was granted, so override the onRequestPermissionResult method of your application and pass the result to Mira.handlePermissions.

    public class MainActivity extends Activity {
      private static final String API_KEY = "YOUR API KEY HERE";
      private Mira mira;
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
    
          ...
    
          mira = Mira.getInstance(this);
          mira.startOrPermissions(this, API_KEY);
      }
    
      @Override
      public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
          mira.handlePermissions(requestCode, permissions, grantResults);
      }
    }
  • Usage

    If your app is already collecting beacon bumps or location information, it’s possible to forward us these events as they arrive. Data can be sent via an HTTPS POST request to https://api.mira.co/partner/LocationEvent.

    Below is an example event and an explanation of required and optional fields.

    {
      "apiVersion": 1,
      "apiKey": "5196d8569179496198529a375bd3fad5",
      "advertisingId": "0a31b055-4c18-44f9-b30a-c37dd29fe803",
      "beacon": {
        "uuid": "56de7c19-7508-4351-bd58-379eadc5aa2e",
        "major": 1,
        "minor": 2
      },
      "location": {
        "latitude": 36.090519,
        "longitude": -79.801034,
        "accuracy": 10
      },
      "timestamp": "2015-12-23T15:28:52-05:00",
      "sourceIp": "63.118.185.98",
      "os": "ios"
    }

    Required fields

    apiVersion - currently 1.

    apiKey - your API key.

    advertisingId - the advertising id is the UUID provided by the Android or iOS operating system for the purpose of mobile advertising. They are known variously as the Android ADID or IDFA.

    beacon and/or location - at least one of these must be present. If you have information on both, send both. Note: the accuracy field is in meters and is optional.

    Optional fields

    timestamp - ISO 8601 timestamp of when the event was triggered. If there is a significant lag between the event hitting your server and your notifying us, please provide this field.

    sourceIp - the IP address of the device as received by your server.

    os - either "ios" or "android".

  • Overview and Integration Guide for OOH Networks

    Updated June 21, 2017

    This document is meant to serve as an overview of the integration process between Mira and digital out-of-home networks. It is intended for network administrators, engineers, and other managers responsible for the technical health of their ad serving and content management systesms

    What is Mira?

    Mira is the crowd analytics platform. Crowd analytics seeks to answer the question: what types of people are in a given place at given time? Mira uses historical and real-time location and behavioral data to place a digital overlay on physical locations. This data can be used to optimize out-of-home campaigns, by informing the decision of which content to show based on the real-time audience.

    Architecture Brief

    Mira’s server maintains a real-time ledger of what content every device should be playing for a given campaign. As new data streams in, it is fed through our models which update the ledger if necessary. There is a web of internal mappings and logical groupings that affect this. For example, clusters of devices can be grouped together to access the same entry in the ledger, so that they are always using the same decision. There are other parameters that control this, but this is irrelevant to network owners as these are purely demand-side variables.

    This ledger is updated independently of what the signs are doing. It doesn’t matter if there is a schedule for the content. Our server simply answers the question “what should I play right now?” when it is asked, where “I” can be an entire network or just one device.

    Most of the integration centers around asking that question, and getting the answer to translate into something on the screen. There are a few standard delivery mechanisms (see below), but custom solutions are pretty simple to implement. Something has to hit our server, and the “slot” must be configurable to change what is shown, either directly through the CMS (think RSS feeds for weather), or through a dynamic file type that reads this data.

    Content and Naming

    Every piece of content/creative/asset involved in a Mira campaign is assigned a unique URI. When our server makes a decision, the result is the URI of the content that it has decided to show. As an example, suppose one of the assets is a movie named named Mira_Test_Campaign_2016.mp4. Mira creates a URI for this asset:

    s3://mira-assets/7e/0a/7e0ac2c9c04a4c37b8253052790e072c.mp4

    This happens to be an S3 URI, but this is irrelevant. The asset is uniquely identifiable by way of its name.

    API Documentation

    GET /decide

    Decide is the interface to Mira’s ad server.

    It returns either a single decision (for one campaign/device combo), or an array of decisions (for one campaign). The latter can be used in situations where the OOH Network wants to obtain the relevant decisions for all signs in the network corresponding to a particular campaign.

    Parameters
    • api_key (required) - your network/campaign API key, generated by Mira. This will throw a 403 Forbidden if absent or invalid.
    • campaign_id (required) - the identifier of the campaign being decided, generated by Mira (required). This will throw a 400 Bad Request if absent or invalid.
    • device_id - the identifier of a particular device, generated by Mira
    • test - for testing purposes, can set this parameter to 1 to serve a preconfigured decision. This will throw a 400 Bad Request if there are no preconfigured test decisions.
    Response Examples

    /decide?api_key=780f2f38504c425fbc3fb0284cdb5908&campaign_id=12345&device_id=1928

    {
      "campaign_id": 12345,
      "decision": {
        "creative_id": 8123094,
        "decision_uri": "s3://mira-assets/7e/0a/7e0ac2c9c04a4c37b8253052790e072c.mp4",
        "device_id": 1928,
        "model_instance": 45093
      },
      "network_id": 6789,
      "timestamp": "2016-10-12T15:16:56.336145"
    }

    /decide?api_key=780f2f38504c425fbc3fb0284cdb5908&campaign_id=12345

    {
      "campaign_id": 12345,
      "decisions": [
        {
          "creative_id": 8123094,
          "decision_uri": "s3://mira-assets/7e/0a/7e0ac2c9c04a4c37b8253052790e072c.mp4",
          "device_id": 1928,
          "device_name": "Test Sign 1",
          "model_instance": 45093
        },
        {
          "creative_id": 8123095,
          "decision_uri": "s3://mira-assets/d7/e6/d7e6805d6ebe49579335af0d6bfc4b9e.mp4",
          "device_id": 1929,
          "device_name": "Test Sign 2",
          "model_instance": 45094
        },
        {
          "creative_id": 8123094,
          "decision_uri": "s3://mira-assets/7e/0a/7e0ac2c9c04a4c37b8253052790e072c.mp4",
          "device_id": 1930,
          "device_name": "Test Sign 3",
          "model_instance": 45095
        }
      ],
      "network_id": 6789,
      "timestamp": "2016-10-12T15:47:02.414577"
    }

    NB: The creative_id is a different identifier than the unique resource identifier corresponding to the asset.

    POST /track

    The tracking endpoint logs out-of-home impressions. It is called after a decision is made and the asset is rendered. Should always respond with 200 OK, even if payload is improperly configured.

    Parameters
    • api_key (required) - your network/campaign API key, generated by Mira.
    • campaign_id (required) - the identifier of the campaign being tracked, generated by Mira.
    • device_id (required) - the identifier of the device that rendered the impression, generated by Mira.
    • creative_id (required) - the identifier of the creative that was rendered, generated by Mira.

    Delivery Mechanisms

    Delivery mechanisms are, to use inelegant terms, how we get our dynamic content on your screens. How this happens is usually dictated by the CMS.

    Remote URL

    Your CMS “visits a website” that shows the appropriate content. This is the easiest way to integrate if your CMS can fetch HTML content from a remote source. The /play endpoint wraps around the /decide endpoint and renders the asset that is returned. The wrapper responds with an HTML5 page sized appropriately for your device, and the decided content. This also eliminates the need to call the /track endpoint. This requires reliable Internet connectivity.

    HTML5 Bundle

    If your system does not have reliable Internet or cannot fetch remote URLs, but can render HTML5 content (i.e. has a browser/web rendering engine), then an HTML5 bundle might be the best option. In this scenario, we deliver a zip file with the following structure:

    .
    ├── bundle.js
    ├── assets
    │   ├── mira
    │   │   ├── 4e3d8c38211d45a9bebba2c2402bf4ef.png
    │   │   ├── 7ca9a504814644edb544aae0d558066b.png
    │   │   └── e7cdb5609f4e4f5c88458a4ed0501f16.png
    │   └── placeholder.png
    ├── index.html
    └── styles
        └── css
            ├── global.css
            └── normalize.css

    The index.html file is a container that executes code from bundle.js, which is configured to ping the /decide endpoint with the appropriate parameters for the slot the bundle is to be schedule in. After receiving a response, the container’s DOM is manipulated to show the appropriate content.

    Flash Bundle

    Same idea as an HTML5 bundle, but the server is pinged from ActionScript. If you are using a CDN already with your CMS, we can place all assets associated with a Mira campaign in your Sync directory. Then, the Flash container can pull these assets in directly from the filesystem, by reading the URI returned from our server. We called this the “External Flash” Method.

    We can additionally embed all creative for a campaign into the flash movie itself, and map the possible decisions to individual “scenes” in the Flash movie. We call this the “Embedded Flash” method.

    CMS-Based System

    Many networks and CMSs have their own data fetching and routing system. One very common use of this type of system is to fetch weather data from some RSS feed. In this method, the CMS itself performs the data fetching and is responsible for affecting the displayed asset. Usually, each asset in the campaign will have a simple mapping rule configured in the CMS, each of which is ultimately mapped to a file. The CMS then extracts the decision URI from Mira’s responses, and uses its rules to determine what to play.

    Custom

    We can work with a variety of custom configurations. These revolve around some system hitting the /decide endpoint for the campaign/device, and making that data available to some container that can alter the default display behavior.

The Mira SDK needs an authorized API key to function. If you need an API key, please email support@mira.co.