-2

I have created the NSBundleResourceRequest object as instance member of the class. if i quit the app the request object is deallocatted. According to the apple document if the request object is deallocated the downloaded asset's control is taken care by the system and the data may be removed permanently. My doubt is ,if a game is using ODR for each level's meta data, we are not downloading the resourced every time while reopening the app. Do we have to manually save the ODR after it downloading the assets?

class ViewController: UIViewController {
    var request = NSBundleResourceRequest(tags: Set(arrayLiteral: "prefetch"))
    
    lazy var imageView1:UIImageView = {
        let imageView  = UIImageView(image: UIImage(named: "car"))
        imageView.layer.borderColor = UIColor.red.cgColor
        imageView.layer.borderWidth = 2
        return imageView
    }()
    let label = UILabel()
    lazy var  imageView2:UIImageView = {
        let imageView  = UIImageView(image:UIImage(named: "hd1"))
        imageView.layer.borderColor = UIColor.red.cgColor
        imageView.layer.borderWidth = 2
        return imageView
    }()
    
    lazy var imageView3:UIImageView = {
        let imageView  = UIImageView(image:UIImage(named: "hd2"))
        imageView.layer.borderColor = UIColor.red.cgColor
        imageView.layer.borderWidth = 2
        return imageView
    }()
    
    let button:UIButton = {
//        let config = UIButton.Configuration.bordered()
        let button = UIButton()
        button.setTitleColor(.white, for: .normal)
        button.backgroundColor = .green
        button.setTitle("Load HD image", for: .normal)
        return button
    }()
    
    override func loadView() {
        super.loadView()
        loadImage()
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureImageViews()
        fetchImage()
    }

    func configureImageViews(){
        view.addSubview(imageView1)
        view.addSubview(imageView2)
        view.addSubview(imageView3)
        view.addSubview(button)
        view.addSubview(label)
        
        label.translatesAutoresizingMaskIntoConstraints = false
        imageView1.translatesAutoresizingMaskIntoConstraints = false
        imageView2.translatesAutoresizingMaskIntoConstraints = false
        imageView3.translatesAutoresizingMaskIntoConstraints = false
        button.translatesAutoresizingMaskIntoConstraints = false

        label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        label.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
        label.heightAnchor.constraint(equalToConstant: 40).isActive = true
        
        imageView1.topAnchor.constraint(equalTo: label.safeAreaLayoutGuide.bottomAnchor).isActive = true
        imageView1.heightAnchor.constraint(equalToConstant: 200).isActive = true
        imageView1.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        imageView1.widthAnchor.constraint(equalToConstant: 200).isActive = true
        
        imageView2.topAnchor.constraint(equalTo: imageView1.safeAreaLayoutGuide.bottomAnchor,constant: 20).isActive = true
        imageView2.heightAnchor.constraint(equalToConstant: 200).isActive = true
        imageView2.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        imageView2.widthAnchor.constraint(equalToConstant: 200).isActive = true
        
        imageView3.topAnchor.constraint(equalTo: imageView2.safeAreaLayoutGuide.bottomAnchor,constant: 20).isActive = true
        imageView3.heightAnchor.constraint(equalToConstant: 200).isActive = true
        imageView3.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        imageView3.widthAnchor.constraint(equalToConstant: 200).isActive = true
        
        button.topAnchor.constraint(equalTo: imageView3.safeAreaLayoutGuide.bottomAnchor,constant: 20).isActive = true
        button.heightAnchor.constraint(equalToConstant: 40).isActive = true
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.widthAnchor.constraint(equalToConstant: 200).isActive = true
        
        button.addTarget(self, action: #selector(buttonDidTapped), for: .touchUpInside)
    }
    
    func fetchImage(){
        request.endAccessingResources()
        self.request.conditionallyBeginAccessingResources(completionHandler: {isImageAvailable in
            if isImageAvailable == true{
                //image already available
                print("--> Local data available")
                self.loadImage()
                DispatchQueue.main.async {
                    self.label.text = "Local data available"
                }
            }else{
                //download from app store
                self.request.beginAccessingResources(completionHandler: {error in
                    if let error{
                        print("--> something went wrong")
                        self.label.text = "something went wrong"
                        return
                    }
                    DispatchQueue.main.async {
                        self.label.text = "fetching remote"
                    }
                    self.loadImage()
                })
            }
            
        })
    }

    
    func loadImage(){
        DispatchQueue.main.async {
            self.imageView1.image = UIImage(named: "car")
            self.imageView2.image = UIImage(named: "hd1")
            self.imageView3.image = UIImage(named: "hd2")
        }
    }
    
   @objc func buttonDidTapped(){
        request = NSBundleResourceRequest(tags: Set(arrayLiteral: "hd1"))
       fetchImage()
    }
}


I expect that how we can assure that the ODR data is not downloaded again?

iroh
  • 23
  • 5

1 Answers1

0

Your use of a particular ODR resource must be bracketed by calls to beginAccessingResources and endAccessingResources, between which you retain the NSBundleResourceRequest. That's all you know and all you need to know.

If your app is terminated before you call endAccessingResources, then on relaunch you obviously have no NSBundleResourceRequest instance; but that doesn't matter, because you just keep following the rules about how to access the resources. Immediately on launch, create an NSBundleResourceRequest and call beginAccessingResources; your resources are probably still present, and so you will get access immediately, and your retention of the NSBundleResourceRequest keeps the resources present going forward until you ultimately release them by saying endAccessingResources.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • you mentioned `your resources are probably still present`. If the system needs memory, may be it will remove our resources when our app is closed. is that correct? – iroh Jun 20 '23 at 00:52