NSURLProtocol - startLoading not called after canInitWithRequest returns YES for page resource
Asked Answered
P

2

8

I'm having an issue where a video resource in my UIWebView is not loading. This is only an issue on an actual device- everything works perfectly on the simulator.

Note: I've already checked this and the issue there has to do with loading the same content multiple times. NSURLProtocol isn't asked to load after YES response to canInitWithRequest

The actual page loads fine- in my custom NSURLProtocol, it returns YES from canInitWithRequest (several times) and then startLoading is called after about the 4th or 5th time. It then loads my css file in a similar fashion. Then I see that the request for my video object returns YES from canInitWithRequest, but nothing happens with it. startLoading is never called for the request of my video resource. Here's the simplified log from my device when I load the page:

canInitWithRequest: https://WebpageURL
canInitWithRequest: https://WebpageURL
canInitWithRequest: https://WebpageURL
canInitWithRequest: https://WebpageURL
canInitWithRequest: https://WebpageURL
START LOADING: https://WebpageURL
canInitWithRequest: https://WebpageURL
canInitWithRequest: https://WebpageURL
canInitWithRequest: https://WebpageURL
canInitWithRequest: https://cssResource
START LOADING: https://cssResource
canInitWithRequest: https://MyVideoResource

When I load the exact same page from the simulator running the same iOS version on the exact same device type, the logs show that the resource actually starts loading here for some reason:

canInitWithRequest: https://WebpageURL
canInitWithRequest: https://WebpageURL
canInitWithRequest: https://WebpageURL
START LOADING: https://WebpageURL
canInitWithRequest: https://WebpageURL
canInitWithRequest: https://cssResource
START LOADING: https://cssResource
canInitWithRequest: https://MyVideoResource
canInitWithRequest: https://MyVideoResource
START LOADING: https://MyVideoResource  -- WHY NOT ON DEVICE?

The page is left with a video that never plays- just a white box where it should be and the "Play video" button over it. The page looks like it's constantly loading something (Activity indicator in the status bar is spinning) but nothing is happening, and the network activity in Xcode's Debug navigator is flatlined.

tl;dr
Does anyone know why loading this resource is never attempted on my iPhone? Obviously the simulator knows that it needs to be loaded.


EDIT: Here is the slightly redacted relevant code from my NSURLProtocol

@implementation CHHTTPURLProtocol
{
    NSURLConnection *connection;
    NSHTTPURLResponse *httpResponse;
    NSData *responseData;
}

+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    if (/* key exists on request */)
        return NO;
    else if (/* not http or https */)
        return NO;

    NSLog(@"canInitWithRequest: %@", request.URL.absoluteString);

    return YES;
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
    return request;
}

- (void)startLoading
{
    NSMutableURLRequest *request = [self.request mutableCopy];
    NSLog(@"START LOADING: %@", request.URL.absoluteString);
    if (authenticate) authenticate(request);
    /* add key to request */
    connection = [NSURLConnection connectionWithRequest:request delegate:self];
}

- (void)stopLoading
{
    [connection cancel];
    connection = nil;
}

/* Other methods like connection:didReceiveData: and connectionDidFinishLoding: */

+ (void)authenticateWithBlock:(AuthenticationBlock)block
{
    authenticate = block;
}

static AuthenticationBlock authenticate;
Piggy answered 12/10, 2015 at 20:5 Comment(4)
Few questions: Simulator and device run the same iOS version ? Is the network the same on your phone and mac where you run the simulator ? Source code you use to load the video ?Falcate
@Falcate Both simulator and device were running 8.4. Both were connected to the same wifi, and were loading the exact same HTML resource. Post has been updated with relevant NSURLProtocol code.Piggy
why don't you return YES on all canInitWithRequest? or at least print out request in the canInitWithRequest to find out the difference?Barefoot
There is no need to return YES if the request is already started, and I don't want to load resources that aren't specifically over http or https. I was logging the request URL, I just didn't include that in my code above. I will add it again. Logging out the entire request only shows the URL and memory location of the request object.Piggy
S
0

I don't have a definite answer but I do have a couple of suggestions which may solve this: Have you considered caching? If the UIWebView thinks it already has a copy of the video, it won't call.

when iOS 8 receive an HTTP response with a Keep-Alive header, it keeps this connection to re-use later (as it should), but it keeps it for more than the timeout parameter of the Keep-Alive header. The video is the last thing to load and if the Mac is faster than the real device, it might be it. Looking at your code, if the NSURLRequest is nil after canInitWithRequest but before startLoading, you would still see something in NSLog.

  1. The Simulator may be using the OSX AVFoundation to play the video. This means that the frameworks are different and fewer codecs are supported on iOS.
  2. The ios device is case sensitive when it comes to file names whilst the simulator is not.
  3. Assuming iOS-Specific Considerations are different between simulator and real device, if this is a second video in the UIWebView, it definitely shouldn't play on iOS but may play on the simulator.
Sack answered 26/10, 2015 at 20:36 Comment(1)
Point 1- I don't think the OS even knows what kind of file is at the other end of the URL yet because it hasn't attempted to load it (no response headers). Point 2- it is not a file hosted on the device, it is a URL. Point 3- it is the only video on the page.Piggy
C
0

My best guess is that the video player used in the UIWebView isn't loading it's content using Foundation services (NSURLSession/NSURLRequest, etc.) which pipe through NSURProtocol. Likely the video player is making requests using the lower-level CFNetwork api.

The implementation likely differs from simulator to device.

I get that your protocol is polled with the canInitWithRequest call, but this doesn't necessarily mean that the player is going to actually load the resource using a NSURLSession/NSURLRequest.

Attempting to load a video via a custom protocol into a MPMoviePlayerViewController has similar behavior as to what you're seeing: How to play movie with a URL using a custom NSURLProtocol?

Clamshell answered 27/10, 2015 at 17:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.