Reputation: 1709
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.
Upvotes: 3
Views: 736
Reputation: 1709
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
Upvotes: 2
Reputation: 1709
The "accepted" answer is now outdated, as you shouldn't be using the old OneDrive pod anymore.
OneDrive API for iOS really made me frustrated at the lack of simple instructions and setup. The Microsoft main demo pages don't even link to iOS samples anymore (at least for what I found).
You need to use GRAPH api. The ObjC Graph pod is outdated it seems, and not sure if/when it will be updated.
MSAL is the authentication library used to authenticate to Microsoft/Graph. This is writen in ObjC. No problem. This IS current and maintained. Use this.
Problem with not having a Graph/OneDrive API pod from Microsoft (unless you want to use "old" one) means you need to figure out all the calls yourself, and do the low level GET/PUT/POST calls, and figure out the URLs. Somewhat troublesome.
Below are links I found useful:
Sample swift code that only uses MSAL cocoapod. I like this because clean; doesn't use old Graph API, which hasn't been updated since 2015 not version 1.0 yet. Downside is you have to figure out your own URL calls, but sample includes a demo call https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-ios
Another older tutorial. The archived tutorial pages (link below) has some setup details I found useful, to compliment above demo. Above demo might all you need, but I found this demo first and some of the instructions related to MSAL setup were helpful. Uses the "outdated" (?) ObjC Graph API cocoapod https://github.com/microsoftgraph/msgraph-training-ios-swift/tree/main/tutorial
OneDrive graph API info: https://learn.microsoft.com/en-us/onedrive/developer/rest-api/?view=odsp-graph-online
General graph info: https://learn.microsoft.com/en-us/graph/use-the-api
Query params: https://learn.microsoft.com/en-us/graph/query-parameters
Paging: https://learn.microsoft.com/en-us/graph/paging
Upvotes: 0