Skip to content

Exclude all files and directories from iCloud backup #1361

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

Merged
merged 5 commits into from
Feb 10, 2022

Conversation

Anderas
Copy link
Contributor

@Anderas Anderas commented Feb 8, 2022

Fixes element-hq/element-ios#5498

As listed in documentation, the iOS is automatically backing up a number of folders to iCloud / iTunes, including <AppHome>/Documents/ and <AppHome>/Library/Application Support/. Additionally the Matrix SDK stores files in AppGroup containers (potentially shared between app and an extension), which are also automatically backed up to iCloud. These folders include data for rooms, spaces etc.

To opt-out of backups completely, each url needs to be explicitely marked with isExcludedFromBackupKey (this is applied recursively). I converted all createDirectory calls with a custom method, which automatically does this when creating a new directory.

This does not yet address smaller files (e.g. User Defaults preferences only created on save) or any future folders, which might be created without the backup exclusion. To solve for this second problem I added two functions excludeAllUserDirectoriesFromBackup and excludeAllAppGroupDirectoriesFromBackup, which need to be called by the main application (e.g. in AppDelegate) and exclude all top-level directories.

Arguably this second approach might make it redundant to explicitly specify backup choice when creating a new folder, however the blanker exclusion is only called at specific moments (e.g. app start), which may be before a particular folder is created. This would expose the folder to backups until the next app launch.

Signed-off-by: Andy Uhnak <andyuhnak@gmail.com>
Note: some directories are excluded automatically if they are nested within `<AppHome>/Library/Caches/` or `<AppHome>/tmp/`
see [details](https://developer.apple.com/documentation/foundation/optimizing_app_data_for_icloud_backup).
*/
@objc func createDirectoryExcludedFromBackup(at url: URL) throws {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I considered an alternative api along the lines of createDirectory(at:withIntermediate:attributes:isExcluded:) but we just pass the same parameters everywhere anyway, so it felt cumbersome and pointless.

Copy link
Member

Choose a reason for hiding this comment

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

This makes sense to me.
I would change it's signature slightly though, to better match its path counterpart: createDirectoryExcludedFromBackup(atURL url: URL)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This api distinction already exists within FileManager's API ((at url: URL) vs (atPath path: String)). Reading swift conventions I assume that (at url: URL) is preferable, but then (at path: String) could not be easily distinguished / would be overloading. Since URL is preferable type over String path, the url-api is given preferential treatment. I don't mind aligning them with your suggestion, just wanted to point out that this was a deliberate choice given the existing FileManager api.

Copy link
Member

Choose a reason for hiding this comment

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

If it were my choice I would make them symmetrical but no strong feelings really.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea after I changed the excludeItemFromBackup method, it seems preferable to stick with (at url: URL) everywhere for consistency sake.

Copy link
Member

@stefanceriu stefanceriu left a comment

Choose a reason for hiding this comment

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

This all looks very reasonable to me, the only thing I'm worried about is this line from the documentation: Set the key each time you save the file because certain common file operations clear the key.. I don't think we have control over all the saves.

We have quite a lot of folder within the application group container. Do we really need all of those shared with the other processes? 🤔

Also, have you checked how the integration tests behave with these changes? (MXBackgroundSyncServiceTests, MXSessionTests, MXCoreDataRoomListDataManagerUnitTests etc.)

Note: some directories are excluded automatically if they are nested within `<AppHome>/Library/Caches/` or `<AppHome>/tmp/`
see [details](https://developer.apple.com/documentation/foundation/optimizing_app_data_for_icloud_backup).
*/
@objc func excludeFromBackup(url: URL) throws {
Copy link
Member

Choose a reason for hiding this comment

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

I would rename this to something like excludeURLFromBackup(_ url: URL) to be more in line with the others.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking at FileManager docs, I think an even better name would be excludeItemFromBackup(at url: URL) which aligns with copyItem(at url: URL) or moveItem(at url: URL). WIll make that change 👍

Note: some directories are excluded automatically if they are nested within `<AppHome>/Library/Caches/` or `<AppHome>/tmp/`
see [details](https://developer.apple.com/documentation/foundation/optimizing_app_data_for_icloud_backup).
*/
@objc func createDirectoryExcludedFromBackup(at url: URL) throws {
Copy link
Member

Choose a reason for hiding this comment

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

This makes sense to me.
I would change it's signature slightly though, to better match its path counterpart: createDirectoryExcludedFromBackup(atURL url: URL)

@Anderas
Copy link
Contributor Author

Anderas commented Feb 9, 2022

This all looks very reasonable to me, the only thing I'm worried about is this line from the documentation: Set the key each time you save the file because certain common file operations clear the key.. I don't think we have control over all the saves.

Yea this is true, though the way I understand it, it would mostly be an issue if we were setting the attribute on specific files/folders that are often updated and saved. Instead we are either marking top-level folders that we do not directly create (Documents, Library), or use the createDirectoryExcludedFromBackup to always pair mutation/creation with setting the attribute. And if that fails, we'll be calling the blanket exclusion from app delegate.

We have quite a lot of folder within the application group container. Do we really need all of those shared with the other processes? 🤔

Very good question. I don't fully understand the integration with extensions yet, and would probably be a larger change, but worth taking that as the next logical step.

Also, have you checked how the integration tests behave with these changes? (MXBackgroundSyncServiceTests, MXSessionTests, MXCoreDataRoomListDataManagerUnitTests etc.)

I have some issues running tests in general, even on develop, so need to debug that first.

@Anderas
Copy link
Contributor Author

Anderas commented Feb 10, 2022

Also, have you checked how the integration tests behave with these changes? (MXBackgroundSyncServiceTests, MXSessionTests, MXCoreDataRoomListDataManagerUnitTests etc.)

I have some issues running tests in general, even on develop, so need to debug that first.

So the integration tests (particularly MXSessionTests) seem quite flaky on develop, so in a future PR I can try to fix some of them, but I observed no difference in the failures between this PR and develop, so supposedly no regressions.

@Anderas Anderas requested a review from stefanceriu February 10, 2022 09:57
Copy link
Contributor

@SBiOSoftWhare SBiOSoftWhare left a comment

Choose a reason for hiding this comment

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

Good job, this is well documented. Btw you can probably use MXLog.error instead of MXLog.debug for try/catch errors.

Copy link
Member

@stefanceriu stefanceriu left a comment

Choose a reason for hiding this comment

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

Looks good, let's roll with it 👍

@Anderas
Copy link
Contributor Author

Anderas commented Feb 10, 2022

Good job, this is well documented. Btw you can probably use MXLog.error instead of MXLog.debug for try/catch errors.

Ah yes, I thought I had. Thanks.

@Anderas Anderas merged commit 30602a2 into develop Feb 10, 2022
@Anderas Anderas deleted the andy/5498_icloud_backup branch February 10, 2022 17:53
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.

What is being backed up to iCloud?
3 participants