jsaddle-wkwebview 0.8.3.2 → 0.9.0.0
raw patch · 7 files changed
+352/−43 lines, 7 filesdep +containersdep +data-defaultdep ~jsaddle
Dependencies added: containers, data-default
Dependency ranges changed: jsaddle
Files
- cbits-cocoa/AppDelegate.m +3/−3
- cbits-uikit/AppDelegate.m +122/−2
- cbits-uikit/ViewController.m +2/−2
- cbits/WKWebView.m +31/−8
- jsaddle-wkwebview.cabal +16/−8
- src-ghc/Language/Javascript/JSaddle/WKWebView.hs +138/−9
- src/Language/Javascript/JSaddle/WKWebView/Internal.hs +40/−11
cbits-cocoa/AppDelegate.m view
@@ -2,7 +2,7 @@ #import <Cocoa/Cocoa.h> #import <WebKit/WebKit.h> -extern void withWebView(WKWebView *, HsStablePtr);+extern void callWithWebView(WKWebView *, HsStablePtr); @interface AppDelegate : NSObject <NSApplicationDelegate> @property (nonatomic, assign) IBOutlet NSWindow *window;@@ -34,9 +34,9 @@ -(void)applicationWillFinishLaunching:(NSNotification *)notification { WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init]; [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];- WKWebView *webView = [[WKWebView alloc] initWithFrame:_window.contentView.frame configuration:theConfiguration];+ WKWebView *webView = [[WKWebView alloc] initWithFrame: [_window.contentView frame] configuration:theConfiguration]; [_window setContentView:webView];- withWebView(webView, _handler);+ callWithWebView(webView, _handler); } -(void)applicationDidFinishLaunching:(NSNotification *)notification {
cbits-uikit/AppDelegate.m view
@@ -1,55 +1,175 @@ #import "AppDelegate.h" #import "ViewController.h"+#import <UserNotifications/UserNotifications.h>+#import <stdint.h> -@interface AppDelegate ()+extern void callIO(HsStablePtr);+extern void callWithCString(const char * _Nonnull, HsStablePtr); +@interface AppDelegate () @end HsStablePtr globalHandler = 0;+HsStablePtr global_willFinishLaunchingWithOptions = 0;+HsStablePtr global_didFinishLaunchingWithOptions = 0;+HsStablePtr global_applicationDidBecomeActive = 0;+HsStablePtr global_applicationWillResignActive = 0;+HsStablePtr global_applicationDidEnterBackground = 0;+HsStablePtr global_applicationWillEnterForeground = 0;+HsStablePtr global_applicationWillTerminate = 0;+HsStablePtr global_applicationSignificantTimeChange = 0;+HsStablePtr global_applicationUniversalLink = 0;+uint64_t global_requestAuthorizationWithOptions = 0;+uint64_t global_requestAuthorizationOptionBadge = 0;+uint64_t global_requestAuthorizationOptionSound = 0;+uint64_t global_requestAuthorizationOptionAlert = 0;+uint64_t global_requestAuthorizationOptionCarPlay = 0;+uint64_t global_registerForRemoteNotifications = 0;+HsStablePtr global_didRegisterForRemoteNotificationsWithDeviceToken = 0;+HsStablePtr global_didFailToRegisterForRemoteNotificationsWithError = 0; @implementation AppDelegate +- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {+ // Tells the delegate that the launch process has begun but that state restoration has not yet occurred.+ callIO(global_willFinishLaunchingWithOptions);+ return YES;+}+ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {+ // Tells the delegate that the launch process is almost done and the app is almost ready to run. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.rootViewController = [[ViewController alloc] initWithHandler:globalHandler]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible];+ UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];+ center.delegate = self;+ if (global_requestAuthorizationWithOptions) {+ UNAuthorizationOptions options = (UNAuthorizationOptions)0;+ if (global_requestAuthorizationOptionBadge) {+ options = options | UNAuthorizationOptionBadge;+ }+ if (global_requestAuthorizationOptionSound) {+ options = options | UNAuthorizationOptionSound;+ }+ if (global_requestAuthorizationOptionAlert) {+ options = options | UNAuthorizationOptionAlert;+ }+ if (global_requestAuthorizationOptionCarPlay) {+ options = options | UNAuthorizationOptionCarPlay;+ }+ [center requestAuthorizationWithOptions:(options)+ completionHandler:^(BOOL granted, NSError * _Nullable error) {+ // Handler used to alter application behavior based on types of notifications authorized+ }];+ if (global_registerForRemoteNotifications) {+ [application registerForRemoteNotifications];+ }+ }+ callIO(global_didFinishLaunchingWithOptions); return YES; } - (void)applicationWillResignActive:(UIApplication *)application { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.+ callIO(global_applicationWillResignActive); } - (void)applicationDidEnterBackground:(UIApplication *)application { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.+ callIO(global_applicationDidEnterBackground); } - (void)applicationWillEnterForeground:(UIApplication *)application { // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.+ callIO(global_applicationWillEnterForeground); } - (void)applicationDidBecomeActive:(UIApplication *)application { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.+ callIO(global_applicationDidBecomeActive); } - (void)applicationWillTerminate:(UIApplication *)application { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.+ callIO(global_applicationWillTerminate); } +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {+ // Tells the delegate that the app successfully registered with Apple Push Notification service (APNs).+ NSString *deviceTokenString = [deviceToken base64EncodedStringWithOptions: 0];+ callWithCString([deviceTokenString UTF8String], global_didRegisterForRemoteNotificationsWithDeviceToken);+}++- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {+ // Sent to the delegate when Apple Push Notification service cannot successfully complete the registration process.+ NSString *errorString = [error localizedDescription];+ callWithCString([errorString UTF8String], global_didFailToRegisterForRemoteNotificationsWithError);+}++- (void)applicationSignificantTimeChange:(UIApplication *)application {+ // Tells the delegate when there is a significant change in the time.+ callIO(global_applicationSignificantTimeChange);+}++- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {+ // TODO: Reroute universal links when they're clicked in-app.+ // https://developer.apple.com/reference/webkit/wknavigationdelegate/1455643-webview?language=objc+ if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb] && userActivity.webpageURL) {+ callWithCString([userActivity.webpageURL.absoluteString UTF8String], global_applicationUniversalLink);+ return YES;+ }+ return NO;+}+ @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) { @autoreleasepool { globalHandler = handler;+ global_willFinishLaunchingWithOptions = hs_willFinishLaunchingWithOptions;+ global_didFinishLaunchingWithOptions = hs_didFinishLaunchingWithOptions;+ global_applicationDidBecomeActive = hs_applicationDidBecomeActive;+ global_applicationWillResignActive = hs_applicationWillResignActive;+ global_applicationDidEnterBackground = hs_applicationDidEnterBackground;+ global_applicationWillEnterForeground = hs_applicationWillEnterForeground;+ global_applicationWillTerminate = hs_applicationWillTerminate;+ global_applicationSignificantTimeChange = hs_applicationSignificantTimeChange;+ global_applicationUniversalLink = hs_applicationUniversalLink;+ global_requestAuthorizationWithOptions = hs_requestAuthorizationWithOptions;+ global_requestAuthorizationOptionBadge = hs_requestAuthorizationOptionBadge;+ global_requestAuthorizationOptionSound = hs_requestAuthorizationOptionSound;+ global_requestAuthorizationOptionAlert = hs_requestAuthorizationOptionAlert;+ global_requestAuthorizationOptionCarPlay = hs_requestAuthorizationOptionCarPlay;+ global_registerForRemoteNotifications = hs_registerForRemoteNotifications;+ global_didRegisterForRemoteNotificationsWithDeviceToken = hs_didRegisterForRemoteNotificationsWithDeviceToken;+ global_didFailToRegisterForRemoteNotificationsWithError = hs_didFailToRegisterForRemoteNotificationsWithError; const char * _Nonnull argv [] = {"", 0}; UIApplicationMain(0, argv, nil, NSStringFromClass([AppDelegate class])); }
cbits-uikit/ViewController.m view
@@ -1,6 +1,6 @@ #import "ViewController.h" -extern void withWebView(WKWebView *, HsStablePtr);+extern void callWithWebView(WKWebView *, HsStablePtr); @interface ViewController () @@ -27,7 +27,7 @@ - (void)viewDidLoad { [super viewDidLoad];- withWebView(_webView, _handler);+ callWithWebView(_webView, _handler); }
cbits/WKWebView.m view
@@ -4,26 +4,30 @@ BOOL openApp(NSURL * url); -extern void jsaddleStart(HsStablePtr);-extern void jsaddleResult(HsStablePtr, const char * _Nonnull result);--@interface JSaddleHandler : NSObject<WKNavigationDelegate, WKScriptMessageHandler>+@interface JSaddleHandler : NSObject<WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate> @property (nonatomic, assign) HsStablePtr startHandler; @property (nonatomic, assign) HsStablePtr resultHandler;+@property (nonatomic, assign) HsStablePtr syncHandler;+@property (nonatomic, assign) void (^completionHandler)(NSString *result); -- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler;+- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler syncHandler:(HsStablePtr)syncHandler; @end +extern void jsaddleStart(HsStablePtr);+extern void jsaddleResult(HsStablePtr, const char * _Nonnull result);+extern void jsaddleSyncResult(HsStablePtr, JSaddleHandler *, const char * _Nonnull result);+ @implementation JSaddleHandler -- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler+- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler syncHandler:(HsStablePtr)syncHandler { self = [super init]; if (self) { _startHandler = startHandler; _resultHandler = resultHandler;+ _syncHandler = syncHandler; } return self; }@@ -39,6 +43,18 @@ jsaddleResult(self.resultHandler, [s UTF8String]); } +- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt+ defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *result))completionHandler+{+ if([prompt isEqualToString:@"JSaddleSync"]) {+ _completionHandler = completionHandler;+ jsaddleSyncResult(self.syncHandler, self, [defaultText UTF8String]);+ }+ else {+ // TODO decide what if anything should go here+ }+}+ - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { NSURL *url = navigationAction.request.URL;@@ -55,10 +71,11 @@ @end -void addJSaddleHandler(WKWebView *webView, HsStablePtr startHandler, HsStablePtr resultHandler) {- JSaddleHandler * handler = [[JSaddleHandler alloc] initHandler:startHandler resultHandler:resultHandler];+void addJSaddleHandler(WKWebView *webView, HsStablePtr startHandler, HsStablePtr resultHandler, HsStablePtr syncHandler) {+ JSaddleHandler * handler = [[JSaddleHandler alloc] initHandler:startHandler resultHandler:resultHandler syncHandler:syncHandler]; [[[webView configuration] userContentController] addScriptMessageHandler:handler name:@"jsaddle"]; webView.navigationDelegate = handler;+ webView.UIDelegate = handler; } void evaluateJavaScript(WKWebView *webView, const char * _Nonnull js) {@@ -67,6 +84,12 @@ [webView evaluateJavaScript:jsString completionHandler:NULL]; [jsString release]; });+}++void completeSync(JSaddleHandler *handler, const char * _Nonnull s) {+ NSString *string = [[NSString alloc] initWithCString:s encoding:NSUTF8StringEncoding];+ handler.completionHandler(string);+ handler.completionHandler = NULL; } void loadHTMLString(WKWebView *webView, const char * _Nonnull html) {
jsaddle-wkwebview.cabal view
@@ -1,5 +1,5 @@ name: jsaddle-wkwebview-version: 0.8.3.2+version: 0.9.0.0 cabal-version: >=1.10 build-type: Simple license: MIT@@ -14,6 +14,10 @@ category: Web, Javascript author: Hamish Mackenzie +flag include-app-delegate+ description: Include default AppDelegate C sources.+ default: True+ source-repository head type: git location: https://github.com/ghcjs/jsaddle@@ -26,7 +30,9 @@ aeson >=0.8.0.2 && <1.3, base <5, bytestring >=0.10.6.0 && <0.11,- jsaddle >= 0.8.0.0 && <0.9+ jsaddle >= 0.9.0.0 && <0.10,+ data-default,+ containers default-language: Haskell2010 hs-source-dirs: src frameworks: Foundation, WebKit@@ -38,11 +44,13 @@ else hs-source-dirs: src-ghc if os(ios)- frameworks: UIKit- c-sources:- cbits-uikit/AppDelegate.m- cbits-uikit/ViewController.m+ frameworks: UIKit, UserNotifications+ if flag(include-app-delegate)+ c-sources:+ cbits-uikit/AppDelegate.m+ cbits-uikit/ViewController.m else frameworks: Cocoa- c-sources:- cbits-cocoa/AppDelegate.m+ if flag(include-app-delegate)+ c-sources:+ cbits-cocoa/AppDelegate.m
src-ghc/Language/Javascript/JSaddle/WKWebView.hs view
@@ -3,32 +3,161 @@ , jsaddleMainFile , WKWebView(..) , run+ , runWithAppConfig , runFile , mainBundleResourcePath+ , AppDelegateConfig (..)+ , AppDelegateNotificationConfig (..)+ , AuthorizationOption (..) ) where import Data.ByteString (ByteString)+import Data.Default (Default, def)+import Data.Set (Set)+import qualified Data.Set as Set+import Data.Word+import Foreign.Marshal.Utils (fromBool) import Language.Javascript.JSaddle.WKWebView.Internal (jsaddleMain, jsaddleMainFile, WKWebView(..), mainBundleResourcePath) import System.Environment (getProgName) import Foreign.C.String (CString, withCString) import Foreign.StablePtr (StablePtr, newStablePtr) import Language.Javascript.JSaddle (JSM) -foreign import ccall runInWKWebView :: StablePtr (WKWebView -> IO ()) -> CString -> IO ()+foreign import ccall runInWKWebView+ :: StablePtr (WKWebView -> IO ())+ -> CString+ -> StablePtr (IO ()) -- willFinishLaunchingWithOptions+ -> StablePtr (IO ()) -- didFinishLaunchingWithOptions+ -> StablePtr (IO ()) -- applicationDidBecomeActive+ -> StablePtr (IO ()) -- applicationWillResignActive+ -> StablePtr (IO ()) -- applicationDidEnterBackground+ -> StablePtr (IO ()) -- applicationWillEnterForeground+ -> StablePtr (IO ()) -- applicationWillTerminate+ -> StablePtr (IO ()) -- applicationSignificantTimeChange+ -> StablePtr (CString -> IO ()) -- applicationUniversalLink+ -> Word64 -- whether to run requestAuthorizationWithOptions+ -> Word64 -- Ask for Badge authorization+ -> Word64 -- Ask for Sound authorization+ -> Word64 -- Ask for Alert authorization+ -> Word64 -- Ask for CarPlay authorization+ -> Word64 -- registerForRemoteNotifications+ -> StablePtr (CString -> IO ()) -- didRegisterForRemoteNotificationsWithDeviceToken+ -> StablePtr (CString -> IO ()) -- didFailToRegisterForRemoteNotificationsWithError+ -> IO () +data AppDelegateConfig = AppDelegateConfig+ { _appDelegateConfig_willFinishLaunchingWithOptions :: IO ()+ , _appDelegateConfig_didFinishLaunchingWithOptions :: IO ()+ , _appDelegateConfig_applicationDidBecomeActive :: IO ()+ , _appDelegateConfig_applicationWillResignActive :: IO ()+ , _appDelegateConfig_applicationDidEnterBackground :: IO ()+ , _appDelegateConfig_applicationWillEnterForeground :: IO ()+ , _appDelegateConfig_applicationWillTerminate :: IO ()+ , _appDelegateConfig_applicationSignificantTimeChange :: IO ()+ , _appDelegateConfig_applicationUniversalLink :: CString -> IO ()+ , _appDelegateConfig_appDelegateNotificationConfig :: AppDelegateNotificationConfig+ }++instance Default AppDelegateConfig where+ def = AppDelegateConfig+ { _appDelegateConfig_willFinishLaunchingWithOptions = return ()+ , _appDelegateConfig_didFinishLaunchingWithOptions = return ()+ , _appDelegateConfig_applicationDidBecomeActive = return ()+ , _appDelegateConfig_applicationWillResignActive = return ()+ , _appDelegateConfig_applicationDidEnterBackground = return ()+ , _appDelegateConfig_applicationWillEnterForeground = return ()+ , _appDelegateConfig_applicationWillTerminate = return ()+ , _appDelegateConfig_applicationSignificantTimeChange = return ()+ , _appDelegateConfig_applicationUniversalLink = \_ -> return ()+ , _appDelegateConfig_appDelegateNotificationConfig = def+ }++data AuthorizationOption = AuthorizationOption_Badge+ | AuthorizationOption_Sound+ | AuthorizationOption_Alert+ | AuthorizationOption_CarPlay+ deriving (Show, Read, Eq, Ord)++data AppDelegateNotificationConfig = AppDelegateNotificationConfig+ { _appDelegateNotificationConfig_requestAuthorizationWithOptions :: Maybe (Set AuthorizationOption)+ , _appDelegateNotificationConfig_registerForRemoteNotifications :: Bool+ , _appDelegateNotificationConfig_didRegisterForRemoteNotificationsWithDeviceToken :: CString -> IO ()+ , _appDelegateNotificationConfig_didFailToRegisterForRemoteNotificationsWithError :: CString -> IO ()+ }++instance Default AppDelegateNotificationConfig where+ def = AppDelegateNotificationConfig+ { _appDelegateNotificationConfig_requestAuthorizationWithOptions = Nothing+ , _appDelegateNotificationConfig_registerForRemoteNotifications = False+ , _appDelegateNotificationConfig_didRegisterForRemoteNotificationsWithDeviceToken = \_ -> return ()+ , _appDelegateNotificationConfig_didFailToRegisterForRemoteNotificationsWithError = \_ -> return ()+ }++ -- | Run JSaddle in a WKWebView run :: JSM () -> IO ()-run f = do- handler <- newStablePtr (jsaddleMain f)- progName <- getProgName- withCString progName $ runInWKWebView handler+run = run' Nothing def +-- | Run JSaddle in a WKWebView+runWithAppConfig :: AppDelegateConfig -> JSM () -> IO ()+runWithAppConfig = run' Nothing+ -- | Run JSaddle in a WKWebView first loading the specified file -- from the mainBundle (relative to the resourcePath). runFile :: ByteString -- ^ The file to navigate to. -> ByteString -- ^ The path to allow read access to.- -> JSM () -> IO ()-runFile url allowing f = do- handler <- newStablePtr (jsaddleMainFile url allowing f)+ -> AppDelegateConfig+ -> JSM ()+ -> IO ()+runFile url allowing = run' $ Just (url, allowing)++run' :: Maybe (ByteString, ByteString)+ -> AppDelegateConfig+ -> JSM ()+ -> IO ()+run' mUrl cfg f = do+ handler <- case mUrl of+ Just (url, allowing) -> newStablePtr (jsaddleMainFile url allowing f)+ Nothing -> newStablePtr (jsaddleMain f) progName <- getProgName- withCString progName $ runInWKWebView handler++ -- AppDelegate callbacks+ willFinishLaunchingWithOptions <- newStablePtr $ _appDelegateConfig_willFinishLaunchingWithOptions cfg+ didFinishLaunchingWithOptions <- newStablePtr $ _appDelegateConfig_didFinishLaunchingWithOptions cfg+ applicationDidBecomeActive <- newStablePtr $ _appDelegateConfig_applicationDidBecomeActive cfg+ applicationWillResignActive <- newStablePtr $ _appDelegateConfig_applicationWillResignActive cfg+ applicationDidEnterBackground <- newStablePtr $ _appDelegateConfig_applicationDidEnterBackground cfg+ applicationWillEnterForeground <- newStablePtr $ _appDelegateConfig_applicationWillEnterForeground cfg+ applicationWillTerminate <- newStablePtr $ _appDelegateConfig_applicationWillTerminate cfg+ applicationSignificantTimeChange <- newStablePtr $ _appDelegateConfig_applicationSignificantTimeChange cfg+ applicationUniversalLink <- newStablePtr $ _appDelegateConfig_applicationUniversalLink cfg++ -- AppDelegate notification configuration+ let ncfg = _appDelegateConfig_appDelegateNotificationConfig cfg+ (requestAuthorizationWithOptions, authorizationOptions) =+ case _appDelegateNotificationConfig_requestAuthorizationWithOptions ncfg of+ Nothing -> (False, Set.empty)+ Just opts -> (True, opts)+ registerForRemoteNotifications = _appDelegateNotificationConfig_registerForRemoteNotifications ncfg+ didRegisterForRemoteNotificationsWithDeviceToken <- newStablePtr $ _appDelegateNotificationConfig_didRegisterForRemoteNotificationsWithDeviceToken ncfg+ didFailToRegisterForRemoteNotificationsWithError <- newStablePtr $ _appDelegateNotificationConfig_didFailToRegisterForRemoteNotificationsWithError ncfg+ withCString progName $ \pn -> runInWKWebView+ handler+ pn+ willFinishLaunchingWithOptions+ didFinishLaunchingWithOptions+ applicationDidBecomeActive+ applicationWillResignActive+ applicationDidEnterBackground+ applicationWillEnterForeground+ applicationWillTerminate+ applicationSignificantTimeChange+ applicationUniversalLink+ (fromBool requestAuthorizationWithOptions)+ (fromBool $ Set.member AuthorizationOption_Badge authorizationOptions)+ (fromBool $ Set.member AuthorizationOption_Sound authorizationOptions)+ (fromBool $ Set.member AuthorizationOption_Alert authorizationOptions)+ (fromBool $ Set.member AuthorizationOption_CarPlay authorizationOptions)+ (fromBool registerForRemoteNotifications)+ didRegisterForRemoteNotificationsWithDeviceToken+ didFailToRegisterForRemoteNotificationsWithError
src/Language/Javascript/JSaddle/WKWebView/Internal.hs view
@@ -20,24 +20,29 @@ import Foreign.Ptr (Ptr, nullPtr) import Foreign.StablePtr (StablePtr, newStablePtr, deRefStablePtr) -import Language.Javascript.JSaddle (Results, JSM)+import Language.Javascript.JSaddle (Results, Batch, JSM) import Language.Javascript.JSaddle.Run (runJavaScript) import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers) newtype WKWebView = WKWebView (Ptr WKWebView)+newtype JSaddleHandler = JSaddleHandler (Ptr JSaddleHandler) foreign export ccall jsaddleStart :: StablePtr (IO ()) -> IO () foreign export ccall jsaddleResult :: StablePtr (Results -> IO ()) -> CString -> IO ()-foreign export ccall withWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()-foreign import ccall addJSaddleHandler :: WKWebView -> StablePtr (IO ()) -> StablePtr (Results -> IO ()) -> IO ()+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 callIO :: StablePtr (IO ()) -> IO ()+foreign import ccall addJSaddleHandler :: WKWebView -> StablePtr (IO ()) -> StablePtr (Results -> IO ()) -> StablePtr (Results -> IO Batch) -> IO () foreign import ccall loadHTMLString :: WKWebView -> CString -> IO () foreign import ccall loadBundleFile :: WKWebView -> CString -> CString -> IO () foreign import ccall evaluateJavaScript :: WKWebView -> CString -> IO ()+foreign import ccall completeSync :: JSaddleHandler -> CString -> IO () foreign import ccall mainBundleResourcePathC :: IO CString -- | Run JSaddle in WKWebView jsaddleMain :: JSM () -> WKWebView -> IO ()-jsaddleMain f webView = do+jsaddleMain f webView = jsaddleMain' f webView $ useAsCString (toStrict indexHtml) $ loadHTMLString webView @@ -46,7 +51,7 @@ jsaddleMainFile :: BS.ByteString -- ^ The file to navigate to. -> BS.ByteString -- ^ The path to allow read access to. -> JSM () -> WKWebView -> IO ()-jsaddleMainFile url allowing f webView = do+jsaddleMainFile url allowing f webView = jsaddleMain' f webView $ useAsCString url $ \u -> useAsCString allowing $ \a ->@@ -56,14 +61,15 @@ jsaddleMain' f webView loadHtml = do ready <- newEmptyMVar - (processResult, start) <- runJavaScript (\batch ->+ (processResult, syncResult, start) <- runJavaScript (\batch -> useAsCString (toStrict $ "runJSaddleBatch(" <> encode batch <> ");") $ evaluateJavaScript webView) f startHandler <- newStablePtr (putMVar ready ()) resultHandler <- newStablePtr processResult- addJSaddleHandler webView startHandler resultHandler+ syncResultHandler <- newStablePtr syncResult+ addJSaddleHandler webView startHandler resultHandler syncResultHandler loadHtml void . forkOS $ do takeMVar ready@@ -77,20 +83,42 @@ processResult <- deRefStablePtr ptrHandler result <- packCString s case decode (fromStrict result) of- Nothing -> error $ "jsaddle WebSocket decode failed : " <> show result+ Nothing -> error $ "jsaddle Results decode failed : " <> show result Just r -> processResult r -withWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()-withWebView webView ptrF = do+jsaddleSyncResult :: StablePtr (Results -> IO Batch) -> JSaddleHandler -> CString -> IO ()+jsaddleSyncResult ptrHandler jsaddleHandler s = do+ syncProcessResult <- deRefStablePtr ptrHandler+ result <- packCString s+ case decode (fromStrict result) of+ Nothing -> error $ "jsaddle Results decode failed : " <> show result+ Just r -> do -- void . forkIO $ do+ batch <- syncProcessResult r+ useAsCString (toStrict $ encode batch) $+ completeSync jsaddleHandler++callWithWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()+callWithWebView webView ptrF = do f <- deRefStablePtr ptrF f webView +callWithCString :: CString -> StablePtr (CString -> IO ()) -> IO ()+callWithCString c fptr = do+ f <- deRefStablePtr fptr+ f c++callIO :: StablePtr (IO ()) -> IO ()+callIO ptr = do+ f <- deRefStablePtr ptr+ f+ jsaddleJs :: ByteString jsaddleJs = ghcjsHelpers <> "\ \runJSaddleBatch = (function() {\n\ \ " <> initState <> "\n\ \ return function(batch) {\n\- \ " <> runBatch (\a -> "window.webkit.messageHandlers.jsaddle.postMessage(JSON.stringify(" <> a <> "));") <> "\+ \ " <> runBatch (\a -> "window.webkit.messageHandlers.jsaddle.postMessage(JSON.stringify(" <> a <> "));\n")+ (Just (\a -> "JSON.parse(window.prompt(\"JSaddleSync\", JSON.stringify(" <> a <> ")))")) <> "\ \ };\n\ \})();\n\ \"@@ -112,3 +140,4 @@ if bs == nullPtr then return Nothing else Just <$> packCString bs+