Forum Xamarin.iOS
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

WKNavigationDelegate.DidReceiveAuthenticationChallenge crashes on iOS 11

MichaelRumplerMichaelRumpler ATMember ✭✭✭✭✭

I use a WKWebView to connect to a web page with basic authentication. For this purpose I implemented a WKNavigationDelegate and overrode DidReceiveAuthenticationChallenge. This works perfectly on iOS 10.3, but the app crashes on iOS 11. It seems like it crashes when the method DidReceiveAuthenticationChallenge should be called.

I get this exception:

ObjCRuntime.RuntimeException: The ObjectiveC class 'WKNSURLAuthenticationChallenge' could not be registered, it does not seem to derive from any known ObjectiveC class (including NSObject).
at Registrar.DynamicRegistrar.Lookup (System.IntPtr class, System.Boolean throw_on_error) [0x0008c] in /Users/builder/data/lanes/5024/bc9a39fc/source/xamarin-macios/src/ObjCRuntime/DynamicRegistrar.cs:951
at ObjCRuntime.Class.Lookup (System.IntPtr klass, System.Boolean throw_on_error) [0x00000] in /Users/builder/data/lanes/5024/bc9a39fc/source/xamarin-macios/src/ObjCRuntime/Class.cs:121
at ObjCRuntime.Class.Lookup (System.IntPtr klass) [0x00000] in /Users/builder/data/lanes/5024/bc9a39fc/source/xamarin-macios/src/ObjCRuntime/Class.cs:116
at ObjCRuntime.Runtime.GetNSObject (System.IntPtr ptr, System.Type target_type, ObjCRuntime.Runtime+MissingCtorResolution missingCtorResolution, System.Boolean evenInFinalizerQueue, System.Boolean& created) [0x0003d] in /Users/builder/data/lanes/5024/bc9a39fc/source/xamarin-macios/src/ObjCRuntime/Runtime.cs:1154
at ObjCRuntime.Runtime.GetNSObjectWithType (System.IntPtr ptr, System.IntPtr type_ptr, System.Boolean& created) [0x0000c] in /Users/builder/data/lanes/5024/bc9a39fc/source/xamarin-macios/src/ObjCRuntime/Runtime.cs:646
at ObjCRuntime.Runtime.get_nsobject_with_type (System.IntPtr obj, System.IntPtr type, System.Boolean& created, System.Int32& exception_gchandle) [0x00003] in /Users/builder/data/lanes/5024/bc9a39fc/source/xamarin-macios/runtime/Delegates.generated.cs:404

It seems like the internal WKNSURLAuthenticationChallenge is indeed a NSProxy which is no NSObject.
See WKNSURLAuthenticationChallenge, WKObject and NSProxy.

I already opened bug 59279 (with a repro project) for this, but maybe somebody from the community knows how to work around this.

Answers

  • We've had some success by doing the following. Instead of overriding DidReceiveAuthenticationChallenge define a new function with the export attribute:

         [Export("webView:didReceiveAuthenticationChallenge:completionHandler:")]
         public void DidReceiveAuthenticationChallenge(WKWebView webView, NSProxy challenge, [BlockProxy(typeof(AuthenticationChallengeCallbackProxy))] Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
    

    Then use this for NSProxy:

     [Register("NSProxy", true)]
            public class NSProxy : INativeObject
            {
                public NSProxy(IntPtr handle, bool parm)
                {
                    Handle = handle;
                }
    
                public IntPtr Handle
                {
                    get; private set;
                }
            }
    

    And for the BlockProxy:

    [DllImport("/usr/lib/libobjc.dylib")]
            static extern IntPtr _Block_copy(IntPtr ptr);
    
            [DllImport("/usr/lib/libobjc.dylib")]
            static extern void _Block_release(IntPtr ptr);
    
            [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
            internal delegate void ActionCallbackDelegate(IntPtr block, NSUrlSessionAuthChallengeDisposition arg1, IntPtr arg2);
    
            internal class AuthenticationChallengeCallbackProxy
            {
                IntPtr blockPtr;
                ActionCallbackDelegate invoker;
    
                [Preserve(Conditional = true)]
                public unsafe AuthenticationChallengeCallbackProxy(BlockLiteral* block)
                {
                    blockPtr = _Block_copy((IntPtr)block);
                    invoker = block->GetDelegateForBlock<ActionCallbackDelegate>();
                }
    
                [Preserve(Conditional = true)]
                ~AuthenticationChallengeCallbackProxy()
                {
                    _Block_release(blockPtr);
                }
    
                [Preserve(Conditional = true)]
                public unsafe static global::System.Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> Create(IntPtr block)
                {
                    return new AuthenticationChallengeCallbackProxy((BlockLiteral*)block).Invoke;
                }
    
                [Preserve(Conditional = true)]
                unsafe void Invoke(NSUrlSessionAuthChallengeDisposition arg1, NSUrlCredential arg2)
                {
                    invoker(blockPtr, arg1, arg2 == null ? IntPtr.Zero : arg2.Handle);
                }
            }
    

    We have no need to look at the challenge but the callback handler works fine.

  • MichaelRumplerMichaelRumpler ATMember ✭✭✭✭✭

    I do need the challenge (ProtectionSpace and PreviousFailureCount), so I cannot do that.

  • I suspect you could amend the NSProxy declaration to add the additional properties you need with the appropriate export attributes.

  • Looks like an even easier workaround:

    https://bugzilla.xamarin.com/show_bug.cgi?id=59247

Sign In or Register to comment.