Chapter Marks - Adding AVTimedMetadataGroup to Audio (M4A)

Could you provide guidance on how to add chapter marks to an M4A. I've been attempting bookmark. From what I've read, it requires the use of

AVMetadataKey.quickTimeUserDataKeyChapter

track.addTrackAssociation(to: ... type: .chapterList)

or both.

I've looked into AVTimedMetadataGroup but I havent found a way to get it added based on the documentation. I also havent found anyone who has used native Swift to add chapter marks. They've always given in and used ffmpeg or some other external solution.

inputURL is for the file that is being read in

outputURL is for the the final file

chapters is an array of dictionaries, where time is the start of each chapter and its name in the list

The target is macOS

import AVFoundation

class AudioChapterCreator {
    // Function to create an audio file with chapters and a chapter list
    func createAudioFileWithChapters(inputURL: URL, outputURL: URL, chapters: [(time: CMTime, title: String)]) {
        
        let options = [AVURLAssetPreferPreciseDurationAndTimingKey: true]
        let asset = AVURLAsset(url: inputURL, options: options)
        let durationInSeconds = CMTimeGetSeconds(asset.duration)
        print("asset durationInSeconds: \(durationInSeconds)")
        
        guard let audioTrack = asset.tracks(withMediaType: .audio).first else {
            print("Error: Unable to find audio track in the asset.")
            return
        }
        
   
        // Create metadata items for chapters
        let chapterMetadataItems = chapters.map { chapter -> AVMetadataItem in
            let item = AVMutableMetadataItem()
            // this duration is just for testing
            let tempDur = CMTime(seconds: 100, preferredTimescale: 1)
            item.keySpace = AVMetadataKeySpace.quickTimeUserData
            item.key = AVMetadataKey.quickTimeUserDataKeyChapter as NSString
            item.value = chapter.title as NSString
            item.time = chapter.time
            item.duration = tempDur
            return item
        }
        
       // Create an AVAssetExportSession for writing the output file
        guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
            print("Error: Unable to create AVAssetExportSession.")
            return
        }
        
        // Configure the AVAssetExportSession
        exportSession.outputFileType = .m4a
        exportSession.outputURL = outputURL
        exportSession.metadata  = asset.metadata + chapterMetadataItems
        exportSession.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: asset.duration);

        // Export the audio file
        exportSession.exportAsynchronously {
            switch exportSession.status {
            case .completed:
                print("Audio file with chapters and chapter list created successfully.")
            case .failed:
                print("Error: Failed to create the audio file.")
            case .cancelled:
                print("Export cancelled.")
            default:
                print("Export failed with unknown status.")
            }
        }
    }
}