From d3fc1ba3503f2807d6e8bbf6e77d82723a86ce49 Mon Sep 17 00:00:00 2001 From: Brad Howes Date: Sat, 26 Oct 2024 01:30:17 +0200 Subject: [PATCH] Fix detection of iCloud files not being found --- .../SoundFontsApp.xcodeproj/project.pbxproj | 4 + .../project.pbxproj | 4 + .../SoundFontsFramework/Audio/Bookmark.swift | 25 +++++-- .../Fonts/FontStateQuery.swift | 73 +++++++++++++++++++ .../Fonts/FontsTableViewController.swift | 7 +- .../Fonts, Presets, Tags/TableCell.swift | 25 +++++-- .../UI/Views/GuideView.storyboard | 26 +++---- 7 files changed, 135 insertions(+), 29 deletions(-) create mode 100644 SoundFontsFramework/SoundFontsFramework/Fonts, Presets, Tags/Fonts/FontStateQuery.swift diff --git a/SoundFontsApp/SoundFontsApp.xcodeproj/project.pbxproj b/SoundFontsApp/SoundFontsApp.xcodeproj/project.pbxproj index d7a3c7e5..96fa38a0 100644 --- a/SoundFontsApp/SoundFontsApp.xcodeproj/project.pbxproj +++ b/SoundFontsApp/SoundFontsApp.xcodeproj/project.pbxproj @@ -1092,6 +1092,8 @@ DEVELOPMENT_TEAM = UP6SS5ES7E; GCC_SYMBOLS_PRIVATE_EXTERN = NO; INFOPLIST_FILE = SoundFontsApp/App/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = SoundFonts; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -1122,6 +1124,8 @@ DEVELOPMENT_TEAM = UP6SS5ES7E; GCC_SYMBOLS_PRIVATE_EXTERN = YES; INFOPLIST_FILE = SoundFontsApp/App/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = SoundFonts; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/SoundFontsFramework/SoundFontsFramework.xcodeproj/project.pbxproj b/SoundFontsFramework/SoundFontsFramework.xcodeproj/project.pbxproj index c75e5471..a9480920 100644 --- a/SoundFontsFramework/SoundFontsFramework.xcodeproj/project.pbxproj +++ b/SoundFontsFramework/SoundFontsFramework.xcodeproj/project.pbxproj @@ -98,6 +98,7 @@ BD1C22E925CDC6AE007C114F /* TutorialContentPager.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BD1C22E825CDC6AE007C114F /* TutorialContentPager.storyboard */; }; BD1C22ED25CDC701007C114F /* TutorialContentPagerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD1C22EC25CDC701007C114F /* TutorialContentPagerViewController.swift */; }; BD1C22F125CDC781007C114F /* TutorialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD1C22F025CDC781007C114F /* TutorialViewController.swift */; }; + BD1DE29F2CCC56A6004BA803 /* FontStateQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD1DE29E2CCC5691004BA803 /* FontStateQuery.swift */; }; BD1F11B823FE61E300AE9F3D /* GuideViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD1F11B723FE61E300AE9F3D /* GuideViewController.swift */; }; BD1F11BC23FE690300AE9F3D /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD1F11BB23FE690300AE9F3D /* UIViewController.swift */; }; BD239486255213CB004E5E0D /* KeyParamSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD239485255213CB004E5E0D /* KeyParamSequence.swift */; }; @@ -364,6 +365,7 @@ BD1C22E825CDC6AE007C114F /* TutorialContentPager.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = TutorialContentPager.storyboard; sourceTree = ""; }; BD1C22EC25CDC701007C114F /* TutorialContentPagerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TutorialContentPagerViewController.swift; sourceTree = ""; }; BD1C22F025CDC781007C114F /* TutorialViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TutorialViewController.swift; sourceTree = ""; }; + BD1DE29E2CCC5691004BA803 /* FontStateQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontStateQuery.swift; sourceTree = ""; }; BD1F11B723FE61E300AE9F3D /* GuideViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuideViewController.swift; sourceTree = ""; }; BD1F11BB23FE690300AE9F3D /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; BD239485255213CB004E5E0D /* KeyParamSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyParamSequence.swift; sourceTree = ""; }; @@ -830,6 +832,7 @@ BD5B5C3828C14C1E000BC5A4 /* FontsEditorTableViewController.swift */, BD28D73827958CE4000A57DE /* FontsTableViewController.swift */, BD074C2923CDAF51001987E9 /* SelectedSoundFontManager.swift */, + BD1DE29E2CCC5691004BA803 /* FontStateQuery.swift */, ); path = Fonts; sourceTree = ""; @@ -1378,6 +1381,7 @@ BD074C7B23CDAF51001987E9 /* Foundation.swift in Sources */, BD1C22F125CDC781007C114F /* TutorialViewController.swift in Sources */, BD62CF3E253DB4CE00DBDC1B /* PresetChangeManager.swift in Sources */, + BD1DE29F2CCC56A6004BA803 /* FontStateQuery.swift in Sources */, BDEB12642620939B003CE8EE /* ChangesPageViewController.swift in Sources */, BDFD3E3029E3FA5B0010E3CB /* MIDIActionsTableCell.swift in Sources */, BD074C8923CDAF51001987E9 /* UpperViewSwipingActivity.swift in Sources */, diff --git a/SoundFontsFramework/SoundFontsFramework/Audio/Bookmark.swift b/SoundFontsFramework/SoundFontsFramework/Audio/Bookmark.swift index 1e5eb990..0ab37642 100644 --- a/SoundFontsFramework/SoundFontsFramework/Audio/Bookmark.swift +++ b/SoundFontsFramework/SoundFontsFramework/Audio/Bookmark.swift @@ -93,10 +93,11 @@ extension Bookmark { /// Determine the availability state for a bookmarked URL public var isAvailable: Bool { - let secured = url.startAccessingSecurityScopedResource() - let value = try? url.checkResourceIsReachable() - if secured { url.stopAccessingSecurityScopedResource() } - return value ?? false + if url.startAccessingSecurityScopedResource() { + defer { url.stopAccessingSecurityScopedResource() } + return (try? url.checkResourceIsReachable()) ?? false + } + return false } /// Determine if the file is located in an iCloud container @@ -104,6 +105,8 @@ extension Bookmark { /// The various iCloud states a bookmark item may be in. public enum CloudState { + /// Item is local and not synced to iCloud + case local /// Item is on iCloud but not available locally. case inCloud /// Item is queue to be downloaded to the device @@ -120,8 +123,13 @@ extension Bookmark { /// Obtain the current iCloud state of the bookmark item public var cloudState: CloudState { + let secured = url.startAccessingSecurityScopedResource() + defer { if secured { url.stopAccessingSecurityScopedResource() } } + guard let values = try? url.resourceValues(forKeys: [ + .isUbiquitousItemKey, + .ubiquitousItemDownloadRequestedKey, .ubiquitousItemDownloadingStatusKey, .ubiquitousItemIsDownloadingKey, .ubiquitousItemDownloadingErrorKey @@ -129,13 +137,18 @@ extension Bookmark { else { return .unknown } + + let isUbiquitous = (values.isUbiquitousItem ?? false) + if !isUbiquitous { return .local } + guard values.ubiquitousItemDownloadingError == nil else { return .downloadError } - guard let status = values.ubiquitousItemDownloadingStatus else { return .unknown } + + guard let status = values.ubiquitousItemDownloadingStatus else { return .inCloud } switch status { case .current: return .downloaded case .downloaded: return .downloading case .notDownloaded: return .inCloud - default: return .unknown + default: return .inCloud } } } diff --git a/SoundFontsFramework/SoundFontsFramework/Fonts, Presets, Tags/Fonts/FontStateQuery.swift b/SoundFontsFramework/SoundFontsFramework/Fonts, Presets, Tags/Fonts/FontStateQuery.swift new file mode 100644 index 00000000..0e6625da --- /dev/null +++ b/SoundFontsFramework/SoundFontsFramework/Fonts, Presets, Tags/Fonts/FontStateQuery.swift @@ -0,0 +1,73 @@ +class FontStateQuery { + let query = NSMetadataQuery() + let queue: OperationQueue + + init(queue: OperationQueue = .main) { + self.queue = queue + } + + func searchMetadataItems(paths: [URL]) -> AsyncStream<[MetadataItemWrapper]> { + query.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope] + query.sortDescriptors = [] + query.predicate = NSPredicate(value: true) + query.searchItems = paths + return AsyncStream { continuation in + NotificationCenter.default.addObserver( + forName: .NSMetadataQueryDidFinishGathering, + object: query, + queue: queue + ) { _ in + let result = self.query.results.compactMap { item -> MetadataItemWrapper? in + guard let metadataItem = item as? NSMetadataItem else { + return nil + } + return MetadataItemWrapper(metadataItem: metadataItem) + } + continuation.yield(result) + } + + NotificationCenter.default.addObserver( + forName: .NSMetadataQueryDidUpdate, + object: query, + queue: queue + ) { _ in + let result = self.query.results.compactMap { item -> MetadataItemWrapper? in + guard let metadataItem = item as? NSMetadataItem else { + return nil + } + return MetadataItemWrapper(metadataItem: metadataItem) + } + continuation.yield(result) + } + + query.start() + + continuation.onTermination = { @Sendable _ in + self.query.stop() + NotificationCenter.default.removeObserver(self, name: .NSMetadataQueryDidFinishGathering, object: self.query) + NotificationCenter.default.removeObserver(self, name: .NSMetadataQueryDidUpdate, object: self.query) + } + } + } +} + +struct MetadataItemWrapper: Sendable { + let path: String? + let isPlaceholder: Bool + let isDownloading: Bool + let isUploaded: Bool + + init(metadataItem: NSMetadataItem) { + path = metadataItem.value(forAttribute: NSMetadataItemPathKey) as? String + isPlaceholder = metadataItem.value(forAttribute: NSMetadataUbiquitousItemDownloadingStatusKey) as? Bool ?? false + + let downloadStatus = metadataItem.value(forAttribute: NSMetadataUbiquitousItemIsDownloadingKey) as? String + isDownloading = downloadStatus == NSMetadataUbiquitousItemDownloadingStatusCurrent + + // Check whether the file has been uploaded successfully or saved in the cloud + let uploaded = metadataItem.value(forAttribute: NSMetadataUbiquitousItemIsUploadedKey) as? Bool ?? false + let uploading = metadataItem.value(forAttribute: NSMetadataUbiquitousItemIsUploadingKey) as? Bool ?? true + + isUploaded = uploaded && !uploading + } +} diff --git a/SoundFontsFramework/SoundFontsFramework/Fonts, Presets, Tags/Fonts/FontsTableViewController.swift b/SoundFontsFramework/SoundFontsFramework/Fonts, Presets, Tags/Fonts/FontsTableViewController.swift index 60f91833..a96fd45b 100644 --- a/SoundFontsFramework/SoundFontsFramework/Fonts, Presets, Tags/Fonts/FontsTableViewController.swift +++ b/SoundFontsFramework/SoundFontsFramework/Fonts, Presets, Tags/Fonts/FontsTableViewController.swift @@ -169,8 +169,9 @@ private extension FontsTableViewController { for index in 0.. - + - + @@ -29,7 +29,7 @@ - + @@ -74,7 +74,7 @@ - + @@ -105,7 +105,7 @@ - + @@ -330,8 +330,8 @@ all notes (Panic mode) - + @@ -407,7 +407,7 @@ all notes (Panic mode) - +