diff --git a/cbits-cocoa/AppDelegate.m b/cbits-cocoa/AppDelegate.m
--- a/cbits-cocoa/AppDelegate.m
+++ b/cbits-cocoa/AppDelegate.m
@@ -2,6 +2,9 @@
 #import <Cocoa/Cocoa.h>
 #import <WebKit/WebKit.h>
 
+extern void callIO(HsStablePtr);
+extern bool callWithCStringReturningBool(const char * _Nonnull, HsStablePtr);
+extern void callWithCString(const char * _Nonnull, HsStablePtr);
 extern void callWithWebView(WKWebView *, HsStablePtr);
 
 @interface AppDelegate : NSObject <NSApplicationDelegate>
@@ -11,6 +14,12 @@
 - (instancetype)initApp:(HsStablePtr)handler progName:(NSString *)progName;
 @end
 
+HsStablePtr global_willFinishLaunchingWithOptions = 0;
+HsStablePtr global_didFinishLaunchingWithOptions = 0;
+HsStablePtr global_applicationUniversalLink = 0;
+HsStablePtr global_applicationOpenFile = 0;
+uint64_t global_developerExtrasEnabled = 1;
+
 @implementation AppDelegate
 
 - (void)applicationWillTerminate:(NSNotification *)aNotification {
@@ -31,18 +40,39 @@
     return self;
 }
 
