diff --git a/cbits-cocoa/AppDelegate.m b/cbits-cocoa/AppDelegate.m
new file mode 100644
--- /dev/null
+++ b/cbits-cocoa/AppDelegate.m
@@ -0,0 +1,62 @@
+#include "HsFFI.h"
+#import <Cocoa/Cocoa.h>
+#import <WebKit/WebKit.h>
+
+extern void withWebView(WKWebView *, HsStablePtr);
+
+@interface AppDelegate : NSObject <NSApplicationDelegate>
+@property (nonatomic, assign) IBOutlet NSWindow *window;
+@property (nonatomic, assign) HsStablePtr handler;
+
+- (instancetype)initApp:(HsStablePtr)handler progName:(NSString *)progName;
+@end
+
+@implementation AppDelegate
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+    // Insert code here to tear down your application
+}
+
+-(id)initApp:(HsStablePtr)handler progName:(NSString *)progName {
+    self = [super init];
+    if (self) {
+        _handler = handler;
+        NSRect contentSize = NSMakeRect(0.0, 500.0, 1000.0, 700.0);
+        NSUInteger windowStyleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
+        _window = [[NSWindow alloc] initWithContentRect:contentSize styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:YES];
+        _window.backgroundColor = [NSColor whiteColor];
+        _window.title = progName;
+    }
+    return self;
+}
+
+-(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];
+    [_window setContentView:webView];
+    withWebView(webView, _handler);
+}
+
+-(void)applicationDidFinishLaunching:(NSNotification *)notification {
+    [_window orderFrontRegardless];
+    [_window center];
+    [NSApp activateIgnoringOtherApps:YES];
+}
+
+-(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
+    return YES;
+}
+
+@end
+
+void runInWKWebView(HsStablePtr handler, const char * _Nonnull progName) {
+    @autoreleasepool {
+        NSApplication *application = [NSApplication sharedApplication];
+        AppDelegate *appDelegate = [[AppDelegate alloc] initApp:handler progName:[NSString stringWithCString:progName encoding:NSUTF8StringEncoding]];
+        [application setDelegate:appDelegate];
+        const char * _Nonnull argv [] =  {"", 0};
+        NSApplicationMain(0, argv);
+    }
+}
+
diff --git a/cbits-uikit/AppDelegate.m b/cbits-uikit/AppDelegate.m
new file mode 100644
--- /dev/null
+++ b/cbits-uikit/AppDelegate.m
@@ -0,0 +1,57 @@
+#import "AppDelegate.h"
+#import "ViewController.h"
+
+@interface AppDelegate ()
+
+
+@end
+
+HsStablePtr globalHandler = 0;
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+    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];
+    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.
+}
+
+
+- (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.
+}
+
+
+- (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.
+}
+
+
+- (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.
+}
+
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+}
+
+@end
+
+void runInWKWebView(HsStablePtr handler, const char * _Nonnull progName) {
+    @autoreleasepool {
+        globalHandler = handler;
+        const char * _Nonnull argv [] =  {"", 0};
+        UIApplicationMain(0, argv, nil, NSStringFromClass([AppDelegate class]));
+    }
+}
+
diff --git a/cbits-uikit/ViewController.m b/cbits-uikit/ViewController.m
new file mode 100644
--- /dev/null
+++ b/cbits-uikit/ViewController.m
@@ -0,0 +1,40 @@
+#import "ViewController.h"
+
+extern void withWebView(WKWebView *, HsStablePtr);
+
+@interface ViewController ()
+
+@end
+
+@implementation ViewController
+
+-(id)initWithHandler:(HsStablePtr)handler {
+    self = [super init];
+    if (self) {
+        _handler = handler;
+    }
+    return self;
+}
+
+- (void)loadView {
+    [super loadView];
+    // WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
+    // [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
+    _webView = [[WKWebView alloc] init];
+    _webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+    self.view = _webView;
+}
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    withWebView(_webView, _handler);
+}
+
+
+- (void)didReceiveMemoryWarning {
+    [super didReceiveMemoryWarning];
+    // Dispose of any resources that can be recreated.
+}
+
+
+@end
diff --git a/cbits/Main.m b/cbits/Main.m
deleted file mode 100644
--- a/cbits/Main.m
+++ /dev/null
@@ -1,62 +0,0 @@
-#include "HsFFI.h"
-#import <Cocoa/Cocoa.h>
-#import <WebKit/WebKit.h>
-
-extern void withWebView(WKWebView *, HsStablePtr);
-
-@interface AppDelegate : NSObject <NSApplicationDelegate>
-@property (nonatomic, assign) IBOutlet NSWindow *window;
-@property (nonatomic, assign) HsStablePtr handler;
-
-- (instancetype)initApp:(HsStablePtr)handler progName:(NSString *)progName;
-@end
-
-@implementation AppDelegate
-
-- (void)applicationWillTerminate:(NSNotification *)aNotification {
-    // Insert code here to tear down your application
-}
-
--(id)initApp:(HsStablePtr)handler progName:(NSString *)progName {
-    self = [super init];
-    if (self) {
-        _handler = handler;
-        NSRect contentSize = NSMakeRect(0.0, 500.0, 1000.0, 700.0);
-        NSUInteger windowStyleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
-        _window = [[NSWindow alloc] initWithContentRect:contentSize styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:YES];
-        _window.backgroundColor = [NSColor whiteColor];
-        _window.title = progName;
-    }
-    return self;
-}
-
--(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];
-    [_window setContentView:webView];
-    withWebView(webView, _handler);
-}
-
--(void)applicationDidFinishLaunching:(NSNotification *)notification {
-    [_window orderFrontRegardless];
-    [_window center];
-    [NSApp activateIgnoringOtherApps:YES];
-}
-
--(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
-    return YES;
-}
-
-@end
-
-void runInWKWebView(HsStablePtr handler, const char * _Nonnull progName) {
-    @autoreleasepool {
-        NSApplication *application = [NSApplication sharedApplication];
-        AppDelegate *appDelegate = [[AppDelegate alloc] initApp:handler progName:[NSString stringWithCString:progName encoding:NSUTF8StringEncoding]];
-        [application setDelegate:appDelegate];
-        char * argv [] =  {"", 0};
-        NSApplicationMain(0, argv);
-    }
-}
-
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.8.1.0
+version: 0.8.2.0
 cabal-version: >=1.10
 build-type: Simple
 license: MIT
