store ios: Exclude the database file from iOS backup#2195
store ios: Exclude the database file from iOS backup#2195gnprice merged 2 commits intozulip:mainfrom
Conversation
9012d25 to
0f3b485
Compare
0f3b485 to
ace6b25
Compare
chrisbobbe
left a comment
There was a problem hiding this comment.
Thanks! Comments below.
| 37D847A22F4CE7C20020EB99 /* RunnerUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = RunnerUITests; sourceTree = "<group>"; }; | ||
| 37D847A22F4CE7C20020EB99 /* RunnerUITests */ = { | ||
| isa = PBXFileSystemSynchronizedRootGroup; | ||
| exceptions = ( | ||
| ); | ||
| explicitFileTypes = { | ||
| }; | ||
| explicitFolders = ( | ||
| ); | ||
| path = RunnerUITests; | ||
| sourceTree = "<group>"; | ||
| }; |
There was a problem hiding this comment.
The substantive change here is just to add an empty exceptions entry, right? It looks like this section was added in 509d88f (Patrol setup); I suspect it's not related to the intended change for your commit.
Would you take a few minutes to try and reproduce this change on its own, and either discard it or make a separate commit for it depending on what you learn? (E.g. probably we want to keep this change if it happens naturally when you clear an Xcode cache and rebuild, because in that case it probably belonged in 509d88f and was just left out accidentally.)
lib/model/store.dart
Outdated
| if (defaultTargetPlatform == TargetPlatform.iOS) { | ||
| // Exclude the database file from backups on iOS. | ||
| await IosNativeHostApi().setExcludedFromBackup(file.path); | ||
| } |
There was a problem hiding this comment.
If this throws it'll block loading the app (i.e. stop before creating the LiveGlobalStore) with no feedback to the user. Maybe instead run this unawaited() with a TODO(log) on error?
Also how about putting this call immediately after the await _dbFile() line? It sets an attribute on the file; the intervening database queries (await db.getGlobalSettings() etc.) seem unrelated and not prerequisites for this filesystem action.
That proximity should also help us remember to handle this detail carefully in the migration in your other PR #2156.
There was a problem hiding this comment.
We can't put this after _dbFile(), because the database file may not exists yet. We can though put it after AppDatabase(NativeDatabase.createInBackground(file));, moved there.
There was a problem hiding this comment.
Hmm but will the file exist right after the AppDatabase(NativeDatabase.createInBackground(file)) line? There's no await there, so maybe that line creates the file synchronously…which seems unusual? But then why is the method's name createInBackground?
I wonder if, in your previous revision, await db.getGlobalSettings() and friends were actually giving time for the file to finish being created asynchronously, before the setExcludedFromBackup call? Causing that call to succeed basically by accident.
There was a problem hiding this comment.
A call like await db.getGlobalSettings() will wait for the database to be opened, which I believe will involve creating the database file if it doesn't exist. (Ultimately it calls sqlite3_open_v2 with flags SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, via [Sqlite3Implementation.open].)
I just spent a few minutes looking around Drift's API, and I don't see a totally clean way to say "wait for the database to be opened, then do X" short of actually making a query. (The closest is await db.executor.ensureOpen(db); but the type of executor is documented to be internal to Drift and unstable.)
There was a problem hiding this comment.
NativeDatabase.createInBackground(file) returns the QueryExecutor which is same as db.executor. So, AIUI it should be safe to use executor.ensureOpen via the returned QueryExecutor?
There was a problem hiding this comment.
Alternatively, there's db.doWhenOpened but it also says:
Calling this method directly might circumvent the current transaction. For that reason, it should only be called inside drift.
There was a problem hiding this comment.
Reverted to the previous revision for now, where the call is made at the end of the load method.
ios/Runner/AppDelegate.swift
Outdated
| notificationTapEventListener = NotificationTapEventListener() | ||
| NotificationTapEventsStreamHandler.register(with: controller.binaryMessenger, streamHandler: notificationTapEventListener!) | ||
|
|
||
| IosNativeHostApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: IosNativeHostApiImpl()) |
There was a problem hiding this comment.
nit: how about moving this above the // Retrieve the remote notification payload comment, so it doesn't interrupt notification-related code
pigeon/ios_native.dart
Outdated
|
|
||
| @HostApi() | ||
| abstract class IosNativeHostApi { | ||
| /// Sets the flag to exclude from iOS backups for the given filepath. |
There was a problem hiding this comment.
nit: can be more transparent about what this method does:
| /// Sets the flag to exclude from iOS backups for the given filepath. | |
| /// Sets UrlResourceValues.isExcludedFromBackup for the given file path. | |
| /// | |
| /// See doc: | |
| /// https://developer.apple.com/documentation/foundation/urlresourcevalues/isexcludedfrombackup |
ios/Runner/AppDelegate.swift
Outdated
| var resourceValues = URLResourceValues() | ||
| resourceValues.isExcludedFromBackup = true | ||
|
|
||
| var url = URL(fileURLWithPath: path) |
There was a problem hiding this comment.
| var url = URL(fileURLWithPath: path) | |
| var url = URL(fileURLWithPath: path, isDirectory: false) |
I think? In case this affects app startup time; see doc:
For earlier versions [than iOS 16], opt for the nonblocking variants of the methods in the following table.
6fe5c64 to
b91fe25
Compare
|
Thanks for the review @chrisbobbe! Pushed an update, PTAL. |
chrisbobbe
left a comment
There was a problem hiding this comment.
Thanks!! Comments below.
ios/Runner/AppDelegate.swift
Outdated
|
|
||
lib/model/store.dart
Outdated
| if (defaultTargetPlatform == TargetPlatform.iOS) { | ||
| // Exclude the database file from backups on iOS. | ||
| await IosNativeHostApi().setExcludedFromBackup(file.path); | ||
| } |
There was a problem hiding this comment.
Hmm but will the file exist right after the AppDatabase(NativeDatabase.createInBackground(file)) line? There's no await there, so maybe that line creates the file synchronously…which seems unusual? But then why is the method's name createInBackground?
I wonder if, in your previous revision, await db.getGlobalSettings() and friends were actually giving time for the file to finish being created asynchronously, before the setExcludedFromBackup call? Causing that call to succeed basically by accident.
8c8c0a9 to
e85346a
Compare
|
Thanks for the review @chrisbobbe! Pushed an update, PTAL. |
chrisbobbe
left a comment
There was a problem hiding this comment.
Thanks! Bump on something small below, and marking for Greg's review.
lib/model/store.dart
Outdated
| // Disable OS backups for the database file, see: | ||
| // https://github.com/zulip/zulip-flutter/issues/2158 | ||
| unawaited(_maybeDisableOsBackup(file)); |
There was a problem hiding this comment.
| // Disable OS backups for the database file, see: | |
| // https://github.com/zulip/zulip-flutter/issues/2158 | |
| unawaited(_maybeDisableOsBackup(file)); | |
| // Disable OS backups for the database file, see: | |
| // https://github.com/zulip/zulip-flutter/issues/2158 | |
| unawaited(_maybeDisableOsBackup(file)); // TODO(log) on error |
Just running `flutter build ios --config-only` wasn't sufficient, there were no changes, I had to trigger Xcode's edits to the `project.pbxproj` first, by changing the order of build phases (moved "Compile Sources" phase above "Run Script") and then again reverted the order of those build phases. The Xcode's edits ended up being changing the `objectVersion = 54;` to `objectVersion = 70;`, and addition of couple of empty `inputPaths = ();` and `outputPaths = ();`. After Xcode's edits to `project.pbxproj`, I ran `flutter build ios --config-only` which resulted in this commit. This tells that this was probably missed in merge/rebase conflict in 509d88f (Patrol setup). Which changed the `objectVersion` entry to `objectVersion = 54;` but missed the change to `37D847A22F4CE7C20020EB99 /* RunnerUITests */ = {` block. So, on the next `flutter run/build`, it probably doesn't bother to verify or make changes to the `project.pbxproj` because `objectVersion` is already set to a value that `flutter run/build` likes.
|
Thanks @rajveermalviya for taking care of this, and @chrisbobbe for the detailed reviews! Looks good; merging, with a few added comments including the one Chris suggested above: diff --git lib/model/store.dart lib/model/store.dart
index 7c8fe3ecd..1084a2f01 100644
--- lib/model/store.dart
+++ lib/model/store.dart
@@ -1120,7 +1120,9 @@ class LiveGlobalStore extends GlobalStore {
// Disable OS backups for the database file, see:
// https://github.com/zulip/zulip-flutter/issues/2158
- unawaited(_maybeDisableOsBackup(file));
+ // This comes after the queries above, because it must come after
+ // the database file has been created on disk.
+ unawaited(_maybeDisableOsBackup(file)); // TODO(log) on error
return LiveGlobalStore._(
backend: LiveGlobalStoreBackend._(db: db),
diff --git pigeon/ios_native.dart pigeon/ios_native.dart
index f33261120..40bbebe8d 100644
--- pigeon/ios_native.dart
+++ pigeon/ios_native.dart
@@ -12,6 +12,9 @@ import 'package:pigeon/pigeon.dart';
abstract class IosNativeHostApi {
/// Sets UrlResourceValues.isExcludedFromBackup for the given file path.
///
+ /// The file at this path must already exist,
+ /// and be a regular file (not a directory).
+ ///
/// See doc:
/// https://developer.apple.com/documentation/foundation/urlresourcevalues/isexcludedfrombackup
/// https://developer.apple.com/documentation/foundation/optimizing-your-app-s-data-for-icloud-backupThose two points seemed like things that would otherwise be easy to miss when making future edits to this code. |
These changes belonged in 1d03e57. Before merging that PR zulip#2195, I edited this comment in the source file of the pigeon but didn't rerun generating the files (nor CI), oops.
These changes belonged in 19d67b4. Before merging that PR zulip#2195, I edited this comment in the source file of the pigeon but didn't rerun generating the files (nor CI), oops.
Fixes #2158.
Tested by generating iOS backups from Finder app on macOS, and then using iTunes-Backup-Explorer to read the backup files: