5

I am currently trying to implement Apple's API for On Demand Resource Management for AWS Cloudfront because Apple's ODR is somehow too unreliable.

I noticed that when I tag images inside of Assets.scnassets/ with an ODR resource tag, I can access that image using

UIImage(name: resourceName)

once it has been downloaded by a NSBundleRequest object. Because I can access the downloaded resource as a UIImage, I know that the resource is located in the app's main bundle but I thought this was impossible because Bundles were read-only. How did apple do this? The most important aspect is being able to create UIImages using this incredibly simple interface.

btomtom5
  • 842
  • 8
  • 16

2 Answers2

3

I know that the resource is located in the app's main bundle but I thought this was impossible because Bundles were read-only. How did apple do this?

It’s an illusion. ODR files are downloaded to a directory outside the app bundle, and calls like UIImage init(named:) are swizzled to look in that directory.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Woah thanks for that insight. What monitoring tools did you use to learn this? – btomtom5 Nov 19 '19 at 02:52
  • 1
    Actually you can discover this directly thru Xcode. Your app's sandbox (not the app bundle) has an _OnDemandResources_ directory which is where the downloaded stuff actually lives. – matt Nov 19 '19 at 03:00
1

Not sure how it is done technically, but I am pretty certain that attributing ODR to a particular bundle (typically main) is done on purpose, so you can rely on it.

I am using RxOnDemandResources library to fetch hundreds of MP3 from dozens of tags, and it is done on a per bundle basis -

Bundle
    .main
    .rx
    .demandResources(withTags: tags) // tags: Set<String>
    .subscribeOn(Scheduler.concurrentUser) // declare your scheduler first
    .observeOn(Scheduler.concurrentMain) // declare your scheduler first
    .subscribe { event in
        switch event {
        case .next(let progress):
            self.showProgress(progress: Float(progress.fractionCompleted)) // declare your handler first
        case .error(let error):
            break // handle appropriately
        case .completed:
            if let url = Bundle.main.url(forResource: file, withExtension: "") {
                os_log("url: %@", log: Log.odr, type: .info, "\(url)")
                // TODO use your resource
            }
        }
    }
    .disposed(by: self.disposeBag)

Back to your question: yes you can access ODR resources via specific bundle as usual but with all preconditions (checking availability, fetching if needed) - i.e. not always.

Maxim Volgin
  • 3,957
  • 1
  • 23
  • 38