@@ -21,6 +21,7 @@
 library
     exposed-modules:
         Language.Javascript.JSaddle.WKWebView
+        Language.Javascript.JSaddle.WKWebView.Internal
     build-depends:
         aeson >=0.8.0.2 && <1.1,
         base <5,
@@ -28,9 +29,20 @@
         jsaddle >= 0.8.0.0 && <0.9
     default-language: Haskell2010
     hs-source-dirs: src
-    frameworks: Cocoa, WebKit
+    frameworks: Foundation, WebKit
     ghc-options: -ferror-spans -Wall
     c-sources:
-        cbits/Main.m
         cbits/WKWebView.m
-
+    if impl(ghcjs)
+        hs-source-dirs: src-ghcjs
+    else
+        hs-source-dirs: src-ghc
+        if os(ios)
+            frameworks: UIKit
+            c-sources:
+                cbits-uikit/AppDelegate.m
+                cbits-uikit/ViewController.m
+        else
+            frameworks: Cocoa
+            c-sources:
+                cbits-cocoa/AppDelegate.m
diff --git a/src-ghc/Language/Javascript/JSaddle/WKWebView.hs b/src-ghc/Language/Javascript/JSaddle/WKWebView.hs
new file mode 100644
--- /dev/null
+++ b/src-ghc/Language/Javascript/JSaddle/WKWebView.hs
@@ -0,0 +1,19 @@
+module Language.Javascript.JSaddle.WKWebView
+    ( jsaddleMain
+    , WKWebView(..)
+    , run
+    ) where
+
+import Language.Javascript.JSaddle.WKWebView.Internal (jsaddleMain, WKWebView(..))
+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 ()
+
+run :: JSM () -> IO ()
+run f = do
+    handler <- newStablePtr (jsaddleMain f)
+    progName <- getProgName
+    withCString progName $ runInWKWebView handler
diff --git a/src-ghcjs/Language/Javascript/JSaddle/WKWebView.hs b/src-ghcjs/Language/Javascript/JSaddle/WKWebView.hs
new file mode 100644
--- /dev/null
+++ b/src-ghcjs/Language/Javascript/JSaddle/WKWebView.hs
@@ -0,0 +1,7 @@
+module Language.Javascript.JSaddle.WKWebView
+    ( run
+    ) where
+
+run :: IO () -> IO ()
+run = id
+{-# INLINE run #-}
diff --git a/src/Language/Javascript/JSaddle/WKWebView.hs b/src/Language/Javascript/JSaddle/WKWebView.hs
deleted file mode 100644
--- a/src/Language/Javascript/JSaddle/WKWebView.hs
+++ /dev/null
@@ -1,90 +0,0 @@
-{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings #-}
-module Language.Javascript.JSaddle.WKWebView
-    ( jsaddleMain
-    , WKWebView(..)
-    , run
-    ) where
-
-import System.Environment (getProgName)
-import Control.Monad (void, join)
-import Control.Concurrent (forkIO)
-
-import Data.Monoid ((<>))
-import Data.ByteString (useAsCString, packCString)
-import Data.ByteString.Lazy (ByteString, toStrict, fromStrict)
-import Data.Aeson (encode, decode)
-
-import Foreign.C.String (CString, withCString)
-import Foreign.Ptr (Ptr)
-import Foreign.StablePtr (StablePtr, newStablePtr, deRefStablePtr)
-
-import Language.Javascript.JSaddle (Results, JSM)
-import Language.Javascript.JSaddle.Run (runJavaScript)
-import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
-
-newtype WKWebView = WKWebView (Ptr WKWebView)
-
-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 import ccall loadHTMLString :: WKWebView -> CString -> IO ()
-foreign import ccall evaluateJavaScript :: WKWebView -> CString -> IO ()
-foreign import ccall runInWKWebView :: StablePtr (WKWebView -> IO ()) -> CString -> IO ()
-
-run :: JSM () -> IO ()
-run f = do
-    handler <- newStablePtr (jsaddleMain f)
-    progName <- getProgName
-    withCString progName $ runInWKWebView handler
-
-jsaddleMain :: JSM () -> WKWebView -> IO ()
-jsaddleMain f webView = do
-    (processResult, start) <- runJavaScript (\batch ->
-        useAsCString (toStrict $ "runJSaddleBatch(" <> encode batch <> ");") $
-            evaluateJavaScript webView)
-        f
-
-    startHandler <- newStablePtr (void $ forkIO start)
-    resultHandler <- newStablePtr processResult
-    addJSaddleHandler webView startHandler resultHandler
-
-    useAsCString (toStrict indexHtml) $ loadHTMLString webView
-    useAsCString (toStrict jsaddleJs) $ evaluateJavaScript webView
-
-jsaddleStart :: StablePtr (IO ()) -> IO ()
-jsaddleStart ptrHandler = join (deRefStablePtr ptrHandler)
-
-jsaddleResult :: StablePtr (Results -> IO ()) -> CString -> IO ()
-jsaddleResult ptrHandler s = do
-    processResult <- deRefStablePtr ptrHandler
-    result <- packCString s
-    case decode (fromStrict result) of
-        Nothing -> error $ "jsaddle WebSocket decode failed : " <> show result
-        Just r  -> processResult r
-
-withWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
-withWebView webView ptrF = do
-    f <- deRefStablePtr ptrF
-    f webView
-
-jsaddleJs :: ByteString
-jsaddleJs = ghcjsHelpers <> "\
-    \runJSaddleBatch = (function() {\n\
-    \ " <> initState <> "\n\
-    \ return function(batch) {\n\
-    \ " <> runBatch (\a -> "window.webkit.messageHandlers.jsaddle.postMessage(JSON.stringify(" <> a <> "));") <> "\
-    \ };\n\
-    \})();\n\
-    \"
-
-indexHtml :: ByteString
-indexHtml =
-    "<!DOCTYPE html>\n\
-    \<html>\n\
-    \<head>\n\
-    \<title>JSaddle</title>\n\
-    \</head>\n\
-    \<body>\n\
-    \</body>\n\
-    \</html>"
diff --git a/src/Language/Javascript/JSaddle/WKWebView/Internal.hs b/src/Language/Javascript/JSaddle/WKWebView/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/Javascript/JSaddle/WKWebView/Internal.hs
@@ -0,0 +1,81 @@
+{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings #-}
+module Language.Javascript.JSaddle.WKWebView.Internal
+    ( jsaddleMain
+    , WKWebView(..)
+    ) where
+
+import Control.Monad (void, join)
+import Control.Concurrent (forkIO)
+
+import Data.Monoid ((<>))
+import Data.ByteString (useAsCString, packCString)
+import Data.ByteString.Lazy (ByteString, toStrict, fromStrict)
+import Data.Aeson (encode, decode)
+
+import Foreign.C.String (CString)
+import Foreign.Ptr (Ptr)
+import Foreign.StablePtr (StablePtr, newStablePtr, deRefStablePtr)
+
+import Language.Javascript.JSaddle (Results, JSM)
+import Language.Javascript.JSaddle.Run (runJavaScript)
+import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
+
+newtype WKWebView = WKWebView (Ptr WKWebView)
+
+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 import ccall loadHTMLString :: WKWebView -> CString -> IO ()
+foreign import ccall evaluateJavaScript :: WKWebView -> CString -> IO ()
+
+jsaddleMain :: JSM () -> WKWebView -> IO ()
+jsaddleMain f webView = do
+    (processResult, start) <- runJavaScript (\batch ->
+        useAsCString (toStrict $ "runJSaddleBatch(" <> encode batch <> ");") $
+            evaluateJavaScript webView)
+        f
+
+    startHandler <- newStablePtr (void $ forkIO start)
+    resultHandler <- newStablePtr processResult
+    addJSaddleHandler webView startHandler resultHandler
+
+    useAsCString (toStrict indexHtml) $ loadHTMLString webView
+    useAsCString (toStrict jsaddleJs) $ evaluateJavaScript webView
+
+jsaddleStart :: StablePtr (IO ()) -> IO ()
+jsaddleStart ptrHandler = join (deRefStablePtr ptrHandler)
+
+jsaddleResult :: StablePtr (Results -> IO ()) -> CString -> IO ()
+jsaddleResult ptrHandler s = do
+    processResult <- deRefStablePtr ptrHandler
+    result <- packCString s
+    case decode (fromStrict result) of
+        Nothing -> error $ "jsaddle WebSocket decode failed : " <> show result
+        Just r  -> processResult r
+
+withWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
+withWebView webView ptrF = do
+    f <- deRefStablePtr ptrF
+    f webView
+
+jsaddleJs :: ByteString
+jsaddleJs = ghcjsHelpers <> "\
+    \runJSaddleBatch = (function() {\n\
+    \ " <> initState <> "\n\
+    \ return function(batch) {\n\
+    \ " <> runBatch (\a -> "window.webkit.messageHandlers.jsaddle.postMessage(JSON.stringify(" <> a <> "));") <> "\
+    \ };\n\
+    \})();\n\
+    \"
+
+indexHtml :: ByteString
+indexHtml =
+    "<!DOCTYPE html>\n\
+    \<html>\n\
+    \<head>\n\
+    \<title>JSaddle</title>\n\
+    \</head>\n\
+    \<body>\n\
+    \</body>\n\
+    \</html>"
