Here is code across different files for an ios app like veed.io which adds an animated waveform based on audio in a video and runs on ipad m4 with iOS 17.5. It then allows me to export the updated video which has original video with new animated waveform element as a layover. It is not working. help me fix it & give updated code with instructions. Do not use deprecated code.

// ContentView.swift
import SwiftUI
import AVFoundation
import UIKit

struct ContentView: View {
    @State private var showingImagePicker = false
    @State private var videoURL: URL?
    @State private var processedVideoURL: URL?
    @State private var waveformImage: UIImage?
    @State private var isProcessing = false
    @State private var errorMessage: String?
    @State private var showingShareSheet = false
    @State private var shareItems: [Any] = []
    @StateObject private var playerViewModel = PlayerViewModel(videoURL: nil) // Initialize with nil

    var body: some View {
        NavigationView {
            ScrollView {
                VStack(alignment: .center, spacing: 20) {
                    // Header
                    Text("WaveViz")
                        .font(.largeTitle)
                        .fontWeight(.bold)
                        .padding(.top, 20)
                    
                    // Upload Button
                    Button(action: {
                        showingImagePicker = true
                    }) {
                        HStack {
                            Image(systemName: "plus.circle")
                                .font(.title)
                            Text("Upload Video")
                                .font(.title2)
                        }
                        .padding()
                        .foregroundColor(.white)
                        .background(Color.blue)
                        .cornerRadius(10)
                    }
                    .padding()
                    .sheet(isPresented: $showingImagePicker, onDismiss: {
                        Task {
                            await processVideo()
                        }
                    }) {
                        VideoPicker(videoURL: $videoURL, isPresented: $showingImagePicker)
                    }
                    
                    // Instructions
                    Text("Upload a video up to 2 hours long. The app will generate a waveform visualization overlay at the bottom of the video.")
                        .font(.subheadline)
                        .multilineTextAlignment(.center)
                        .padding([.leading, .trailing])
                    
                    // Waveform Visualization
                    if !isProcessing && samplesAvailable {
                        AnimatedWaveformView(playerViewModel: playerViewModel, samples: playerViewModel.samples)
                            .frame(height: 100)
                            .cornerRadius(10)
                    } else if isProcessing {
                        ProgressView("Processing...")
                            .padding()
                    }
                    
                    // Playback Controls
                    if let processedURL = processedVideoURL, !isProcessing {
                        PlaybackControls(playerViewModel: playerViewModel)
                    }
                    
                    // Download and Share Buttons
                    if let processedURL = processedVideoURL, !isProcessing {
                        HStack(spacing: 20) {
                            Button(action: {
                                saveVideoToPhotos(url: processedURL)
                            }) {
                                VStack {
                                    Image(systemName: "arrow.down.to.line.alt")
                                        .font(.title)
                                    Text("Download Video")
                                        .font(.caption)
                                }
                                .padding()
                                .foregroundColor(.white)
                                .background(Color.green)
                                .cornerRadius(10)
                            }
                            
                            Button(action: {
                                if let waveform = waveformImage {
                                    saveImageToPhotos(image: waveform)
                                }
                            }) {
                                VStack {
                                    Image(systemName: "arrow.down.to.line.alt")
                                        .font(.title)
                                    Text("Download Waveform")
                                        .font(.caption)
                                }
                                .padding()
                                .foregroundColor(.white)
                                .background(Color.orange)
                                .cornerRadius(10)
                            }
                            
                            Button(action: {
                                if let processedURL = processedVideoURL {
                                    shareItems = [processedURL]
                                    showingShareSheet = true
                                }
                            }) {
                                VStack {
                                    Image(systemName: "square.and.arrow.up")
                                        .font(.title)
                                    Text("Share Video")
                                        .font(.caption)
                                }
                                .padding()
                                .foregroundColor(.white)
                                .background(Color.purple)
                                .cornerRadius(10)
                            }
                        }
                        .padding()
                        .sheet(isPresented: $showingShareSheet) {
                            ShareSheet(items: shareItems) { success in
                                // Handle completion if needed
                            }
                        }
                    }
                    
                    // Error Message
                    if let error = errorMessage {
                        Text(error)
                            .foregroundColor(.red)
                            .padding()
                    }
                    
                    Spacer()
                }
                .padding()
            }
            .navigationBarHidden(true)
        }
    }
    
