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?
Your use of a particular ODR resource must be bracketed by calls to
beginAccessingResourcesandendAccessingResources, 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 callbeginAccessingResources; 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 sayingendAccessingResources.