Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Monzo's Android app - Londroid 22nd Novemeber

Emma Guy
November 22, 2016

Monzo's Android app - Londroid 22nd Novemeber

Building a Modern Mobile Banking App

Just over 18 months ago we set out to build the ‘best bank on the planet’ from scratch. Today with 45,000 users and counting, our goals remain the same; to make managing your money as simple as ordering a pizza or a taxi. Join us for an introduction to the newly released Android beta, an insight into the challenges faced and a glance at the possibilities for the future, from the engineering team who made it happen.

Speakers:

Emma Guy @emmaguy

Emma is an opinionated Android developer at Monzo, endlessly enthusiastic about making the app much better than its iOS counterpart. Prior to working at Monzo, she was at ustwo, working on a wide variety of different apps.

Ivan Carballo @ivacf

Ivan is an enthusiastic Android developer who has been building Android apps for over five years. Frustrated with traditional banking apps, he has recently joined the Monzo Android team to help create a modern banking experience that people love.

Emma Guy

November 22, 2016
Tweet

More Decks by Emma Guy

Other Decks in Technology

Transcript

  1. View Slide

  2. View Slide

  3. ● Real time notifications
    ● Freeze your card instantly, unfreeze
    when you find it
    ● Spending insights to help budget
    ● Use abroad with no fees or charges
    ● Open API

    View Slide

  4. History of the Android app
    ● Started as a waiting list app only - released May 2016
    ○ ‘Queue simulator’, couldn’t get a card
    ● Building the app started mid June 2016
    ● Pushed updates into the Google Play Beta channel
    ○ To ~1500 users with cards, who visited our offices
    ● v1.0 launched on 29th September 2016

    View Slide

  5. The Android app today
    ● ~14k users have made their first transaction and logged in
    ● ~5-7k daily active users
    ● £5.7 million spent by Android users
    ● ~20k users in London
    ● 2 developers

    View Slide

  6. App setup
    ● minSdk 21
    ● Otherwise, pretty standard setup
    ○ Model View Presenter (MVP)
    ○ RxJava, RxBinding
    ○ Realm, Retrofit, OkHttp, Gson
    ○ Dagger2
    ○ Mockito, JUnit, Espresso

    View Slide

  7. Tell me more!
    ● Our MVP setup, and how we use it with Realm
    ● Magic Link
    ● Identity Verification (aka Know Your Customer)
    ● Top Up

    View Slide

  8. Opinionated MVP
    ● MVP & RxJava
    ● Interface (package private) on the Presenter, containing:
    ○ actions the user can perform, e.g. refreshing the feed
    ○ view updates - e.g. show a progress bar
    interface View extends BasePresenter.ViewInterface {
    Observable onRefreshAction();
    void showLoading();
    void hideLoading();
    }

    View Slide

  9. Opinionated MVP
    @Override
    public void register(View view) {
    super.register(view);
    addToUnsubscribe(view.onRefreshAction()
    .map(ignored -> accountService.getUser().getAccount().id)
    .doOnNext(ignored -> view.showLoading())
    .switchMap(accountId -> feedRepository
    .fetchFeedItemsFromApi(accountId)
    .subscribeOn(ioScheduler)
    .observeOn(uiScheduler)
    .onErrorResumeNext(throwable -> {
    // handle error
    return Observable.just(null);
    }))
    .subscribe(ignored -> view.hideLoading(),

    View Slide

  10. Opinionated MVP
    ● Presenter should be pure Java
    ● Subscribe to each action when the view is attached
    ● Unsubscribe when the view is detached
    ○ via a CompositeSubscription on the BasePresenter
    ● Observable
    ○ Enough to know it happened, in many cases
    ● No need for a Presenter interface

    View Slide

  11. Opinionated MVP - benefits
    ● Obvious what happens as a consequence of an action
    ● Separate our concerns
    ○ Presenter should be view agnostic
    ○ View easy to change in the future - button click -> swipe
    ○ Dumb view, often methods are just a simple one liner e.g.
    progressBar.setVisibility(View.VISIBLE)
    ● View can attach/detach whenever, nothing will run after detach

    View Slide

  12. Opinionated MVP - benefits
    ● Ease of unit testing
    ○ When we’re doing a network request, does the progress bar
    show when it starts, and hide when it ends?
    ○ Are we ignoring other refreshes if the user is continually
    refreshing?
    ○ What happens when it errors?
    ○ If some operation is timed, we can use a TestScheduler

    View Slide

  13. Opinionated MVP - unit tests
    @Test public void onRefreshAction_refreshesFromApi() {
    homeFeedPresenter.register(view);
    refreshSubject.onNext(null);
    verify(feedRepository).fetchFeedItemsFromApi(anyString());
    }

    View Slide

  14. Opinionated MVP - unit tests
    @Test public void fetchFeedItemsFromApi_networkError_showsError() {
    homeFeedPresenter.register(view);
    when(feedRepository.fetchFeedItemsFromApi(any()))
    .thenReturn(Observable.error(new IOException("network fail")));
    refreshSubject.onNext(null);
    verify(view).showError(.....);
    }

    View Slide

  15. Realm
    ● Retrieve from the API, persist in Realm
    ● App reads from Realm, doesn’t directly update from the API
    ● Subscribe to hot observables
    public Observable> getFeedItems() {
    final Realm realm = Realm.getDefaultInstance();
    return realm.where(FeedItem.class)
    .findAllAsync()
    .asObservable()
    .map(realm::copyFromRealm);
    }

    View Slide

  16. Realm - using hot observables
    ● Emit whenever something changes e.g. user on the feed
    ○ a push notification is received which triggers a refresh
    ○ the view will be automatically updated
    addToUnsubscribe(feedRepository.getFeedItems()
    .subscribe(feedItems -> {
    if (isFeedEmpty()) {
    view.setFeedItems(feedItems);
    } else {
    view.updateFeedItems(feedItems);
    }
    }));

    View Slide

  17. Magic Link
    Because passwords suck

    View Slide

  18. Magic link
    ● Once upon a time, we had email/password auth
    ● … but no reset password mechanism, except for
    contacting customer support
    ■ Turns out, people forget stuff
    ■ Quickly created a UX and operations burden
    ● Passwords are bad for other reasons
    ○ Can be guessed, brute forced, key logged… etc

    View Slide

  19. Magic link - user flow

    View Slide

  20. Magic link
    ● Instead of emailing a link to reset their password, email
    an auth token in the form of a link
    ○ Compromising an email account is still fatal
    ○ At least now there’s no password which also grants
    you access
    ● In the future, magic link will only grant you read only
    access to your account - not sending money, etc

    View Slide

  21. Magic link - dev flow

    View Slide

  22. Magic link - why the state?
    ● SHA256(rnd + email) so it’s unpredictable for external entity
    ● Can verify it’s the same device which requested this login link
    ● Protects us against attacks such as:
    ○ Evil McEvilface creates fake account
    ○ Evil inserts feed item saying “topup required, please send
    money here”
    ○ Evil sends the login link to Victim McVictimface who clicks
    it and gets logged in as the fake account
    ○ Victim follows the instructions

    View Slide

  23. Know Your Customer (KYC)
    Process of identifying and verifying the identity of customers

    View Slide

  24. View Slide

  25. View Slide

  26. View Slide

  27. Implementation

    View Slide

  28. Camera 2 API
    Easy, right?

    View Slide

  29. Camera2 Basic Sample
    https://github.com/googlesamples/android-Camera2Basic

    View Slide

  30. Camera2
    Basic sample
    https://github.com/googlesamples/android-Camera2Basic

    View Slide

  31. 1035 lines of code!

    View Slide

  32. CameraView library
    https://github.com/google/cameraview

    View Slide

  33. android:id="@+id/camera"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:keepScreenOn="true"
    android:adjustViewBounds="true"
    app:autoFocus="true"
    app:aspectRatio="4:3"
    app:facing="back"
    app:flash="auto"/>

    View Slide

  34. @Override
    protected void onResume() {
    super.onResume();
    cameraView.start();
    }
    @Override
    protected void onPause() {
    cameraView.stop();
    super.onPause();
    }

    View Slide

  35. cameraView.addCallback(new CameraView.Callback() {
    @Override
    public void onPictureTaken(CameraView cameraView, byte[] pictureData) {
    // Create Bitmap or save to file
    }
    });
    cameraView.takePicture();

    View Slide

  36. Monzo’s CameraView fork
    https://github.com/monzo/cameraview
    ● Adds video recording capabilities
    ● Smaller - removes support for devices with API < 21
    ● Fixes a few bugs

    View Slide

  37. Still not perfect
    ● Inconsistent behaviour across different devices
    ● Generally works well on Nexus devices
    ● Several issues on other devices:
    ○ Unable to autofocus
    ○ Video and audio out of sync
    ○ Orientation issues

    View Slide

  38. Some advice
    ● Avoid using the camera API if possible

    View Slide

  39. Some advice
    ● Avoid using the camera API if possible
    ● Use the device camera app instead
    ● You can easily open the device camera app using Intents
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);

    View Slide

  40. Card Top Ups

    View Slide

  41. £6 million
    Topped up through the Android app

    View Slide

  42. View Slide

  43. Implementation
    ● Stripe for debit card payments
    ● 3-D Secure support

    View Slide

  44. 3-D Secure (3DS)
    A protocol designed to be an additional security layer for online
    debit/credit card transactions

    View Slide

  45. View Slide

  46. View Slide

  47. @emmaguy @ivacf
    Thank you!

    View Slide