5

The UIWebView does not automatically support processing of Passbook .pkpass files.

In this technical note, Apple recommend implementing a check via the UIWebViewDelegate methods to sniff out the MIME type and process it accordingly.

To add passes using a UIWebView, implement the appropriate UIWebViewDelegate methods to identify when the view loads data with a MIME type of application/vnd.apple.pkpass

However, I cannot find anything within the UIWebView Delegate Protocol Reference that is capable of providing the MIME type.

I can successfully download and process files directly using an NSURLConnection delegate with no problem, but what I wish to achieve is for passes to be properly processed if a user clicks on an Add To Passbook button while browsing within a UIWebView. Since I do not know the link, and many providers do not suffix their links with a .pkpass extension, following Apple's advice of examining the MIME type seems the best way to go.

I have tried adding the following

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)newRequest 
                                                 navigationType:(UIWebViewNavigationType)navigationType 
{

   NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[newRequest URL]];

   // Spoof iOS Safari headers for sites that sniff the User Agent
   [req addValue:@"Mozilla/5.0 (iPhone; CPU iPhone OS 6_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25" forHTTPHeaderField:@"User-Agent"];

   NSURLConnection *conn = [NSURLConnection connectionWithRequest:newRequest delegate:self];

   return YES;
} 

My NSURLConnection delegate:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSString *mime = [response MIMEType];

    if ([mime isEqualToString:@"application/vnd.apple.pkpass"] && ![_data length]) {

        _data = nil; // clear any old data
        _data = [[NSMutableData alloc] init];

        [_webPanel stopLoading];
    }
}

-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
    [_data appendData:data];
    NSLog(@"Size: %d", [_data length]);
}

-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{

    if ([_data length]) {

        PKAddPassesViewController  *pkvc = [PassKitAPI  presentPKPassFileFromData:_data];
        pkvc.delegate = self;
        [self presentViewController:pkvc
                           animated:YES
                         completion:nil];
    }
}

The NSURLConnection delegates work fine when a connection is invoked directly, without the UIWebView. However, when I try launching an NSURLConnection from the UIWebView delegate the pass download fails because the only 80% or so of the .pkpass is being downloaded (I get a random mismatch of bytes in the _data variable and the Content-Length header).

So, my questions:

  1. Is there an easier way to get hold of a MIME type, directly from the UIWebView Delegate methods?
  2. If not, then am I going about this the right way with opening up a parallel NSURLConnection, or is there a better way?
  3. If an NSURLConnection is the way to go, then what could be causing it to stop short of downloading the full file?
PassKit
  • 12,231
  • 5
  • 57
  • 75
  • Did you find a solution for this? If yes can you share it please? – George Eracleous Sep 29 '13 at 14:09
  • No - I discussed it with Apple engineers at WWDC and they told me that there is no solution. I have a couple of open bug reports against the documentation and the UIWebView delegates. – PassKit Sep 29 '13 at 16:29
  • Have you figured this one out? I'm about to have to undertake this and would love to not have to manually hit the server again. – MrTristan Sep 07 '14 at 19:23
  • Unfortunately not - this I spoke again with the WebKit team at WWDC and they said they were still working on adding native support for .pkpass bundles. – PassKit Sep 08 '14 at 03:19

3 Answers3

2

Just use js

let contentType = webView.stringByEvaluatingJavaScript(from: "document.contentType;")
aryaxt
  • 76,198
  • 92
  • 293
  • 442
0
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {

    NSURL *url = request.URL;
    NSURLRequest *req  = [NSURLRequest requestWithURL:url];
    NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
    [conn start];
    return YES;
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{

    NSString *mime = [response MIMEType];
    NSLog(@"%@",mime);

}
iC7Zi
  • 1,528
  • 2
  • 15
  • 21
  • While this works, it has a huge overhead since every request is downloaded twice. Your code also stops at the MIME type - my problem is that the MIME type application/vnd.apple.pkpass is not directly supported by UIWebView and so I need to conditionally (and efficiently) download these files via a NSURLConnection, not indiscriminately download everything in parallel. – PassKit Sep 08 '13 at 01:40
  • Can you explain on above two points 1) Download twice , 2) Stops MIME Type. In my code all these delegates call only once. shouldStartLoadWithRequest,didReceiveResponse,webViewDidStartLoad,webViewDidFinishLoad. – iC7Zi Sep 08 '13 at 22:51
  • When these delegate methods trigger, the UIWebView is also downloading the file (but will eventually ditch it because it does not know how to process it natively). Using a NSURLConnection requires two independent requests to the server for the same resource (the original from UIWebView + the second form NSURLConnection). .pkpass files are CPU intensive to generate and can weigh in at up to 500kb. Ideally I should be able to access the data from the original UIWebView request, but the error delegate ditches this data. – PassKit Sep 09 '13 at 02:23
0

You could try subclassing NSURLProtocol and handling the response information parsing there.

Look at

- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy

Don't forget to about subresources also using these hooks.

cynistersix
  • 1,215
  • 1
  • 16
  • 30
  • 1
    Could you provide a little more detail how to approach that? It seems that the only waay to avoid having to double load resources is to use UIWebview or NSURLConnection exclusively, but to do the latter seens like a lot of work. – PassKit Oct 08 '13 at 00:48
  • Inside your NSURLProtocol you will get hit with every request that every subresource makes. You can tap into every response as well. You can check if you're a subresource based on the mainDocumentURL and the URL of the request (sometimes). The response information has a MIMEType property you can look at. – cynistersix Oct 09 '13 at 15:47