Skip to content

Conversation

@Kharchevskyi
Copy link
Contributor

@Kharchevskyi Kharchevskyi commented Feb 11, 2020

Add step approach for app loading: close #185

  • Setup Core of the project
  • Setup database and perform migration if needed
  • Setup services required for user notification (Google SDK)
  • Setup a session. Create or renew Imap session
  • Check if user has encrypted storage
  • Presenting flow for user

Remove encrypted storage access checks: close #183
Realm Migration: close #190
Minor fix for renewing access token

Copy link
Collaborator

@tomholub tomholub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see comments.

While there are a lot of nitpicks (typos and English), there are also some more important questions and requests.

Copy link
Collaborator

@tomholub tomholub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good. I'll test it and have a second look tomorrow

@tomholub
Copy link
Collaborator

But the tests are failing for some reason @Kharchevskyi

Copy link
Collaborator

@tomholub tomholub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The startupflow is over-engineered.

You have:

  • LaunchFlowController
  • FlowControllerType
  • FlowController
  • LaunchContext
  • LaunchStepType
  • LaunchFlow
  • LaunchFlowStepFactory
  • CoreLaunchStep
  • AuthLaunchStep
  • DataBaseLaunchStep
  • SessionLaunchStep
  • MainLaunchStep

All of this complexity to find out if a few things are as expected. With promises, this could be one class with 6 methods, 5 of them private.

Pseudo-code:

struct LaunchContext {
    let window: UIWindow
    let aplication: UIApplication
    let launchOptions: [UIApplication.LaunchOptionsKey: Any]?
}

class StartupChecks {
  
  public fun runChecks(context: LaunchContext): Promise<void>  {
    return Promise.all([
      this.checkCore(context),
      this.checkAuthentication(context),
      this.checkDatabase(context),
      this.checkSession(context),
      this.checkMain(context)
    ]);
  }

  private fun checkCore(context: LaunchContext): Promise<void> {
    return Promise<void> { _ in
        DispatchQueue.promises = .global()
        core.startInBackgroundIfNotAlreadyRunning()
    }
  }

  // private fun checkAuthentication() ...

  // ...
}

It's faster to write, less effort to maintain. The more classes and abstractions you hide a problem behind, the harder it is to solve the problem.

Keep it simple. Simple code is easy to read. Simple code is easy to understand. Simple code is easy to review for security.

I'll refactor it.

@tomholub
Copy link
Collaborator

tomholub commented Feb 13, 2020

todo:

  • test migration
  • remove local storage token after migration done
  • test AppStartup error rendering
  • fix failing tests
  • run UITests
  • encryptedConfiguration cannot be optional

@tomholub
Copy link
Collaborator

Cannot run UI tests, stuck on #191

tomholub
tomholub previously approved these changes Feb 13, 2020
Copy link
Collaborator

@tomholub tomholub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok, but please also review it @Kharchevskyi

@Kharchevskyi
Copy link
Contributor Author

This solution is not testable. And not scalable.
This complexity was added for the purpose.

Every of this launch steps ( CoreLaunchStep, AuthLaunchStep,DataBaseLaunchStep,SessionLaunchStep,MainLaunchStep) can be tested separately or mocked for tests. All of them has it's own responsibility.

LaunchFlow and LaunchFlowStepFactory create steps, base on a context. In case we receive push notification or open app from deeplink it will create Launchsteps which are responsible for handling events.

I don't agree about over-engineering

This was scalable, testable and reusable. Now it's only one god object class.

@tomholub
Copy link
Collaborator

To me, simplicity is more important. I need our code to be easily readable and reviewable, and I need it to be of minimal complexity.

Every of this launch steps ( CoreLaunchStep, AuthLaunchStep,DataBaseLaunchStep,SessionLaunchStep,MainLaunchStep) can be tested separately or mocked for tests. All of them has it's own responsibility.

Factory/step approach:

  • 381 lines over 12 classes, 11 files
  • difficult to follow business logic
  • each piece testable individually
  • difficult to do security review

Single class approach:

  • 60 lines, 1 class
  • easy to follow business logic, chronological
  • hard to test individually
  • trivial security review

For our usecase, the tradeoffs are different from a regular app. We need simplicity, readability above everything else.

LaunchFlow and LaunchFlowStepFactory create steps, base on a context. In case we receive push notification or open app from deeplink it will create Launchsteps which are responsible for handling events.

To handle deeplinks, edit chooseView. Add an argument containing the possible deeplink + 1 if.


The added complexity comes with a tradeoff, and the tradeoff is not worth it for us.

@tomholub
Copy link
Collaborator

As for testing, consider this:

  • CoreLaunchStep - we already test core extensively
  • AuthLaunchStep - just fills a few constants, nothing to test
  • DataBaseLaunchStep - if this fails, no UITests will work, already tested. Plus we aren't going to test migrations, anyway.
  • SessionLaunchStep - if this doesn't work, it will show in other tests
  • MainLaunchStep - This can be easily tested with UITests

I prefer tests that are not tightly coupled to implementation details. Any tests you write that would be testing the factory/step design directly would necessarily be tightly tied to implementation. That is not the kind of tests I want to focus on.

@tomholub tomholub merged commit 5b9beba into master Feb 14, 2020
@tomholub tomholub deleted the feature/issue-185-app-loading-checks branch February 10, 2021 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

realm migration from 0.1.6 to 0.1.7 app loading initial checks remove canHaveAccessToStorage and accessCheck from EncryptedStorage

4 participants