    var samplesAvailable: Bool {
        return !playerViewModel.samples.isEmpty
    }
    
    // Function to process video after selection
    func processVideo() async {
        guard let videoURL = videoURL else {
            errorMessage = "No video selected."
            return
        }
        
        isProcessing = true
        errorMessage = nil
        waveformImage = nil
        processedVideoURL = nil
        
        do {
            let samples = try await extractAudioSamples(from: videoURL)
            let downsampledSamples = downsample(samples: samples, factor: 100) // Adjust factor as needed
            
            // Ensure downsampling was successful
            guard !downsampledSamples.isEmpty else {
                throw ProcessingError.waveformGenerationFailed
            }
            
            // Update samples in ViewModel on the main thread
            DispatchQueue.main.async {
                playerViewModel.samples = downsampledSamples
            }
            
            let waveform = try await generateWaveformAsync(from: downsampledSamples, size: CGSize(width: 800, height: 200))
            let processedURL = try await overlayWaveformAsync(on: videoURL, with: waveform)
            
            // Update the player with the processed video
            DispatchQueue.main.async {
                self.isProcessing = false
                self.processedVideoURL = processedURL
                self.waveformImage = waveform
                self.playerViewModel.updateVideoURL(processedURL) // Load processed video
            }
        } catch {
            DispatchQueue.main.async {
                self.isProcessing = false
                self.errorMessage = error.localizedDescription
            }
        }
    }
    
    func downsample(samples: [Float], factor: Int) -> [Float] {
        guard factor > 0 else { return samples }
        var downsampled = Float // Initialize as an array of Float
        var index = 0
        while index < samples.count {
            let end = min(index + factor, samples.count)
            let slice = samples[index..<end]
            let average = slice.reduce(0, +) / Float(slice.count)
            downsampled.append(average)
            index += factor
        }
        return downsampled
    }
}

// AnimatedWaveformView.swift
import SwiftUI
import AVFoundation

struct AnimatedWaveformView: UIViewRepresentable {
    @ObservedObject var playerViewModel: PlayerViewModel
    var samples: [Float]
    var waveformHeight: CGFloat = 100
    var color: UIColor = .cyan

    func makeUIView(context: Context) -> WaveformUIView {
        let view = WaveformUIView()
        view.backgroundColor = .clear
        view.waveformColor = color
        view.waveformHeight = waveformHeight
        view.samples = samples
        view.player = playerViewModel.player
        return view
    }

    func updateUIView(_ uiView: WaveformUIView, context: Context) {
        uiView.samples = samples
        uiView.player = playerViewModel.player
        uiView.setNeedsDisplay()
    }
}

class WaveformUIView: UIView {
    var samples: [Float] = []
    var waveformColor: UIColor = .cyan
    var waveformHeight: CGFloat = 100
    var player: AVPlayer? {
        didSet {
            setupPlayerObserver()
        }
    }
    
    private var timeObserver: Any?
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        guard samples.count > 0 else { return }
        
        let context = UIGraphicsGetCurrentContext()
        context?.setStrokeColor(waveformColor.cgColor)
        context?.setLineWidth(1.0)
        
        let midY = bounds.height / 2
        let path = UIBezierPath()
        
        let step = max(1, samples.count / Int(bounds.width))
        
        for x in 0..<Int(bounds.width) {
            let sampleIndex = x * step
            if sampleIndex < samples.count {
                let sample = samples[sampleIndex]
                let y = CGFloat(sample) * midY
                path.move(to: CGPoint(x: CGFloat(x), y: midY - y))
                path.addLine(to: CGPoint(x: CGFloat(x), y: midY + y))
            }
        }
        
        context?.addPath(path.cgPath)
        context?.strokePath()
    }
    
    private func setupPlayerObserver() {
        guard let player = player else { return }
        // Remove existing observer if any
        if let observer = timeObserver {
            player.removeTimeObserver(observer)
            timeObserver = nil
        }
        
        // Add periodic time observer
        timeObserver = player.addPeriodicTimeObserver(forInterval: CMTime(seconds: 0.05, preferredTimescale: 600), queue: .main) { [weak self] time in
            self?.setNeedsDisplay()
        }
    }
    
    deinit {
        if let observer = timeObserver {
            player?.removeTimeObserver(observer)
        }
    }
}

