Search code examples
swiftxcodensdatam3u8

Swift - Check if the file download from a URL is completed


I am downloading mp4 clips from a M3U8 manifest which can have around 700 clips. Everything works fine to download them but what would be the best to check individual downloads are finished? When all the clips are downloaded, I merge them into one but I need to know when all my clips have been downloaded first.

This is the code snippet I use to download the video clip.

func download(video: String){
       DispatchQueue.global(qos: .background).async {
           if let url = URL(string: "http://SERVER/storage/sessions/SESSIONID/mp4_segments/\(video)"),
               let urlData = NSData(contentsOf: url) {
               let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0];
               let fileName = video
               let filePath = "\(documentsPath)/SegmentVideos/\(fileName)"
               urlData.write(toFile: filePath, atomically: true)
           }
       }
   }

This is the code snippet that reads the M3U8 file and splits it so I can grab the video clip's name.

func checkM3U8forClips(){

       guard let url = url else {return}

           do {
               let contents = try String(contentsOf: url)
               let splitContent = contents.components(separatedBy: "\n")
               for split in splitContent {
                   if split.hasSuffix("mp4") {
                      download(video: split)
                   }
               }
           } catch {
               print("error with mp4 segments: \(error.localizedDescription)")
           }
   }

Solution

  • One reason you are in a quandary is that this code is wrong:

     if let url = URL(string: "http://SERVER/storage/sessions/SESSIONID/mp4_segments/\(video)"),
         let urlData = NSData(contentsOf: url) {
    

    You must never use NSData(contentsOf:) to do networking. If you want to network, then network: use URLSession and a proper data task or download task. Now you get the callbacks you need; if you do it in the full form you get a full set of delegate callbacks that tell you exactly when a download has succeeded and completed (or failed).

    As for your overall question, i.e. how can I know when multiple asynchronous operations have all finished, that is what things like DispatchGroup, or operation dependencies, or the new Combine framework are for.