-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
issue 1111 only initialize Realm and KeyChainService once #1117
Conversation
Sorry for another monstrous PR. The main goal of this PR was:
For this I created While I was doing it, certain things kept getting in my way, in particular magical
While doing this, I noticed some classes are used across the app and rely on storage, and so I've added them into Things that bother me about this PR and will need to get cleaned up (hopefully in other PRs):
As of now, the app builds and runs and I didn't run into a bug yet. But Swift tests are likely broken. I'll have a look. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some highlights below
class CheckMailAuthViewController: TableNodeViewController { | ||
private let globalRouter: GlobalRouterType | ||
private let appContext: AppContext | ||
|
||
init(globalRouter: GlobalRouterType = GlobalRouter()) { | ||
init(appContext: AppContext, globalRouter: GlobalRouterType = GlobalRouter()) { | ||
self.appContext = appContext | ||
self.globalRouter = globalRouter | ||
super.init(node: TableNode()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Context now gets passed as first argument of every VC init.
Next step will be to pass User
(not optional) into every VC that requires it. #863
self.foldersProvider = foldersProvider ?? FoldersService( | ||
encryptedStorage: appContext.encryptedStorage, | ||
remoteFoldersProvider: appContext.getRequiredMailProvider().remoteFoldersProvider | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I initialized FolderService
many times this way, good candidate to go into AppContext
(a method, maybe, similar to EmailProvider
)
FlowCrypt/Functionality/DataManager/Encrypted Storage/KeyChainService.swift
Show resolved
Hide resolved
private init( | ||
currentAuthType: @autoclosure @escaping () -> (AuthType?), | ||
services: [MailServiceProvider] | ||
init( | ||
currentAuthType: AuthType, | ||
currentUser: User | ||
) { | ||
self.currentAuthType = currentAuthType | ||
self.services = services | ||
self.services = MailServiceProviderFactory.services(user: currentUser) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was a bit worried about this - originally dynamic as a closure, now user is directly provided. Need to scrutinize more. But app seems to work the same.
Am I right that we have now one global context with app lifetime? What about Realm and threading? I see that context is forced to be created on UI thread, so Realms is created on that thread. That means we must do any read query on UI thread. |
I believe so. (except that new
That can be reworked - I don't mind to create Realm on any thread. But the encryption key for realm, which uses KeyChain, must be created on main thread, to be safe. I don't exactly mind sticking to main thread for Realm either, though. (if it doesn't cause issues, not sure) |
@ivan-ushakov could you please help me with these when running tests. I'm not very familiar with this stack. |
Sure, let me check. |
I'm still hoarding some changes here. I'll go ahead and push them |
@@ -9,16 +9,9 @@ | |||
import Foundation | |||
import UIKit | |||
|
|||
autoreleasepool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure we could skip autoreleasepool
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure either - the IDE was telling me that it's no longer needed. @sosnovsky may know
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
Looks like you added some UI classes to unit test target You could check |
|
Got it! Filed #1130 |
Current issues are caused by https://github.com/FlowCrypt/flowcrypt-ios/blob/issue-1111-realm-keychain-init/FlowCryptAppTests/Functionality/Services/ComposeMessageServiceTests.swift#L31 - this code needs |
I tried to remove |
The Core parts are needed for tests in either case. And so is SwiftyRSA. |
Thanks for help! |
Also for testing we can use |
I'm not entirely sure we use Realm in Swift tests. But will file an issue. |
Yes, this |
It's initiated only in |
Currently unit tests are running, but |
Yes but one of its dependencies must have previously still needed Realm, even if Realm wasn't touched in the tests. Do you want to look into this? I'm ok to temporarily disable those tests, too. |
Currently looking into this issue |
All tests are passing now ✅ |
I'll merge this. After that, I'll review the already merged PR - there are many opportunities for small fixes. Then can settle that after. Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Below are smaller cleanup/refactors that I wanted to do in this PR
fetchedEncryptedKeys: [KeyDetails], | ||
router: GlobalRouterType = GlobalRouter(), | ||
keyStorage: KeyStorageType = KeyDataStorage(), | ||
decorator: SetupViewDecorator = SetupViewDecorator(), | ||
core: Core = Core.shared, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Controllers don't need core in init, since we're not unit testing controllers
@@ -44,23 +43,21 @@ final class SetupBackupsViewController: TableNodeViewController, PassPhraseSavea | |||
} | |||
|
|||
init( | |||
appContext: AppContext, | |||
fetchedEncryptedKeys: [KeyDetails], | |||
router: GlobalRouterType = GlobalRouter(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Router not needed here, can get from context
let core: Core | ||
let core: Core = Core.shared | ||
let router: GlobalRouterType |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
core and router not needed here in init
user: UserId, | ||
keys: [KeyDetails] = [], | ||
core: Core = .shared, | ||
router: GlobalRouterType = GlobalRouter(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
router not needed, get from ctx
user: UserId, | ||
backupService: BackupServiceType = BackupService(), | ||
core: Core = .shared, | ||
router: GlobalRouterType = GlobalRouter(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can get router from ctx instead
// todo - rename argument to folderService: | ||
user: User, | ||
folderProvider: FoldersServiceType, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename folderProvider
to folderService
here
logger.logInfo("proceed for session \(session.debugDescription)") | ||
AppStartup().initializeApp(window: keyWindow, session: session) | ||
private func proceed(with appContext: AppContext) { | ||
logger.logInfo("proceed for session: \(appContext.session?.description ?? "nil")") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
originally it used debugDescription
, not sure of the difference
// todo - what is this and what is it used for? | ||
final class KeyDataStorage { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this class, it just wraps functionality with no value add
storage: KeyStorageType = KeyDataStorage(), | ||
passPhraseService: PassPhraseServiceType = PassPhraseService(), | ||
currentUserEmail: @autoclosure @escaping () -> (String?) = DataService.shared.email | ||
storage: KeyStorageType, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please rename storage
to keyStorage
here
This PR cleans up realm init to minimize race conditions and have more control over when and how it's initialized.
close #1111
WIP
To be filled by reviewers
I have reviewed that this PR... (tick whichever items you personally focused on during this review):