title: How to programmatically download Images from drone using the IOS DJI-SDK
body: <p>I've been having some issues downloaded images from my drone directly to my application. I keep getting a "<strong>System is busy, please retry later.(code:-1004)</strong>" error message when attempting to download the image, I have checked the DJI forum and other questions here on stackoverflow and have not been able to find any solution to this problem. </p>
  
  <p>I've already taken a look at <a href="https://stackoverflow.com/questions/45872030/retrieve-images-from-a-dji-drone-with-exif-data">this question</a>, but i'm already using the technique suggested from that answer. </p>
  
  <p>Here is the function I've written to download the images:</p>
  
  <pre><code>func downloadFilesFromDrone(){
  
      // get current product
      guard let drone = DJISDKManager.product() else {
          Logger.logError("Product is connected but DJISDKManager.product is nil when attempting to download media")
          return
      }
  
  
      // Get camera on drone
      guard  let camera: DJICamera = drone.camera else {
          Logger.logError("Unable to detect Camera in downloadFilesFromDrone()")
          // make recursive call until we are able to detect the camera
          DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
              Logger.logH1("Trying to detect Camera again")
              self.downloadFilesFromDrone()
          }
          return
      }
  
      Logger.logH1("Successfully detected the camera")
  
      // check if we can download images with the product
       if !camera.isMediaDownloadModeSupported() {
          Logger.logError("Product does not support media download mode")
          return
      }
  
  
      // switch camera mode to allow for media downloads
      camera.setMode( .mediaDownload, withCompletion: {(error) in
          if error != nil {
              print("\(error!.localizedDescription)")
          }
          else {
  
              // get the media manager from the drone to gain access to the files
              let manager = camera.mediaManager!
              manager.refreshFileList(completion: { (error) in
  
                  if error != nil {
                      print("State: \(manager.fileListState.rawValue)")
                      print("Error refreshing list: \(error!.localizedDescription)")
                  }
                  else {
                      Logger.logH1("Refreshed file list")
                      print("State: \(manager.fileListState.rawValue)")
  
  
                      guard let files = manager.fileListSnapshot() else {
                          Logger.logError("No files to download")
                          return
                      }
  
                      Logger.logH1("There are files to download")
  
                      var images: [UIImage] = []
  
                      for file in files {
  
                          if file.mediaType == .JPEG {
  
                              print("Time created: \(file.timeCreated)")
  
                              DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  
                                  file.fetchData(withOffset: 0, update: DispatchQueue.main, update: {(_ data: Data?, _ isComplete: Bool, _ error: Error?) -&gt; Void in
  
                                      if error != nil {
                                          print("State: \(manager.fileListState.rawValue)")
                                          print("Error downloading photo: \(error!)")
                                      }
                                      else {
                                          // unwrap downloaded data and create image
                                          if let data = data, let downloadedImage = UIImage(data: data) {
                                              print("Image was downloaded!")
                                              images.append( downloadedImage )
                                          }
                                      }
  
                                  }) // end of filedata fetch
  
                              }
  
                          }
  
                      } // end of loop
  
  
                  }
              }) // end of file-refresh block
  
          }
  
      })// end of camera setMode block
  
  }
  </code></pre>
  
  <p>And here is the output of this function when testing with my drone:</p>
  
  <pre><code>*** Product Connected ***
  *** Unable to detect Camera in downloadFilesFromDrone() ***
  *** Firmware package version is: Unknown ***
  --&gt; Trying to detect Camera again
  --&gt; Successfully detected the camera
  --&gt; Refreshed file list
  State: 0
  --&gt; There are files to download
  Time created: 2017-09-01 15:17:04
  Time created: 2017-09-01 15:17:16
  Time created: 2017-09-01 15:17:26
  Time created: 2017-09-01 15:17:36
  Time created: 2017-09-01 15:19:06
  State: 0
  Error downloading photo: System is busy, please retry later.(code:-1004)
  State: 0
  Error downloading photo: System is busy, please retry later.(code:-1004)
  State: 0
  Error downloading photo: System is busy, please retry later.(code:-1004)
  State: 0
  Error downloading photo: System is busy, please retry later.(code:-1004)
  </code></pre>
  
  <h1>Edit: </h1>
  
  <p><b>Below is the code I used to download images from the drone.</b></p>
  
  <pre><code>/**
   * This function downloads the N latest images from the drone and passes them to the completionhandler once all images have completed downloading
   */
  func downloadImages( files: [DJIMediaFile], howMany: Int, maxErrors: Int, completion: @escaping ([UIImage]) -&gt; Void){
  
      Logger.logH1("Queueing \(howMany) image(s) to be downloaded")
  
      func downloadNextImage( files: [DJIMediaFile], fileCount: Int, index: Int = 0, downloadedFiles: [UIImage] = [], errorCount: Int = 0) {
  
          // stop when we reach the end of the list
          if index == fileCount {
              completion(downloadedFiles)
              return
          }
          else {
              var imageData: Data?
              let file = files[index]
  
              file.fetchData(withOffset: 0, update: DispatchQueue.main, update: {(_ data: Data?, _ isComplete: Bool, _ error: Error?) -&gt; Void in
  
                  if let error = error {
                      Logger.logError("\(error)")
  
                      if errorCount &lt; maxErrors {
                          DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                              Logger.logH1("Attempting to download: \(file.fileName) again")
                              downloadNextImage(files: files, fileCount: fileCount, index: index, downloadedFiles: downloadedFiles, errorCount: errorCount + 1)
                          }
  
                      }
                      else {
                          Logger.logError("Too many errors downloading the images, try downloading again")
                      }
  
  
                  }
                  else {
                      // if image is done downloading
                      if isComplete {
  
                          // get full image data
                          if let imageData = imageData, let image = UIImage(data: imageData) {
                              Logger.logH1("Downloaded: \(file.fileName)")
  
                              // now that the image is done downloading, move onto the next image
                              downloadNextImage(files: files, fileCount: fileCount, index: (index + 1), downloadedFiles: downloadedFiles + [image], errorCount: 0)
                          }
                      }
                          // else, download the file
                      else {
  
                          // If image exists, append the data
                          if let _ = imageData, let data = data {
                              imageData?.append(data)
                          }
                              // initialize the image data
                          else {
                              imageData = data
                          }
  
                      }
                  }
  
  
              }) // end of filedata fetch
  
  
          }   // end of else statement
      }
  
      // bounds checking
      let available = files.count
      let n = howMany &gt; available ? available : howMany
  
      // grab the N latest images taken by the drone
      let filesToDownload : [DJIMediaFile] = Array ( files.suffix(n) )
  
  
      // start the recursive function
      downloadNextImage(files: filesToDownload, fileCount: filesToDownload.count)
  }
  </code></pre>
  
  <p><b> And here is how to call it: </b></p>
  
  <pre><code>  // get current product
      guard let drone = DJISDKManager.product() else {
          Logger.logError("Product is connected but DJISDKManager.product is nil when attempting to download media")
          return
      }
  
  
      // Get camera on drone
      guard  let camera: DJICamera = drone.camera else {
          Logger.logError("Unable to detect Camera in initDownload()")
          return
      }
  
      Logger.logH1("Successfully detected the camera")
  
      // check if we can download images with the product
      if !camera.isMediaDownloadModeSupported() {
          Logger.logError("Product does not support media download mode")
          return
      }
  
      // switch camera mode to allow for media downloads
      camera.setMode( .mediaDownload, withCompletion: {(error) in
          if error != nil {
              print("\(error!.localizedDescription)")
          }
          else {
  
              // get the media manager from the drone to gain access to the files
              let manager = camera.mediaManager!
              manager.refreshFileList(completion: { (error) in
  
                  if error != nil {
                      print("State: \(manager.fileListState.rawValue)")
                      print("Error refreshing list: \(error!.localizedDescription)")
                  }
                  else {
                      Logger.logH1("Refreshed file list")
                      print("State: \(manager.fileListState.rawValue)")
  
                      // get list of files
                      guard let files = manager.fileListSnapshot() else {
                          Logger.logError("No files to download")
                          return
                      }
  
                      Logger.logH1("There are files to download.. Beginning Download")
                      self.downloadImages(files: files, howMany: waypoints, maxErrors: 4, completion: { images in
                          Logger.logH1("Finished downloading: \(images.count) image(s)")
                          // do something with the images here
                      })
  
  
  
                  }
              }) // end of file-refresh block
  
          }
  
      })// end of camera setMode block
  </code></pre>