3

I have a WKWebview loading a web based UI where I would like users to be able to upload a file from their iCloud documents. I have granted the correct permissions and I am able to browse the iCloud documents. However when I either select a file or click the cancel button, as well as the document picker view dismissing the parent view of my WKWebview is also dismissed.

I have tried to track the dismiss path. I am 100% sure I am not calling the dismiss function on my view.

Does anyone have any idea what is triggering the dismiss on my WKWebview container and how to prevent it?

AndyRyan
  • 1,500
  • 12
  • 23

2 Answers2

4

There is a bug in UIDocumentPickerViewController.

1) Save weak reference to UIDocumentPickerViewController inside what ever view controller presents the UIDocumentPickerViewController. (This usually end up being a UINavigationController so you will probably have to subclass UINavigationController to fix this.)

///Due to a bug in UIDocumentPickerViewController we need to stop the UIDocumentPickerViewController from dismissing this navigation controller. Or at least provide control. This is a weak reference to a UIDocumentPickerController that this controller presents
weak var documentPicker: UIDocumentPickerViewController?

2) Override these two functions on the UIViewController that is presenting the UIDocumentPickerViewController

//MARK: Overrides
override public func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
    if self.presentedViewController == nil && self.documentPicker != nil {
        self.documentPicker = nil
    }else{
        super.dismiss(animated: flag, completion: completion)
    }
}

public override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
    if viewControllerToPresent is UIDocumentPickerViewController {
        self.documentPicker = viewControllerToPresent as? UIDocumentPickerViewController
    }
    super.present(viewControllerToPresent, animated: flag, completion: completion)
}

Now the second call from the UIDocumentPickerViewController will not dismiss the presenting UIViewController.

Jon Vogel
  • 5,244
  • 1
  • 39
  • 54
  • Spent forever switching from UIWebView to WKWebView due to this same bug except for image pickers to discover that UIWebViews default to application/x-www-form-urlencoded for Content-Type header and WKWebViews do not. Finally get my shizz working, get happy about it, try out the document selector for shiggles, and find out that the same bug exists here, except it isn't patched (though happens to have last been commented on today! Fix soon?). Slammed my face on the desk and decided to see what workarounds I could find. You're a godsend. – Jake T. Jul 03 '18 at 21:39
3

I had the same problem on Objective-C and iOS11 with WKWebView and solved it using this workaround. You should be able to migrate it to Swift easily :

  • my WKWebView was owned by a view controller directly extending UIViewController
  • inside this view controller add this weak property

    @property (weak, nonatomic) UIDocumentPickerViewController *_Nullable docPickerPtr;

  • inside same view controller override these two methods originally part of the UIViewController base class

    - (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^ __nullable)(void))completion
    {
        if ([viewControllerToPresent isKindOfClass:[UIDocumentPickerViewController class]])
        {
            _docPickerPtr = (UIDocumentPickerViewController*)viewControllerToPresent;
        }
    
        [super presentViewController:viewControllerToPresent animated:flag completion:completion];
    }
    
    - (void)dismissViewControllerAnimated:(BOOL)flag
                               completion:(void (^)(void))completion
    {
        if (_docPickerPtr != nil && self.presentedViewController == nil)
        {
            NSLog(@">>>>>>>>>>>>PREVENT FROM DOING 2nd DISMISS!");
        }
        else
        {    
            [super dismissViewControllerAnimated:flag completion:completion];
        }
    }
    
  • what we do is :

    1. when we're about to display the document picker, save a weak pointer to the UIDocumentPickerViewController
    2. the dismissViewControllerAnimated:completition gets called twice. Once while the presentedViewController is not nil yet to kill the actual document picker, and a 2nd time for unknown reasons when presentedViewController is gone but UIDocumentPickerViewController is still alive. The idea is to prevent this 2nd dismiss propagate to super
dmind
  • 815
  • 1
  • 6
  • 9
  • 1
    Hey thanks for this answer! I see that it's the same concept as the other answer and posted months earlier, just not in the tagged language. I was rolling with Swift as well, but credit where it's due :) – Jake T. Jul 03 '18 at 21:37