// PlayerViewModel.swift
import Foundation
import AVFoundation
import Combine

class PlayerViewModel: ObservableObject {
    @Published var player: AVPlayer
    @Published var samples: [Float] = []
    
    init(videoURL: URL?) {
        if let url = videoURL {
            self.player = AVPlayer(url: url)
        } else {
            self.player = AVPlayer()
        }
    }
    
    // Function to update the video URL
    func updateVideoURL(_ url: URL) {
        self.player = AVPlayer(url: url)
        self.samples = [] // Reset samples if necessary
    }
}

//VideoPicker.swift
import SwiftUI
import UIKit
import AVFoundation

struct VideoPicker: UIViewControllerRepresentable {
    @Binding var videoURL: URL?
    @Binding var isPresented: Bool
    
    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        let parent: VideoPicker
        
        init(parent: VideoPicker) {
            self.parent = parent
        }
        
        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            parent.isPresented = false
        }
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            if let mediaURL = info[.mediaURL] as? URL {
                parent.videoURL = mediaURL
            }
            parent.isPresented = false
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }
    
    func makeUIViewController(context: Context) -> some UIViewController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        picker.mediaTypes = ["public.movie"] // Restrict to movie files
        picker.allowsEditing = false
        return picker
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
}

// PlaybackControls.swift
import SwiftUI
import AVKit

struct PlaybackControls: View {
    @ObservedObject var playerViewModel: PlayerViewModel
    @State private var isPlaying = false
    @State private var currentTime: Double = 0
    @State private var duration: Double = 0
    
    var body: some View {
        VStack {
            VideoPlayer(player: playerViewModel.player)
                .frame(height: 300)
                .cornerRadius(10)
                .onAppear {
                    loadDuration()
                    
                    // Update current time periodically
                    playerViewModel.player.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: 600), queue: .main) { time in
                        self.currentTime = CMTimeGetSeconds(time)
                    }
                }
            
            HStack {
                Button(action: {
                    if isPlaying {
                        playerViewModel.player.pause()
                    } else {
                        playerViewModel.player.play()
                    }
                    isPlaying.toggle()
                }) {
                    Image(systemName: isPlaying ? "pause.circle.fill" : "play.circle.fill")
                        .resizable()
                        .frame(width: 50, height: 50)
                        .foregroundColor(.blue)
                }
                
                Slider(value: Binding(
                    get: { currentTime },
                    set: { newValue in
                        currentTime = newValue
                        let time = CMTime(seconds: newValue, preferredTimescale: 600)
                        playerViewModel.player.seek(to: time)
                    }
                ), in: 0...duration)
            }
            .padding()
        }
    }
    
    private func loadDuration() {
        if let durationCM = playerViewModel.player.currentItem?.asset.duration {
            self.duration = CMTimeGetSeconds(durationCM)
        } else {
            self.duration = 0
        }
    }
}

//WaveformView.swift
import SwiftUI

struct WaveformView: View {
    var waveformImage: UIImage

    var body: some View {
        GeometryReader { geometry in
            Image(uiImage: waveformImage)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width: geometry.size.width * 0.9, height: geometry.size.height * 0.3)
                .padding()
        }
        .frame(height: 200) // Adjust height as needed
    }
}

// ShareSheet.swift
import SwiftUI

struct ShareSheet: UIViewControllerRepresentable {
    var items: [Any]
    var completion: ((Bool) -> Void)? = nil

    func makeUIViewController(context: Context) -> UIActivityViewController {
        let controller = UIActivityViewController(activityItems: items, applicationActivities: nil)
        controller.completionWithItemsHandler = { activityType, completed, returnedItems, error in
            completion?(completed)
        }

        return controller
    }

    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}

// Utilities.swift
import SwiftUI
import AVFoundation
import UIKit

enum ProcessingError: LocalizedError {
    case audioTrackNotFound
    case waveformGenerationFailed
    case videoProcessingFailed
    case exportFailed
    
