4

I'm using NSURLCache and I'd like to set a custom key for a NSURLRequest. Is that possible?

Complete explanation:

I'm developing a mapping app, that uses OpenStreetMap tiles. OpenStreetMap provides multiple servers to serve the tiles, to reduce the load on each server. I'm using these servers randomly. So for instance, the following URLs will give the same tile:

Obviously, it causes some problems for my caching, because if I cache a tile from server A, next time, if I try to load from server B, the NSURLCache won't find the tile.

So, I'd like to set myself the cache key, in order to handle that case. Is that possible?

Kara
  • 6,115
  • 16
  • 50
  • 57
Tim Autin
  • 6,043
  • 5
  • 46
  • 76
  • The other approach is to use `NSCache` to cache your responses and before you initiate a request, see if you have what you need in your `NSCache`. You can use whatever key you want there. – Rob Jun 07 '16 at 20:15

2 Answers2

5

You can subclass NSURLCache and override the implementations of cachedResponseForRequest: and storeCachedResponse:forRequest: -- then use setSharedURLCache: to set it to your subclass.

The easiest thing is probably to call super for storing (or not override), but then on the lookup, if it's a tile request, check all the possibilities (using super) and return the response if you get a non-nil one.

Lou Franco
  • 87,846
  • 14
  • 132
  • 192
  • Thanks, subclassing was the solution. I will post my own answer though, as yours doesn't address the custom key part. – Tim Autin Jun 08 '16 at 11:30
2

So, I finally created my own NSURLCache, modifying its behavior completely, to a more "manual" mode: now, requests are not cached by default, but I can manually put/get responses with a custom key. Here is the code:

import Foundation

extension NSURLCache {

    public func put(cachedResponse:NSCachedURLResponse, forKey key:String) {}
    public func get(key:String) -> NSCachedURLResponse? { return nil; }
}

public class CustomNsUrlCache: NSURLCache {

    // Prevent default caching:
    public override func cachedResponseForRequest(request: NSURLRequest) -> NSCachedURLResponse? { return nil; }
    public override func storeCachedResponse(cachedResponse: NSCachedURLResponse, forRequest request: NSURLRequest) {}

    private func requestForKey(key:String) -> NSURLRequest {

        let url:NSURL? = NSURL(string:"http://" + key);
        return NSURLRequest(URL:url!);
    }

    public override func put(cachedResponse:NSCachedURLResponse, forKey key:String) {

        super.storeCachedResponse(cachedResponse, forRequest:requestForKey(key));
    }

    public override func get(key:String) -> NSCachedURLResponse? {

        return super.cachedResponseForRequest(requestForKey(key));
    }
}

Don't forget to register the custom code in the app's delegate:

let cache = CustomNsUrlCache(memoryCapacity: 4 * 1024 * 1024, diskCapacity: 100 * 1024 * 1024, diskPath: nil);
NSURLCache.setSharedURLCache(cache);

Usage:

if let cachedResponse = NSURLCache.sharedURLCache().get(cacheKey) {

    // Use cached response
}
else {

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {

        data, response, error in

        if let data = data, response = response {

            // Cache the response:
            NSURLCache.sharedURLCache().put(NSCachedURLResponse(response:response, data:data), forKey:cacheKey);

            // Use the fresh response
        }
        else {

            print(error);
        }
    };

    task.resume();
}
Tim Autin
  • 6,043
  • 5
  • 46
  • 76