+-(BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
+    bool b = callWithCStringReturningBool([filename UTF8String], global_applicationOpenFile);
+    return b ? YES : NO;
+}
+
 -(void)applicationWillFinishLaunching:(NSNotification *)notification {
     WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
-    [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
+    if (global_developerExtrasEnabled) {
+      [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
+    }
     WKWebView *webView = [[WKWebView alloc] initWithFrame: [_window.contentView frame] configuration:theConfiguration];
     [_window setContentView:webView];
     callWithWebView(webView, _handler);
+    callIO(global_willFinishLaunchingWithOptions);
+    // Listen to URL events
+    NSAppleEventManager *manager = [NSAppleEventManager sharedAppleEventManager];
+    [manager
+      setEventHandler:self
+      andSelector:@selector(applicationUniversalLink:)
+      forEventClass:kInternetEventClass
+      andEventID:kAEGetURL];
 }
 
+- (void)applicationUniversalLink:(NSAppleEventDescriptor *)event {
+  NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+  callWithCString([url UTF8String], global_applicationUniversalLink);
+}
+
 -(void)applicationDidFinishLaunching:(NSNotification *)notification {
     [_window orderFrontRegardless];
     [_window center];
     [NSApp activateIgnoringOtherApps:YES];
+    callIO(global_didFinishLaunchingWithOptions);
 }
 
 -(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
@@ -51,8 +81,33 @@
 
 @end
 
-void runInWKWebView(HsStablePtr handler, const char * _Nonnull progName) {
+void runInWKWebView(HsStablePtr handler,
+                    const char * _Nonnull progName,
+                    HsStablePtr hs_willFinishLaunchingWithOptions,
+                    HsStablePtr hs_didFinishLaunchingWithOptions,
+                    HsStablePtr hs_applicationDidBecomeActive,
+                    HsStablePtr hs_applicationWillResignActive,
+                    HsStablePtr hs_applicationDidEnterBackground,
+                    HsStablePtr hs_applicationWillEnterForeground,
+                    HsStablePtr hs_applicationWillTerminate,
+                    HsStablePtr hs_applicationSignificantTimeChange,
+                    HsStablePtr hs_applicationUniversalLink,
+                    const uint64_t hs_requestAuthorizationWithOptions,
+                    const uint64_t hs_requestAuthorizationOptionBadge,
+                    const uint64_t hs_requestAuthorizationOptionSound,
+                    const uint64_t hs_requestAuthorizationOptionAlert,
+                    const uint64_t hs_requestAuthorizationOptionCarPlay,
+                    const uint64_t hs_registerForRemoteNotifications,
+                    HsStablePtr hs_didRegisterForRemoteNotificationsWithDeviceToken,
+                    HsStablePtr hs_didFailToRegisterForRemoteNotificationsWithError,
+                    const uint64_t hs_developerExtrasEnabled,
+                    HsStablePtr hs_applicationOpenFile) {
     @autoreleasepool {
+        global_willFinishLaunchingWithOptions = hs_willFinishLaunchingWithOptions;
+        global_didFinishLaunchingWithOptions = hs_didFinishLaunchingWithOptions;
+        global_applicationUniversalLink = hs_applicationUniversalLink;
+        global_applicationOpenFile = hs_applicationOpenFile;
+        global_developerExtrasEnabled = hs_developerExtrasEnabled;
         NSApplication *application = [NSApplication sharedApplication];
         AppDelegate *appDelegate = [[AppDelegate alloc] initApp:handler progName:[NSString stringWithCString:progName encoding:NSUTF8StringEncoding]];
         [application setDelegate:appDelegate];
diff --git a/cbits-uikit/AppDelegate.m b/cbits-uikit/AppDelegate.m
--- a/cbits-uikit/AppDelegate.m
+++ b/cbits-uikit/AppDelegate.m
@@ -159,7 +159,8 @@
                     const uint64_t hs_requestAuthorizationOptionCarPlay,
                     const uint64_t hs_registerForRemoteNotifications,
                     HsStablePtr hs_didRegisterForRemoteNotificationsWithDeviceToken,
-                    HsStablePtr hs_didFailToRegisterForRemoteNotificationsWithError) {
+                    HsStablePtr hs_didFailToRegisterForRemoteNotificationsWithError,
+                    const uint64_t hs_developerExtrasEnabled) {
     @autoreleasepool {
         globalHandler = handler;
         global_willFinishLaunchingWithOptions = hs_willFinishLaunchingWithOptions;
diff --git a/jsaddle-wkwebview.cabal b/jsaddle-wkwebview.cabal
--- a/jsaddle-wkwebview.cabal
+++ b/jsaddle-wkwebview.cabal
@@ -1,5 +1,5 @@
 name: jsaddle-wkwebview
-version: 0.9.6.0
+version: 0.9.7.0
 cabal-version: >=1.10
 build-type: Simple
 license: MIT
diff --git a/src-ghc/Language/Javascript/JSaddle/WKWebView.hs b/src-ghc/Language/Javascript/JSaddle/WKWebView.hs
--- a/src-ghc/Language/Javascript/JSaddle/WKWebView.hs
+++ b/src-ghc/Language/Javascript/JSaddle/WKWebView.hs
@@ -24,6 +24,7 @@
        (jsaddleMain, jsaddleMainHTMLWithBaseURL, jsaddleMainFile, WKWebView(..), mainBundleResourcePath)
 import System.Environment (getProgName)
 import Foreign.C.String (CString, withCString)
+import Foreign.C.Types (CBool)
 import Foreign.StablePtr (StablePtr, newStablePtr)
 import Language.Javascript.JSaddle (JSM)
 
@@ -47,6 +48,8 @@
     -> Word64 -- registerForRemoteNotifications
     -> StablePtr (CString -> IO ()) -- didRegisterForRemoteNotificationsWithDeviceToken
     -> StablePtr (CString -> IO ()) -- didFailToRegisterForRemoteNotificationsWithError
+    -> Word64 -- developerExtrasEnabled
+    -> StablePtr (CString -> IO CBool) -- applicationOpenFile
     -> IO ()
 
 data AppDelegateConfig = AppDelegateConfig
@@ -59,7 +62,20 @@
     , _appDelegateConfig_applicationWillTerminate :: IO ()
     , _appDelegateConfig_applicationSignificantTimeChange :: IO ()
     , _appDelegateConfig_applicationUniversalLink :: CString -> IO ()
+    -- ^ Called when the app is launched from a URL. 'CString' contains the URL.
+    --
+    -- The Core Foundation plist key CFBundleURLTypes controls which URL schemes
+    -- the app should respond to.
     , _appDelegateConfig_appDelegateNotificationConfig :: AppDelegateNotificationConfig
+    , _appDelegateConfig_developerExtrasEnabled :: Bool
+    -- ^ Allow devtools in the app. Defaults to 'True'
+    , _appDelegateConfig_applicationOpenFile :: CString -> IO Bool
+    -- ^ Called when the app is launched by opening a file. 'CString' contains
+    -- the file path. Return value is 'True' if the file was successfully
+    -- opened. Defaults to ignoring the file and returning 'False'.
+    --
+    -- The Core Foundation plist key CFBundleDocumentTypes controls which file
+    -- types should be associated with the app.
     }
 
 instance Default AppDelegateConfig where
@@ -74,6 +90,8 @@
         , _appDelegateConfig_applicationSignificantTimeChange = return ()
         , _appDelegateConfig_applicationUniversalLink = \_ -> return ()
         , _appDelegateConfig_appDelegateNotificationConfig = def
+        , _appDelegateConfig_developerExtrasEnabled = True
+        , _appDelegateConfig_applicationOpenFile = \_ -> return False
         }
 
 data AuthorizationOption = AuthorizationOption_Badge
@@ -144,6 +162,7 @@
     applicationWillTerminate <- newStablePtr $ _appDelegateConfig_applicationWillTerminate cfg
     applicationSignificantTimeChange <- newStablePtr $ _appDelegateConfig_applicationSignificantTimeChange cfg
     applicationUniversalLink <- newStablePtr $ _appDelegateConfig_applicationUniversalLink cfg
+    applicationOpenFile <- newStablePtr $ fmap fromBool . _appDelegateConfig_applicationOpenFile cfg
 
     -- AppDelegate notification configuration
     let ncfg = _appDelegateConfig_appDelegateNotificationConfig cfg
@@ -174,3 +193,5 @@
       (fromBool registerForRemoteNotifications)
       didRegisterForRemoteNotificationsWithDeviceToken
       didFailToRegisterForRemoteNotificationsWithError
+      (fromBool $ _appDelegateConfig_developerExtrasEnabled cfg)
+      applicationOpenFile
diff --git a/src/Language/Javascript/JSaddle/WKWebView/Internal.hs b/src/Language/Javascript/JSaddle/WKWebView/Internal.hs
--- a/src/Language/Javascript/JSaddle/WKWebView/Internal.hs
+++ b/src/Language/Javascript/JSaddle/WKWebView/Internal.hs
@@ -20,6 +20,7 @@
 import Data.Text.Encoding (encodeUtf8)
 
 import Foreign.C.String (CString)
+import Foreign.C.Types (CBool(..))
 import Foreign.Ptr (Ptr, nullPtr)
 import Foreign.StablePtr (StablePtr, newStablePtr, deRefStablePtr)
 
@@ -37,6 +38,7 @@
 foreign export ccall jsaddleSyncResult :: StablePtr (Results -> IO Batch) -> JSaddleHandler -> CString -> IO ()
 foreign export ccall callWithWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
 foreign export ccall callWithCString :: CString -> StablePtr (CString -> IO ()) -> IO ()
+foreign export ccall callWithCStringReturningBool :: CString -> StablePtr (CString -> IO CBool) -> IO CBool
 foreign export ccall callIO :: StablePtr (IO ()) -> IO ()
 foreign import ccall addJSaddleHandler :: WKWebView -> StablePtr (IO ()) -> StablePtr (Results -> IO ()) -> StablePtr (Results -> IO Batch) -> IO ()
 foreign import ccall loadHTMLStringWithBaseURL :: WKWebView -> CString -> CString -> IO ()
@@ -123,6 +125,11 @@
 
 callWithCString :: CString -> StablePtr (CString -> IO ()) -> IO ()
 callWithCString c fptr = do
+    f <- deRefStablePtr fptr
+    f c
+
+callWithCStringReturningBool :: CString -> StablePtr (CString -> IO CBool) -> IO CBool
+callWithCStringReturningBool c fptr = do
     f <- deRefStablePtr fptr
     f c
 
