Swiftui - ZStack + DragGesture - how to remove first item

So I have created simple code like below.

I have 2 problems:

  • first is when you try do drag first video it is not being removed, instead last one (from the background) is.
  • I have a Task in .onAppear which download all videos at the same time. How to make is sync, so next video will be downloaded only when first one is completed?
import SwiftUI
import AVKit

struct Video: Identifiable {
  var id: Int
  var cacheName: String
  var reply: Bool
}



struct PlayerView: View {
  @Binding var data: [Video]
  @State private var offset = CGSize.zero
  @State private var video: Video?
  
  var body: some View {
    ZStack {
      ForEach(self.data.indices.reversed(), id: \.self) { index in
        let i = self.data[index]
        
        
        if index < 3 {
          if let player = CacheManager.shared.get(name: i.cacheName) {
            Player(player: player)
              .frame(width: 300, height: 300)
              .overlay {
                Text("\(index) \(i.id)").foregroundColor(.blue)
              }
            
              .scaleEffect(1 - 0.05 * CGFloat(index), anchor: .top)
              .offset(
                x: 0,
                y: ({
                  
                  if (index > 0) {
                    return CGFloat(index) * -5.5
                  }
                  //not a first card, or swiping up
                  if offset.height < 0 {
                    return 0
                  }
                  
                  // swiping down
                  return offset.height * 1.1
                }())
              )
              .onTapGesture(perform: {
                print("tapped", index)
                if index == 0 {
                  self.video = i
                }
              })
              .gesture(
                DragGesture()
                  .onChanged { gesture in
                    offset = gesture.translation
                  }
                  .onEnded { value in
                    if abs(offset.height) > 100 && index == 0 {
                      withAnimation {
                        print("iiii", i)
                        
                        // <<<<<< this function removes invalid element from the array, WHY?
                        self.data.removeAll { $0.cacheName == i.cacheName }
                        offset = .zero
                      }
                    } else {
                      offset = .zero
                    }
                  })
              .onAppear {
                if index == 0 {
                  player.play()
                } else {
                  player.pause()
                }
              }
              .onDisappear {
                player.pause()
              }
            
            
          } else {
            Text("not found")
          }
        } else {
          EmptyView()
        }
        
        
      }
    }
    .fullScreenCover(item: $video, content: { video in
      if let player = CacheManager.shared.get(name: video.cacheName) {
        ZStack {
          Player(player: player)
            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
            .ignoresSafeArea()
          
          VStack {
            Button(action: {
              player.seek(to: .zero, completionHandler: {_ in
                player.play()
              })
            }, label: {
              Text("PLAY").foregroundColor(.black)
            })
            
            Button(action: {
              self.video = nil
            }, label: {
              Text("CLOSE").foregroundColor(.black)
            })
          }
        }
      } else {
        Text("none")
      }
    })
  }
}

struct Player: UIViewControllerRepresentable {
  var player: AVPlayer
  
  func makeUIViewController(context: Context) -> AVPlayerViewController {
    let view = AVPlayerViewController()
    view.player = player
    view.showsPlaybackControls = false
    view.videoGravity  = .resizeAspectFill
    
    return view
  }
  
  func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
    
  }
}

let urls = [
  "https://dnr07ippqik25.cloudfront.net/01GXX7WFGJVZ11RCC6Q7XMYH02.mp4",
  "https://dnr07ippqik25.cloudfront.net/01GXX7D1MJRNG10BT16EV3RF4Z.mp4",
  "https://dnr07ippqik25.cloudfront.net/01GXX7FXXHPP3WGX7ZY5SCXCC5.mp4",
  "https://dnr07ippqik25.cloudfront.net/01GXX7F5R5MTEF6AWTRA806SAZ.mp4"
]

struct ContentView: View {
  @State var data: [Video] = []
  
  private let cm = CacheManager.shared
  
  var body: some View {
    PlayerView(data: $data)
      .onAppear {
        urls.indices.forEach { index in
          Task {
            do {
              
              let url = URL(string: urls[index])!
              let folder = try! FileManager.default
                .url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
              let destination = folder.appendingPathComponent(url.lastPathComponent)
              
              if cm.get(name: urls[index]) != nil {
                data.append(.init(id: index, cacheName: urls[index], reply: false))
              } else {
                try await downloadFiles(url: url, destination: destination)
                cm.add(player: AVPlayer(url: destination), name: urls[index])
                data.append(.init(id: index, cacheName: urls[index], reply: false))
              }
              
            } catch {
              print(error)
            }
          }
          
        }
        
        
        
      }
  }
}

func downloadFiles(url: URL, destination: URL) async throws {
  
  if !FileManager.default.fileExists(atPath: destination.path) {
    let (source, _) = try await URLSession.shared.download(from: url)
    try FileManager.default.moveItem(at: source, to: destination)
  } else {
    print("file already downloaded")
  }
}

class CacheManager {
  static let shared = CacheManager()
  
  private init() {}
  
  var avPlayerCache: NSCache<NSString, AVPlayer> = {
    let cache = NSCache<NSString, AVPlayer>()
    cache.countLimit = 50
    cache.totalCostLimit = 1024 * 1024 * 100 // 100MB
    return cache
  }()
  
  func add(player: AVPlayer, name: String) {
    avPlayerCache.setObject(
      player,
      forKey: name as NSString
    )
  }
  
  func remove(name: String) {
    avPlayerCache.removeObject(forKey: name as NSString)
  }
  
  func get(name: String) -> AVPlayer? {
    return avPlayerCache.object(forKey: name as NSString)
  }
  
}

Swiftui - ZStack + DragGesture - how to remove first item
 
 
Q