Search code examples
iosonedrive

iOS OneDrive SDK - needs update for deprecated UIWebView


OneDriveSDK for iOS hasn't been updated in a while and uses the deprecated UIWebView for sign in. Apple will stop accepting apps that use UIWebView. Because we don't know if/when Microsoft will update this SDK, I wanted to share the code changes I made which uses WKWebView instead.


Solution

  • This solution is now outdated. Don't use.


    My solution updates ODAuthenticationViewController.m

    See code below.
    I added comment "ern2" to places I made updates.

    In ADAL target/pod, I removed the reference to 4 files that references UIWebView that I'm not using. (If you are using these, then my solution doesn't work.)

    ADAuthenticationViewController.h
    ADAuthenticationViewController.m
    ADAuthenticationWebViewController.h
    ADAuthenticationWebViewController.m

    //  Copyright 2015 Microsoft Corporation
    //
    //  Permission is hereby granted, free of charge, to any person obtaining a copy
    //  of this software and associated documentation files (the "Software"), to deal
    //  in the Software without restriction, including without limitation the rights
    //  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    //  copies of the Software, and to permit persons to whom the Software is
    //  furnished to do so, subject to the following conditions:
    //  
    //  The above copyright notice and this permission notice shall be included in
    //  all copies or substantial portions of the Software.
    //  
    //  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    //  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    //  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    //  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    //  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    //  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    //  THE SOFTWARE.
    //
    
    
    #import "ODAuthenticationViewController.h"
    #import "ODAuthHelper.h"
    #import "ODAuthConstants.h"
    
    #define kRequestTimeoutDefault  60
    
    @interface ODAuthenticationViewController() <WKNavigationDelegate>  // Ernie Ern2 UIWebVie_wDelegate
    
    @property WKWebView *webView;   // Ern2
    
    @property NSURLRequest *initialRequest;
    @property (strong, nonatomic) ODEndURLCompletion successCompletion;
    @property (strong, nonatomic) NSURL *endURL;
    
    @property (strong, nonatomic) NSTimer *timer;
    @property (nonatomic) BOOL isComplete;
    
    @end
    
    @implementation ODAuthenticationViewController
    
    - (instancetype)initWithStartURL:(NSURL *)startURL
                              endURL:(NSURL *)endURL
                             success:(ODEndURLCompletion)sucessCompletion
    {
        self = [super init];
        if (self){
            _endURL = endURL;
            _initialRequest = [NSURLRequest requestWithURL:startURL];
            _successCompletion = sucessCompletion;
            _requestTimeout = kRequestTimeoutDefault;
            _isComplete = NO;
        }
        return self;
    }
    
    - (void)cancel
    {
        if (!self.isComplete)
        {
            [self.timer invalidate];
            self.timer = nil;
            self.isComplete = YES;
            
            NSError *cancelError = [NSError errorWithDomain:OD_AUTH_ERROR_DOMAIN code:ODAuthCanceled userInfo:@{}];
            if (self.successCompletion){
                self.successCompletion(nil, cancelError);
            }
        }
    }
    
    - (void)loadInitialRequest
    {
        [self.webView loadRequest:self.initialRequest];
    }
    
    - (void)redirectWithStartURL:(NSURL *)startURL
                          endURL:(NSURL *)endURL
                          success:(ODEndURLCompletion)successCompletion
    {
        self.endURL = endURL;
        self.successCompletion = successCompletion;
        self.initialRequest = [NSURLRequest requestWithURL:startURL];
        self.isComplete = NO;
        [self.webView loadRequest:self.initialRequest];
    }
    
    - (void)loadView
    {
        self.webView = [[WKWebView alloc] init];
        // Ern2 [self.webView setScalesPageToFit:YES];
        self.webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        self.webView.navigationDelegate = self; // Ern2
        self.view = self.webView;
        UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
                                                                                    target:self
                                                                                    action:@selector(cancel)];
        self.navigationController.topViewController.navigationItem.leftBarButtonItem = cancel;
        
    }
    
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        [self.webView loadRequest:self.initialRequest];
    }
    
    - (void)viewWillDisappear:(BOOL)animated
    {
        [self.webView stopLoading];
        self.webView.navigationDelegate = nil;
        [super viewWillDisappear:animated];
    }
    
    #pragma mark - UI_WebViewDelegate
    
    // Ern2
    
    - (void) webView: (WKWebView *) webView didStartProvisionalNavigation: (null_unspecified WKNavigation *) navigation {
        [self.timer invalidate];
        self.timer = [NSTimer scheduledTimerWithTimeInterval:self.requestTimeout target:self selector:@selector(failWithTimeout) userInfo:nil repeats:NO];
    }
    /*
    - (void)webViewDidStartLoad:(UIWebVie_w *)webView
    {
        [self.timer invalidate];
        self.timer = [NSTimer scheduledTimerWithTimeInterval:self.requestTimeout target:self selector:@selector(failWithTimeout) userInfo:nil repeats:NO];
    } */
    
    - (void) webView: (WKWebView *) webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
        [self.timer invalidate];
        self.timer = nil;
    }
    /*
    - (void)webViewDidFinishLoad:(UIWebVie_w *)webView
    {
        [self.timer invalidate];
        self.timer = nil;
    } */
    
    - (void) webView: (WKWebView *) webView decidePolicyForNavigationAction: (WKNavigationAction *) navigationAction decisionHandler: (void (^)(WKNavigationActionPolicy)) decisionHandler {
        
        //NSLog(@"[ept] %@   %@", [navigationAction.request.URL absoluteString], [self.endURL absoluteString]);
        if ([[[navigationAction.request.URL absoluteString] lowercaseString] hasPrefix:[[self.endURL absoluteString] lowercaseString]]){
            self.isComplete = YES;
            [self.timer invalidate];
            self.timer = nil;
            
            self.successCompletion(navigationAction.request.URL, nil);
            decisionHandler(WKNavigationActionPolicyCancel);
        }
        else decisionHandler(WKNavigationActionPolicyAllow);
    }
    
    /*
    - (BOOL)webView:(UIWebVie_w *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebVie_wNavigationType)navigationType
    {
        if ([[[request.URL absoluteString] lowercaseString] hasPrefix:[[self.endURL absoluteString] lowercaseString]]){
            self.isComplete = YES;
            [self.timer invalidate];
            self.timer = nil;
            
            self.successCompletion(request.URL, nil);
            return NO;
        }
        return YES;
    } */
    
    - (void) webView: (WKWebView *) webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(nonnull NSError *)error {
        
        [self.timer invalidate];
        self.timer = nil;
        
        if (NSURLErrorCancelled == error.code)
        {
            //This is a common error that webview generates and could be ignored.
            //See this thread for details: https://discussions.apple.com/thread/1727260
            return;
        }
        
        if([error.domain isEqual:@"WebKitErrorDomain"]){
            return;
        }
        
        // Ignore failures that are triggered after we have found the end URL
        if (self.isComplete)
        {
            //We expect to get an error here, as we intentionally fail to navigate to the final redirect URL.
            return;
        }
        
        if (self.successCompletion) {
            self.successCompletion(nil, error);
        }
    }
    /*
    - (void)webView:(UIWebVie_w *)webView didFailLoadWithError:(NSError *)error
    {
        [self.timer invalidate];
        self.timer = nil;
        
        if (NSURLErrorCancelled == error.code)
        {
            //This is a common error that webview generates and could be ignored.
            //See this thread for details: https://discussions.apple.com/thread/1727260
            return;
        }
        
        if([error.domain isEqual:@"WebKitErrorDomain"]){
            return;
        }
        
        // Ignore failures that are triggered after we have found the end URL
        if (self.isComplete)
        {
            //We expect to get an error here, as we intentionally fail to navigate to the final redirect URL.
            return;
        }
        
        if (self.successCompletion) {
            self.successCompletion(nil, error);
        }
    }
    */
    
    - (void)failWithTimeout
    {
        [self webView: self.webView didFailNavigation: nil withError: [NSError errorWithDomain: NSURLErrorDomain code: NSURLErrorTimedOut userInfo:nil]];
        //[self webView:self.webView didFailLoadWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:nil]];
    }
    
    @end