iosswiftavkit

Swift code to retrieve audio file metadata is not working on some iOS devices


Consider the following Swift code to retrieve audio file metadata (title, album name, artist, artwork) on iOS. It iterates through files in a given directory and extracts metadata using AVAsset

This code works as expected on physical and virtual devices running iOS v16.3 or lower. However, on my iPhone 13 Pro Max running iOS v16.3.1, all metadata returns null. The songURI however, works just fine (and the proof to that is that I can play the songs normally using AVAudioPlayer using songuri

I have no idea what could be causing this. I cannot test this on any other physical device (I do not own any other devices) and also, my MacBook is a bit outdated so I cannot run newer iOS versions (such as 16.5) due to the limitation of Xcode and macOS Monterey which I cannot upgrade)

What could be the reason ?

do {
                // Get the directory contents urls (including subfolders urls)
                let directoryContents = try FileManager.default.contentsOfDirectory(at: url,includingPropertiesForKeys: nil)
                
                for songurl in directoryContents {
                    print(songurl.localizedName ?? songurl.lastPathComponent)
                    
                    if isValidAudioFileExtension(songurl.pathExtension) {
                        let shouldStopAccessing = songurl.startAccessingSecurityScopedResource()
                        
                        defer {
                            if shouldStopAccessing {
                                songurl.stopAccessingSecurityScopedResource()
                            }
                        }
                        
                        let asset = AVAsset(url: songurl)
                        let metaData = asset.metadata
                        
                        let title = metaData.first(where: {$0.commonKey == .commonKeyTitle})?.value as? String
                        let artist = metaData.first(where: {$0.commonKey == .commonKeyArtist})?.value as? String
                        let album = metaData.first(where: {$0.commonKey == .commonKeyAlbumName})?.value as? String
                        let playbackDuration: Double? = CMTimeGetSeconds(asset.duration)
                        var artwork: MPMediaItemArtwork? = nil
                        if let artworkdata = metaData.first(where: {$0.commonKey == .commonKeyArtwork}), let value = artworkdata.value as? Data {
                            artwork = MPMediaItemArtwork(boundsSize: CGSize(width: 60, height: 60), requestHandler: { (size) -> UIImage in
                                return UIImage(data: value)!
                            })
                        }

                        do {
                            let bookmarkData = try songurl.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil)
                            var isStale = false
                            let bookmarkedURL = try URL(resolvingBookmarkData: bookmarkData, bookmarkDataIsStale: &isStale)
                        
                            print(bookmarkedURL)
                            
                            let song = SongItem(
                                url: bookmarkedURL,
                                title: title ?? "Undefined Song",
                                artist: artist,
                                albumTitle: album,
                                playbackDuration: Int(playbackDuration ?? 0),
                                artwork: artwork
                            )
                            
                            self.tracklist.append(song)
                        } catch {
                            print("Failed to create bookmark data for URL: \(error.localizedDescription)")
                            showToast(message: "\(error.localizedDescription)")
                        }
                    }
                }
            } catch {
                print(error)
            }

NOTE: I also tried removing the security-scoped access block (```startAccessingSecurityScopedResource``) but no difference.


Solution

  • Turns out that it's a device-specific bug leading to unexpected behavior, which not only affected media metadata retrieval but also the entirety of AV framework. Code began to work as expected after upgrading device to iOS 16.5.