Skip to content

Commit

Permalink
Merge pull request #733 from quoid/refactor/app-webview-initialization
Browse files Browse the repository at this point in the history
refactor: improve app webview initialization process
  • Loading branch information
ACTCD authored Sep 29, 2024
2 parents 2700906 + 4e1f5fb commit 55f0f1b
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 75 deletions.
110 changes: 60 additions & 50 deletions src/app/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,80 @@
import logo from "./img/logo.svg?raw";
const baseUrl = "https://github.com/quoid/userscripts";
const webkit = window.webkit.messageHandlers.controller;
let version = "v0.0.0";
let build = "(0)";
let directory = "init";
window.APP = {
show: () => {},
printVersion: (v, b) => {
version = v;
build = b;
},
printDirectory: (d) => {
directory = d;
},
window.webapp = {
updateDirectory: (newDir) => (directory = newDir),
};
async function initialize() {
const app = await webkit.postMessage("INIT");
directory = app.directory;
return app;
}
function changeDirectory() {
window.webkit?.messageHandlers.controller.postMessage("CHANGE_DIRECTORY");
webkit.postMessage("CHANGE_DIRECTORY");
}
function openDirectory() {
window.webkit?.messageHandlers.controller.postMessage("OPEN_DIRECTORY");
webkit.postMessage("OPEN_DIRECTORY");
}
</script>

<main>
<div class="section icons">
<img class="icon" src={icon} alt="Userscripts App Icon" draggable="false" />
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
<div class="logo">{@html logo}</div>
<div class="version">
{#if import.meta.env.GIT_TAG && import.meta.env.GIT_COMMIT}
<a href="{baseUrl}/releases/tag/{import.meta.env.GIT_TAG}">
{import.meta.env.GIT_TAG}
</a>
(<a href="{baseUrl}/commit/{import.meta.env.GIT_COMMIT}">
{import.meta.env.GIT_COMMIT.slice(0, 7)}
</a>)
{:else}
<span>{version}</span>
<span>{build}</span>
{/if}
{#await initialize() then app}
<div class="section icons">
<img
alt="Userscripts App Icon"
class="icon"
draggable="false"
src={icon}
/>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
<div class="logo">{@html logo}</div>
<div class="version">
{#if import.meta.env.GIT_TAG && import.meta.env.GIT_COMMIT}
<a href="{baseUrl}/releases/tag/{import.meta.env.GIT_TAG}">
{import.meta.env.GIT_TAG}
</a>
(<a href="{baseUrl}/commit/{import.meta.env.GIT_COMMIT}">
{import.meta.env.GIT_COMMIT.slice(0, 7)}
</a>)
{:else}
<span>v{app.version}</span>
<span>({app.build})</span>
{/if}
</div>
</div>
</div>
<div class="section guide">
<p>
You can turn on the Userscripts iOS Safari extension in Settings or
Safari, then use the extension in Safari. Please refer to the "Usage"
section in the
<a
href="{baseUrl}/blob/{import.meta.env.GIT_TAG ??
'main'}/README.md#usage">README of this version</a
>.
</p>
</div>
<div class="section action">
<button id="changedir" on:click={changeDirectory}>
Change Userscripts Directory
</button>
<div class="current">CURRENT DIRECTORY:</div>
<button id="directory" class="link" on:click={openDirectory}>
{directory}
</button>
</div>
<div class="section guide">
<p>
You can turn on the Userscripts iOS Safari extension in Settings or
Safari, then use the extension in Safari. Please refer to the "Usage"
section in the
<a
href="{baseUrl}/blob/{import.meta.env.GIT_TAG ??
'main'}/README.md#usage">README of this version</a
>.
</p>
</div>
<div class="section action">
<button id="changedir" on:click={changeDirectory}>
Change Userscripts Directory
</button>
<div class="current">CURRENT DIRECTORY:</div>
<button id="directory" class="link" on:click={openDirectory}>
{directory}
</button>
</div>
{:catch error}
<div class="section">
{error}
</div>
{/await}

<div class="section footer">
<div class="links">
<a href="{baseUrl}/blob/{import.meta.env.GIT_TAG ?? 'main'}/README.md"
Expand Down
20 changes: 11 additions & 9 deletions src/app/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,25 @@

declare global {
interface Window {
APP: {
show: (
platform: "ios" | "mac",
enabled: boolean,
useSettingsInsteadOfPreferences: boolean,
) => void;
printVersion: (v: string, b: string) => void;
printDirectory: (d: string) => void;
webapp: {
updateDirectory: (directory: string) => void;
};
webkit: {
messageHandlers: {
controller: {
postMessage: function;
postMessage: <T extends MessageBody>(
message: T,
) => Promise<MessageReply<T>>;
};
};
};
}
}

type MessageBody = "INIT" | "CHANGE_DIRECTORY" | "OPEN_DIRECTORY";

type MessageReply<T> = T extends "INIT"
? { build: string; version: string; directory: string }
: void;

export {};
47 changes: 31 additions & 16 deletions xcode/App-Shared/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import WebKit

private let logger = USLogger(#fileID)

class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, UIDocumentPickerDelegate {
class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandlerWithReply, UIDocumentPickerDelegate {

@IBOutlet var webView: WKWebView!

Expand All @@ -13,7 +13,7 @@ class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHan
// https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler
configuration.setURLSchemeHandler(USchemeHandler(), forURLScheme: AppWebViewUrlScheme)
// https://developer.apple.com/documentation/webkit/wkusercontentcontroller
configuration.userContentController.add(self, name: "controller")
configuration.userContentController.addScriptMessageHandler(self, contentWorld: .page, name: "controller")
// https://developer.apple.com/documentation/webkit/wkwebview
self.webView = WKWebView(frame: .zero, configuration: configuration)
// https://developer.apple.com/documentation/webkit/wknavigationdelegate
Expand All @@ -40,12 +40,11 @@ class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHan
webView.load(URLRequest(url: URL(string: "\(AppWebViewUrlScheme):///")!))
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "??"
let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "??"
webView.evaluateJavaScript("APP.printVersion('v\(appVersion)', '(\(buildNumber))')")
webView.evaluateJavaScript("APP.printDirectory('\(getCurrentScriptsDirectoryString())')")
}
// https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview
// DOMContentLoaded
// func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// webView.evaluateJavaScript("")
// }

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.navigationType == .linkActivated {
Expand Down Expand Up @@ -79,32 +78,48 @@ class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHan
decisionHandler(.allow)
}

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (
Any?,
String?
) {
guard let name = message.body as? String else {
logger?.error("\(#function, privacy: .public) - Userscripts iOS received a message without a name")
return
return (nil, "bad message body")
}
if name == "CHANGE_DIRECTORY" {
switch name {
case "INIT":
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0"
let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "0"
return ([
"build": buildNumber,
"version": appVersion,
"directory": getCurrentScriptsDirectoryString(),
], nil)
case "CHANGE_DIRECTORY":
// https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories
logger?.info("\(#function, privacy: .public) - Userscripts iOS has requested to set the readLocation")
let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.folder])
documentPicker.delegate = self
documentPicker.directoryURL = getDocumentsDirectory()
present(documentPicker, animated: true, completion: nil)
}
if name == "OPEN_DIRECTORY" {
break
case "OPEN_DIRECTORY":
guard var components = URLComponents(url: Preferences.scriptsDirectoryUrl, resolvingAgainstBaseURL: true) else {
return
return (nil, "ScriptsDirectoryUrl malformed")
}
components.scheme = "shareddocuments"
if let url = components.url, UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
await UIApplication.shared.open(url)
}
break
default:
return (nil, "Unexpected message body")
}
return (nil, nil)
}

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
Preferences.scriptsDirectoryUrl = url
webView.evaluateJavaScript("APP.printDirectory('\(getCurrentScriptsDirectoryString())')")
webView.evaluateJavaScript("webapp.updateDirectory('\(getCurrentScriptsDirectoryString())')")
}
}

0 comments on commit 55f0f1b

Please sign in to comment.