Introduction

There are several ways to embed the Connect widget in your native app. This guide will take you through embedding Connect in an iOS app via a WebView using our iOS demo app. There are other ways to do this, but we recommend the integration discussed here; it’s by far the most common approach. We’ll also go over some best practices.

While working through this guide, it’s also a good idea to use our demo application to follow along.


1. Get a widget URL

The first thing you’ll need to do is request a Connect URL using the request widget URL endpoint in the Platform API. There are all sorts of ways to configure Connect using this endpoint, but with webviews, it is particularly important to get the right options. We need to use is_mobile_webview, ui_message_version, and ui_message_webview_url_scheme.

is_mobile_webview determines how the widget communicates. If set to true the widget will send messages via window.location = '...' instead of window.postMessage(...).

ui_message_version ensures you get the right messages.

ui_message_webview_url_scheme is used to identify messages and OAuth redirects.

Example URL request

1
2
3
4
5
6
7
8
9
10
11
12
curl -i -X POST 'https://int-api.mx.com/users/{user_guid}/widget_urls' \
-u 'client_id:api_key' \
-H 'Accept: application/vnd.mx.api.v1+json' \
-H 'Content-Type: application/json' \
-d '{
      "widget_url": {
        "widget_type": "connect_widget",
        "is_mobile_webview": true,
        "ui_message_version": 4,
        "ui_message_webview_url_scheme": "appscheme"
      }
    }'

Example URL response

1
2
3
4
5
6
7
{
  "widget_url": {
    "type": "connect_widget",
    "url": "https://int-widgets.moneydesktop.com/md/connect/yxcdk7f1nb99jwApp34lA24m0AZ8rzprgmw17gm8z8h2AzjyAnd1rj42qfv42r3xnn07Amfwlg3j09hwp8bkq8tc5z21j33xjggmp2qtlpkz2v4gywfhfn31l44tx2w91bfc2thc58j4syqp0hgxcyvA4g7754hk7gjc56kt7tc36s45mmkdz2jqqqydspytmtr3dAb9jh6fkb24f3zkfpdjj0v77f0vmrtzvzxkmxz7dklsq8gd0gstkbhlw5bgpgc3m9mAtpAcr2w15gwy5xc4blgxppl42Avnm63291z3cyp0wm3lqgmvgzdAddct423gAdqxdlfx5d4mvc0ck2gt7ktqgks4vxq1pAy5",
    "user_id": "U-jeff-201709221210"
  }
}

2. Handle navigation events

WebViews bring some undesireable baggage for integrations, particularly:

  • How does the native app ‘talk’ to the widget?
  • What happens when the user tries to visit a link in the widget to an external site?

The answer to both of these questions is navigation events. Your native app will need to intercept all events and decide to either ‘block’ the event — if that event is actually a message from the widget — or allow the event and send the user to the browser — like when a user is trying to visit a bank’s site or trying to authenticate via OAuth.

For a good example of handling events, see the demo app code.

Handling events in iOS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class ConnectController: UIViewController, WKNavigationDelegate {
    var webView: WKWebView!
    /**
     `appScheme` needs to match the `ui_message_webview_url_scheme` configuration value, if set.
     Most navigation events will use that scheme instead of `mx://`.
     */
    let appScheme = "appscheme://"
    let mxScheme = "mx://"

    /**
     Handle all navigation events from the webview. Cancel all navigation events that start with your appScheme`, or `mxScheme`.
     */
    func webView(_ webView: WKWebView,
                 decidePolicyFor navigationAction: WKNavigationAction,
                 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        let url = navigationAction.request.url?.absoluteString
        let isPostMessageFromMX = url?.hasPrefix(appScheme) == true || url?.hasPrefix(mxScheme) == true

        if (isPostMessageFromMX) {
            // Handle the event from MX.
            decisionHandler(.cancel)
            return
        }

        // Only allow requests with great caution. Allowing a navigation action
        // could navigate the user away from Connect and lose their session.
        decisionHandler(.allow)
    }
}

3. OAuth: Redirect out to the OAuth provider

OAuth flows in WebViews will require you to facilitate the redirect to the OAuth Provider, as well as let MX know how to get back to your native App.

To facilitate the redirect out, you will need to capture the oauth requested message, get the URL from the payload, and send the user to that URL.

See a good example of handling OAuth in the demo app.

Example WebView Message

appscheme://connect/oauthRequested?metadata={...JSON encoded payload ...}

Example Redirect URL

https://widgets.moneydesktop.com/oauth/predirect_to/MBR-f1e7a927-924f-4b31-a8da-61de02954d74/4wa...snip...02?referral_source=BROWSER&skip_aggregation=false&ui_message_webview_url_scheme=appscheme

Example OAuth Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func webView(_ webView: WKWebView,
             decidePolicyFor navigationAction: WKNavigationAction,
             decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    let url = navigationAction.request.url?.absoluteString
    let isPostMessageFromMX = url?.hasPrefix(appScheme) == true || url?.hasPrefix(atriumScheme) == true

    if (isPostMessageFromMX) {
        let urlc = URLComponents(string: url ?? "")
        let path = urlc?.path ?? ""
        // There is only one query param ("metadata") with each url, so just grab the first.
        let metaDataQueryItem = urlc?.queryItems?.first

        if path == "/oauthRequested" {
          // Handle OAuth redirect here.
        }

        decisionHandler(.cancel)
        return
    }

    decisionHandler(.allow)
}

4. OAuth: Get the user back to your app

To get back to your app, you’ll want to make sure you set the ui_message_webview_url_scheme to a scheme that your app can respond to when linked from a browser. Once OAuth is complete, the MX page that the user is sent to will try to link back to your app via a URI.

Example URI back to your app

appscheme://oauth_complete?status=<success|error>&member_guid=...

If the status field is success, you can simply continue showing the connect widget. It will continue as usual. A success message at this point means we were able to successfully get an OAuth token and have started a job. You’ll want to let Connect finish doing it’s thing before moving on.

If the status field is error, you will want to close the widget and show a generic error view that is appropriate for your application. An error at this point can mean anything from a user rejecting OAuth terms to an internal server error. We are working on adding better error messaging, but for now, just plan on making a generic error page to use as a catch all for now and the future.

In iOS, this can be done in the Info.plist. The demo app has been set up to respond to any URI that links to appscheme://.