    var errorDescription: String? {
        switch self {
        case .audioTrackNotFound:
            return "Audio track not found in the selected video."
        case .waveformGenerationFailed:
            return "Failed to generate waveform."
        case .videoProcessingFailed:
            return "Failed to process the video with waveform overlay."
        case .exportFailed:
            return "Video export failed."
        }
    }
}

// Function to extract audio samples from video
func extractAudioSamples(from url: URL) async throws -> [Float] {
    let asset = AVAsset(url: url)
    guard let track = asset.tracks(withMediaType: .audio).first else {
        throw ProcessingError.audioTrackNotFound
    }
    
    let readerSettings: [String: Any] = [
        AVFormatIDKey: kAudioFormatLinearPCM,
        AVLinearPCMIsFloatKey: true,
        AVLinearPCMBitDepthKey: 32,
        AVLinearPCMIsNonInterleaved: false
    ]
    
    let reader = try AVAssetReader(asset: asset)
    let output = AVAssetReaderTrackOutput(track: track, outputSettings: readerSettings)
    reader.add(output)
    reader.startReading()
    
    var samples = Float // Correctly declare as an array
    
    while let buffer = output.copyNextSampleBuffer(),
          let blockBuffer = CMSampleBufferGetDataBuffer(buffer) {
        autoreleasepool {
            let length = CMBlockBufferGetDataLength(blockBuffer)
            var data = Data(count: length)
            data.withUnsafeMutableBytes { (bytes: UnsafeMutableRawBufferPointer) in
                CMBlockBufferCopyDataBytes(blockBuffer, atOffset: 0, dataLength: length, destination: bytes.baseAddress!)
            }
            
            let floatCount = length / 4
            for i in 0..<floatCount {
                let start = i * 4
                if start + 4 <= data.count {
                    let subData = data.subdata(in: start..<start+4)
                    let sample: Float = subData.withUnsafeBytes { $0.load(as: Float.self) }
                    samples.append(sample) // Now valid as samples is an array
                }
            }
        }
    }
    
    return samples
}

// Function to generate waveform image from audio samples
func generateWaveform(from samples: [Float], size: CGSize) -> UIImage? {
    let renderer = UIGraphicsImageRenderer(size: size)
    let img = renderer.image { ctx in
        let context = ctx.cgContext
        context.setFillColor(UIColor.black.cgColor) // Background color
        context.fill(CGRect(origin: .zero, size: size))
        
        context.setStrokeColor(UIColor.cyan.cgColor) // Waveform color
        context.setLineWidth(1.0)
        
        let midY = size.height / 2
        let path = CGMutablePath()
        let step = max(1, samples.count / Int(size.width))
        
        for x in 0..<Int(size.width) {
            let sampleIndex = x * step
            if sampleIndex < samples.count {
                let sample = samples[sampleIndex]
                let y = CGFloat(sample) * midY
                path.move(to: CGPoint(x: CGFloat(x), y: midY - y))
                path.addLine(to: CGPoint(x: CGFloat(x), y: midY + y))
            }
        }
        context.addPath(path)
        context.strokePath()
    }
    return img
}

// Function to generate waveform asynchronously
func generateWaveformAsync(from samples: [Float], size: CGSize) async throws -> UIImage {
    return try await withCheckedThrowingContinuation { continuation in
        DispatchQueue.global(qos: .userInitiated).async {
            if let waveform = generateWaveform(from: samples, size: size) {
                continuation.resume(returning: waveform)
            } else {
                continuation.resume(throwing: ProcessingError.waveformGenerationFailed)
            }
        }
    }
}

