EAO123
EAO123

Reputation: 67

Firebase-Storage Upload Task progress observer not visible when I leave the view controller

So on the view controller that the user uploads the video to Firebase, I have a UIProgressView and UILabel that observe the progress of the upload task. This works perfectly fine. The problem, is when I leave the view controller. The UIProgressView is reset, and I don't know how to regain that information to resume observing the progress since it's all asynchronous. Is there any way that I can stop the view controller from reloading once the user has left that controller so that the progress view and label remain unchanged and not reloaded? Here is some code for reference (the function responsible for observing the task):

func uploadVideo(videoURL: URL)
    {
        let storage = Storage.storage()
        let storageRef = storage.reference()
        
        let videoRef = storageRef.child("rPosts/\(uid!)/\(fileID).mov")
        let metadata = StorageMetadata()
        metadata.contentType = "video/quicktime"
        
        var videoData: Data = Data()
        
        do
        {
            videoData = try Data(contentsOf: videoURL)
        }
        catch
        {
            print(error.localizedDescription)
            return
        }
        
        let taskReference = videoRef.putData(videoData, metadata: metadata)
        { (metaData, error) in
            guard error == nil else
            {
                self.errorLabel.text = error!.localizedDescription
                return
            }
        }
 
        taskReference.observe(.progress)
        { [weak self] (snapshot) in
            guard let progress = snapshot.progress?.fractionCompleted else { return }
            self?.progressView.progress = Float(progress)
            self?.progressLabel.text = "\(round(100 * Float(progress)))% (2/2)"
            
            if progress == 1
            {
                self!.progressLabel.text = "Upload Successful!"
                self!.progressLabel.textColor = .black
                self!.progressView.progress = 0
            }
        }
    }

How can I access taskReference?

Upvotes: 0

Views: 250

Answers (1)

Shubham
Shubham

Reputation: 773

You just need a better approach to handle this. You should not put this upload related stuff inside your view controller.

You can move upload related stuff to some separate file like below:

protocol StorageManagerObserver: AnyObject {
    func storageManager(_ storageManager: StorageManager, didProgressUpload fractionCompleted: Float)
    func storageManager(_ storageManager: StorageManager, didFailWithError error: Error)
}


class StorageManager: NSObject  {
    
    static var shared = StorageManager()
    private var observers: [StorageManagerObserver] = []
    
    let storage = Storage.storage()
    
    deinit {
        observers.removeAll()
    }
    
    func addObserver(_ observer: StorageManagerObserver) {
        if observers.contains(where: { $0 === observer }) == false {
            observers.append(observer)
        }
    }
    
    func removeObserver(_ observer: StorageManagerObserver) {
        guard let index = self.observers.firstIndex(where: { $0 === observer }) else {
            return
        }
        self.observers.remove(at: index)
    }
    
    
    func uploadVideo(videoURL: URL, uid: Int?, fileID: Int) {

        let storageRef = storage.reference()
        
        let videoRef = storageRef.child("rPosts/\(uid!)/\(fileID).mov")
        let metadata = StorageMetadata()
        metadata.contentType = "video/quicktime"
        
        var videoData: Data = Data()
        
        do {
            videoData = try Data(contentsOf: videoURL)
        }
        catch {
            print(error.localizedDescription)
            return
        }
        
        let taskReference = videoRef.putData(videoData, metadata: metadata) { [weak self] (metaData, error) in
            guard let self = self else { return }
            guard let someError = error else { return }
            for anObserver in self.observers {
                anObserver.storageManager(self, didFailWithError: someError)
            }
        }
        
        taskReference.observe(.progress) { [weak self] (snapshot) in
            guard let self = self else { return }
            guard let progress = snapshot.progress?.fractionCompleted else { return }
            for anObserver in observers {
                anObserver.storageManager(self, didProgressUpload: progress)
            }
        }
    }
}

I've made this StorageManager singleton, you can have some other approach as per you need (as we need to keep it alive until its done). Now, you use this to any view controller, like this:

class YourViewController: UIViewController {
    
    @IBOutlet weak var errorLabel: UILabel!
    @IBOutlet weak var progressLabel: UILabel!
    @IBOutlet weak var progressView: UIProgressView!

    override func viewDidLoad() {
        super.viewDidLoad()
       
        StorageManager.shared.addObserver(self)
    }
    
    deinit {
        StorageManager.shared.removeObserver(self)
    }
}

extension YourViewController: StorageManagerObserver {
    func storageManager(_ storageManager: StorageManager, didProgressUpload fractionCompleted: Float) {
        self.progressView.progress = fractionCompleted
        self.progressLabel.text = "\(round(100 * fractionCompleted))% (2/2)"
        
        if fractionCompleted == 1 {
            self.progressLabel.text = "Upload Successful!"
            self.progressLabel.textColor = .black
            self.progressView.progress = 0
        }
    }
    
    func storageManager(_ storageManager: StorageManager, didFailWithError error: Error) {
        self.errorLabel.text = error.localizedDescription
    }
}

Upvotes: 2

Related Questions