Reputation: 4278
NSURLProtocol
defines following methods:
/*!
@method propertyForKey:inRequest:
@abstract Returns the property in the given request previously
stored with the given key.
@discussion The purpose of this method is to provide an interface
for protocol implementors to access protocol-specific information
associated with NSURLRequest objects.
@param key The string to use for the property lookup.
@param request The request to use for the property lookup.
@result The property stored with the given key, or nil if no property
had previously been stored with the given key in the given request.
*/
+ (nullable id)propertyForKey:(NSString *)key inRequest:(NSURLRequest *)request;
/*!
@method setProperty:forKey:inRequest:
@abstract Stores the given property in the given request using the
given key.
@discussion The purpose of this method is to provide an interface
for protocol implementors to customize protocol-specific
information associated with NSMutableURLRequest objects.
@param value The property to store.
@param key The string to use for the property storage.
@param request The request in which to store the property.
*/
+ (void)setProperty:(id)value forKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;
/*!
@method removePropertyForKey:inRequest:
@abstract Remove any property stored under the given key
@discussion Like setProperty:forKey:inRequest: above, the purpose of this
method is to give protocol implementors the ability to store
protocol-specific information in an NSURLRequest
@param key The key whose value should be removed
@param request The request to be modified
*/
+ (void)removePropertyForKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;
But, if I trying to associate NSURLAuthenticationChallenge
with request, using following code
[NSURLProtocol setProperty:challenge forKey:@"challenge" inRequest:self.request];
I see next error in log:
2016-03-20 18:00:41.252 Web@Work[6084:586155] ERROR: createEncodedCachedResponseAndRequestForXPCTransmission - Invalid protocol-property list - CFURLRequestRef. protoProps=<CFBasicHash 0x7f98ec6dcca0 [0x10684e180]>{type = mutable dict, count = 3,
entries =>
0 : <CFString 0x101f47938 [0x10684e180]>{contents = "challenge"} = <NSURLAuthenticationChallenge: 0x7f98ec4cd700>
1 : <CFString 0x7f98e9fd03a0 [0x10684e180]>{contents = "Accept-Encoding"} = <CFBasicHash 0x7f98ec7c4490 [0x10684e180]>{type = mutable dict, count = 1,
entries =>
2 : <CFString 0x7f98ec78b930 [0x10684e180]>{contents = "Accept-Encoding"} = <CFString 0x106330828 [0x10684e180]>{contents = "gzip, deflate"}
}
2 : <CFString 0x1063310e8 [0x10684e180]>{contents = "kCFURLRequestAllowAllPOSTCaching"} = <CFBoolean 0x10684ebf0 [0x10684e180]>{value = true}
}
From this message, I suspect that only property list supported objects may be associated with NSURLRequest
using this API. But I do not completely understand why.
Also, my question is for which purpose this API should be used. How to use it correctly? I want to know what exactly setProperty:forKey:inRequest:
does, because it seems to do more work, than simply associate objects with request.
UPD So, minimal verifiable example. Very simple NSURLProtocol
subclass:
#import "MyURLProtocol.h"
#define kOurRecursiveRequestFlagProperty @"recursive"
@interface MyURLProtocol ()
@property NSURLConnection *connection;
@end
@implementation MyURLProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
return ![[[self class] propertyForKey:kOurRecursiveRequestFlagProperty inRequest:request] boolValue];
}
- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id <NSURLProtocolClient>)client {
return [super initWithRequest:request cachedResponse:cachedResponse client:client];
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
- (void)startLoading {
NSMutableURLRequest* mutableRequest = [[self request] mutableCopy];
[[self class] setProperty:@YES forKey:kOurRecursiveRequestFlagProperty inRequest:mutableRequest];
// Associate any non-property list object with mutableRequest to reproduce issue.
[[self class] setProperty:[UIApplication sharedApplication] forKey:@"dummyKey" inRequest:mutableRequest];
self.connection = [[NSURLConnection alloc] initWithRequest:mutableRequest delegate:self startImmediately:YES];
}
- (void)stopLoading {
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[[self client] URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[[self client] URLProtocol:self didLoadData:data];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse {
return request;
}
@end
UPD2 For now, I see that caching is to blame. Even when I use NSURLCacheStorageNotAllowed
, this code still tries to create cache dictionary, and write associated [UIApplication sharedApplication]
into it.
2016-03-20 20:52:19.630 WebViewDemo[7102:663895] ADD: failed to create cache dictionary at path=/Users/xxx/Library/Developer/CoreSimulator/Devices/89D7C50F-939B-4360-A19F-4547AE4F7515/data/Containers/Data/Application/6F05411C-0261-4A33-9531-9E4E900C4910/Library/Caches/Test.WebViewDemo. key=0x7fe76af45f90
So, now next my question is - how can I disable this caching without implementing my own associated dictionary. Should I do that, or I simply should avoid setProperty:forKey:inRequest:
.
And, most importantly, is that correct to use setProperty:forKey:inRequest:
to store kOurRecursiveRequestFlagProperty
, as Apple sample suggests? I'm still trying to figure out when I can use this methods, and when I can't.
Upvotes: 1
Views: 1441
Reputation: 10417
IIRC, the reason why the data has to be plist-serializable is that in background tasks, URL requests are serialized and sent over XPC to a background daemon that actually performs the URL fetch. If the data weren't serializable, it would get lost in transit.
The objects you store here need to be small, self-contained pieces of data for the purposes of allowing your protocol to identify that request.
Typically, if you need to associate something more complex with a request, you would use this method to store a UUID or some other arbitrary string. Then you would store that same string as a key in a normal NSDictionary within your app, disposing of the dictionary key and associated data after you tell the client that the request has completed/failed.
Upvotes: 2