// Function to overlay waveform on video
func overlayWaveform(on videoURL: URL, with waveformImage: UIImage, completion: @escaping (URL?) -> Void) {
    let asset = AVAsset(url: videoURL)
    let composition = AVMutableComposition()
    
    guard let videoTrack = asset.tracks(withMediaType: .video).first else {
        completion(nil)
        return
    }
    
    let videoCompositionTrack = composition.addMutableTrack(withMediaType: .video,
                                                            preferredTrackID: kCMPersistentTrackID_Invalid)
    do {
        try videoCompositionTrack?.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration),
                                                  of: videoTrack,
                                                  at: .zero)
    } catch {
        print("Error inserting video track: (error)") // Corrected string interpolation
        completion(nil)
        return
    }
    
    // Add audio track
    if let audioTrack = asset.tracks(withMediaType: .audio).first {
        let audioCompositionTrack = composition.addMutableTrack(withMediaType: .audio,
                                                                  preferredTrackID: kCMPersistentTrackID_Invalid)
        do {
            try audioCompositionTrack?.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration),
                                                      of: audioTrack,
                                                      at: .zero)
        } catch {
            print("Error inserting audio track: (error)")
            // Not aborting; audio is optional
        }
    }
    
    // Create video composition for overlay
    let videoComposition = AVMutableVideoComposition()
    videoComposition.renderSize = videoTrack.naturalSize
    videoComposition.frameDuration = CMTime(value: 1, timescale: 30)
    
    // Create instruction
    let instruction = AVMutableVideoCompositionInstruction()
    instruction.timeRange = CMTimeRange(start: .zero, duration: composition.duration)
    
    // Create layer instruction
    let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoCompositionTrack!)
    instruction.layerInstructions = [layerInstruction]
    videoComposition.instructions = [instruction]
    
    // Create parent layer and video layer
    let parentLayer = CALayer()
    let videoLayer = CALayer()
    
    parentLayer.frame = CGRect(origin: .zero, size: videoTrack.naturalSize)
    videoLayer.frame = CGRect(origin: .zero, size: videoTrack.naturalSize)
    parentLayer.addSublayer(videoLayer)
    
    // Configure waveform layer
    let waveformHeight: CGFloat = 100 // Adjust height as needed
    let waveformLayer = CALayer()
    waveformLayer.contents = waveformImage.cgImage
    waveformLayer.frame = CGRect(x: 0,
                                 y: videoTrack.naturalSize.height - waveformHeight, // Positioned at the bottom
                                 width: videoTrack.naturalSize.width,
                                 height: waveformHeight)
    waveformLayer.opacity = 0.8
    waveformLayer.shouldRasterize = true
    waveformLayer.rasterizationScale = UIScreen.main.scale
    parentLayer.addSublayer(waveformLayer)
    
    // Embed the waveform layer into the video composition
    videoComposition.animationTool = AVVideoCompositionCoreAnimationTool(
        postProcessingAsVideoLayer: videoLayer,
        in: parentLayer
    )
    
    // Export the video
    let exportPath = NSTemporaryDirectory() + "processedVideo_(UUID().uuidString).mov" // Corrected string interpolation
    let exportURL = URL(fileURLWithPath: exportPath)
    if FileManager.default.fileExists(atPath: exportPath) {
        try? FileManager.default.removeItem(at: exportURL)
    }
    
    guard let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) else {
        completion(nil)
        return
    }
    
    exporter.outputURL = exportURL
    exporter.outputFileType = .mov
    exporter.videoComposition = videoComposition
    exporter.shouldOptimizeForNetworkUse = true // Leveraging hardware acceleration
    
    exporter.exportAsynchronously {
        switch exporter.status {
        case .completed:
            completion(exportURL)
        case .failed, .cancelled:
            print("Export failed: (String(describing: exporter.error))")
            completion(nil)
        default:
            completion(nil)
        }
    }
}

// Function to overlay waveform asynchronously
func overlayWaveformAsync(on videoURL: URL, with waveformImage: UIImage) async throws -> URL {
    return try await withCheckedThrowingContinuation { continuation in
        overlayWaveform(on: videoURL, with: waveformImage) { processedURL in
            if let url = processedURL {
                continuation.resume(returning: url)
            } else {
                continuation.resume(throwing: ProcessingError.videoProcessingFailed)
            }
        }
    }
}

// Function to save video to Photos
func saveVideoToPhotos(url: URL) {
    DispatchQueue.main.async {
        UISaveVideoAtPathToSavedPhotosAlbum(url.path, nil, nil, nil)
    }
}

// Function to save image to Photos
func saveImageToPhotos(image: UIImage) {
    DispatchQueue.main.async {
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }
}