Monzo's Android app - Londroid 22nd Novemeber

A8aac549f52be897a2098b280acffea0?s=47 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.

A8aac549f52be897a2098b280acffea0?s=128

Emma Guy

November 22, 2016
Tweet

Transcript

  1. None
  2. None
  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
  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
  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
  6. App setup • minSdk 21 • Otherwise, pretty standard setup

    ◦ Model View Presenter (MVP) ◦ RxJava, RxBinding ◦ Realm, Retrofit, OkHttp, Gson ◦ Dagger2 ◦ Mockito, JUnit, Espresso
  7. Tell me more! • Our MVP setup, and how we

    use it with Realm • Magic Link • Identity Verification (aka Know Your Customer) • Top Up
  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<Void> onRefreshAction(); void showLoading(); void hideLoading(); }
  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(),
  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<Void> ◦ Enough to know it happened, in many cases • No need for a Presenter interface
  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
  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
  13. Opinionated MVP - unit tests @Test public void onRefreshAction_refreshesFromApi() {

    homeFeedPresenter.register(view); refreshSubject.onNext(null); verify(feedRepository).fetchFeedItemsFromApi(anyString()); }
  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(.....); }
  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<List<FeedItem>> getFeedItems() { final Realm realm = Realm.getDefaultInstance(); return realm.where(FeedItem.class) .findAllAsync() .asObservable() .map(realm::copyFromRealm); }
  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); } }));
  17. Magic Link Because passwords suck

  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
  19. Magic link - user flow

  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
  21. Magic link - dev flow

  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
  23. Know Your Customer (KYC) Process of identifying and verifying the

    identity of customers
  24. None
  25. None
  26. None
  27. Implementation

  28. Camera 2 API Easy, right?

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

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

  31. 1035 lines of code!

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

  33. <com.google.android.cameraview.CameraView 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"/>

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

    void onPause() { cameraView.stop(); super.onPause(); }
  35. cameraView.addCallback(new CameraView.Callback() { @Override public void onPictureTaken(CameraView cameraView, byte[] pictureData)

    { // Create Bitmap or save to file } }); cameraView.takePicture();
  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
  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
  38. Some advice • Avoid using the camera API if possible

  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);
  40. Card Top Ups

  41. £6 million Topped up through the Android app

  42. None
  43. Implementation • Stripe for debit card payments • 3-D Secure

    support
  44. 3-D Secure (3DS) A protocol designed to be an additional

    security layer for online debit/credit card transactions
  45. None
  46. None
  47. @emmaguy @ivacf Thank you!