@tokamakdev/platform-ios-simulator-x64 0.1.1-beta.82 → 0.1.1-beta.83
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios-simulator-x64/build/entrypoint +2 -2
- package/ios-simulator-x64/frameworks/TokamakRuntime.framework/Headers/TokamakRuntime.h +6 -6
- package/ios-simulator-x64/frameworks/TokamakRuntime.framework/TokamakRuntime +0 -0
- package/ios-simulator-x64/native-shell/{TokamakHost.swift → TokamakApp.swift} +111 -30
- package/ios-simulator-x64/native-shell/TokamakPlugins.swift +25 -0
- package/ios-simulator-x64/native-shell/TokamakShell.swift +64 -16
- package/package.json +1 -1
|
@@ -172,14 +172,14 @@ fi
|
|
|
172
172
|
# sources are Swift.
|
|
173
173
|
swift_type='^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$'
|
|
174
174
|
registry=$temporary/TokamakPluginRegistry.swift
|
|
175
|
-
printf '%s\n\n%s\n%s\n' 'import Foundation' 'func tokamakPlugins(
|
|
175
|
+
printf '%s\n\n%s\n%s\n' 'import Foundation' 'func tokamakPlugins(app: TokamakApp) -> [any TokamakPlugin] {' ' [' > "$registry"
|
|
176
176
|
for plugin in "$input/plugins"/*/; do
|
|
177
177
|
class=$(cat "$plugin/class" 2>/dev/null || true)
|
|
178
178
|
if [[ ! "$class" =~ $swift_type ]]; then
|
|
179
179
|
echo "plugin '$(basename "$plugin")' has invalid $platform class '$class'" >&2
|
|
180
180
|
exit 1
|
|
181
181
|
fi
|
|
182
|
-
printf ' %s(host:
|
|
182
|
+
printf ' %s(host: TokamakHost(app: app, plugin: "%s")),\n' "$class" "$(basename "$plugin")" >> "$registry"
|
|
183
183
|
for source in "$plugin/sources"/*; do
|
|
184
184
|
case "$source" in
|
|
185
185
|
*.swift) swift_sources+=("$source") ;;
|
|
@@ -23,19 +23,19 @@ typedef struct {
|
|
|
23
23
|
|
|
24
24
|
void *tokamak_runtime_start(const char *packaged_dir, const char *state_dir,
|
|
25
25
|
const char *storage_dir, const char *host,
|
|
26
|
-
char *error, size_t error_len);
|
|
26
|
+
bool foreground, char *error, size_t error_len);
|
|
27
27
|
void *tokamak_runtime_start_development(const char *state_dir, const char *host,
|
|
28
28
|
const char *endpoint,
|
|
29
|
-
const char *session_token,
|
|
29
|
+
const char *session_token,
|
|
30
|
+
bool foreground, char *error,
|
|
30
31
|
size_t error_len);
|
|
31
32
|
uint16_t tokamak_runtime_port(const void *runtime);
|
|
32
33
|
uint16_t tokamak_runtime_restore_gateway(const void *runtime, char *error,
|
|
33
34
|
size_t error_len);
|
|
34
35
|
void tokamak_runtime_stop(void *runtime);
|
|
35
|
-
bool
|
|
36
|
-
const char *
|
|
37
|
-
TokamakBytes *
|
|
38
|
-
size_t error_len);
|
|
36
|
+
bool tokamak_runtime_emit(const void *runtime, const char *name,
|
|
37
|
+
const char *event, uint64_t timeout_ms,
|
|
38
|
+
TokamakBytes *reply, char *error, size_t error_len);
|
|
39
39
|
|
|
40
40
|
int32_t tokamak_runtime_server_authority(const void *runtime, const char *host,
|
|
41
41
|
TokamakBytes *authority);
|
|
Binary file
|
|
@@ -1,22 +1,43 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import TokamakRuntime
|
|
3
3
|
|
|
4
|
+
#if os(iOS)
|
|
5
|
+
import UIKit
|
|
6
|
+
#endif
|
|
7
|
+
|
|
4
8
|
private let startupErrorFile = "startup-error.log"
|
|
9
|
+
/// How long the Worker has for `resume` and `suspend`.
|
|
10
|
+
private let lifecycleDeadline: TimeInterval = 10
|
|
11
|
+
#if os(iOS)
|
|
12
|
+
/// The system gives an app about 30 seconds after it leaves the foreground.
|
|
13
|
+
private let suspendDeadline: TimeInterval = 25
|
|
14
|
+
#endif
|
|
5
15
|
|
|
6
16
|
/// The tokamak runtime and native plugins of this process. Both outlive any
|
|
7
17
|
/// WebView, so plugins receive app events before a page loads and a Worker
|
|
8
18
|
/// event can run while no page is loaded.
|
|
9
|
-
final class
|
|
19
|
+
final class TokamakApp {
|
|
10
20
|
private(set) var plugins: [any TokamakPlugin] = []
|
|
11
21
|
private var runtime: Result<RuntimeHandle, Error>?
|
|
12
22
|
private var waiting: [(Result<RuntimeHandle, Error>) -> Void] = []
|
|
23
|
+
/// Delivers `resume` and `suspend` one at a time, in order.
|
|
24
|
+
private let lifecycle = DispatchQueue(label: "tokamak.lifecycle")
|
|
25
|
+
#if os(iOS)
|
|
26
|
+
/// The scenes in the foreground.
|
|
27
|
+
private var foregroundScenes = 0
|
|
28
|
+
#endif
|
|
13
29
|
|
|
14
|
-
/// Creates the plugins
|
|
15
|
-
/// before launch finishes.
|
|
30
|
+
/// Creates the plugins. Call it before launch finishes.
|
|
16
31
|
init() {
|
|
17
|
-
plugins = tokamakPlugins(
|
|
32
|
+
plugins = tokamakPlugins(app: self)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// Starts the runtime in the background, which delivers `start` with
|
|
36
|
+
/// `foreground`. Call it once, on the main thread.
|
|
37
|
+
func start(foreground: Bool) {
|
|
38
|
+
dispatchPrecondition(condition: .onQueue(.main))
|
|
18
39
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
19
|
-
let result = Result { try RuntimeHandle() }
|
|
40
|
+
let result = Result { try RuntimeHandle(foreground: foreground) }
|
|
20
41
|
DispatchQueue.main.async { self.started(result) }
|
|
21
42
|
}
|
|
22
43
|
}
|
|
@@ -32,15 +53,17 @@ final class TokamakHost {
|
|
|
32
53
|
}
|
|
33
54
|
}
|
|
34
55
|
|
|
35
|
-
///
|
|
36
|
-
///
|
|
37
|
-
///
|
|
38
|
-
///
|
|
39
|
-
|
|
56
|
+
/// Delivers the event `name` with `event`, JSON-serialisable, to the
|
|
57
|
+
/// Worker's listeners on `queue`, and calls `completion` on the main thread
|
|
58
|
+
/// with their reply, parsed from JSON, or nil. Fails unless they return
|
|
59
|
+
/// within `timeout`, which includes runtime startup. Call it on the main
|
|
60
|
+
/// thread.
|
|
61
|
+
func emit(
|
|
40
62
|
_ name: String,
|
|
41
|
-
|
|
63
|
+
event: Any,
|
|
42
64
|
timeout: TimeInterval,
|
|
43
|
-
|
|
65
|
+
on queue: DispatchQueue = .global(qos: .userInitiated),
|
|
66
|
+
completion: @escaping (Result<Any?, Error>) -> Void
|
|
44
67
|
) {
|
|
45
68
|
let deadline = Date() + timeout
|
|
46
69
|
whenStarted { result in
|
|
@@ -48,9 +71,9 @@ final class TokamakHost {
|
|
|
48
71
|
case .failure(let error):
|
|
49
72
|
completion(.failure(error))
|
|
50
73
|
case .success(let runtime):
|
|
51
|
-
|
|
74
|
+
queue.async {
|
|
52
75
|
let outcome = Result {
|
|
53
|
-
try runtime.
|
|
76
|
+
try runtime.emit(name, event: event, timeout: deadline.timeIntervalSinceNow)
|
|
54
77
|
}
|
|
55
78
|
DispatchQueue.main.async { completion(outcome) }
|
|
56
79
|
}
|
|
@@ -58,6 +81,59 @@ final class TokamakHost {
|
|
|
58
81
|
}
|
|
59
82
|
}
|
|
60
83
|
|
|
84
|
+
/// Delivers `resume` when the app moves into the foreground and `suspend`
|
|
85
|
+
/// when it moves out. Call it on the main thread.
|
|
86
|
+
func moved(toForeground foreground: Bool) {
|
|
87
|
+
let name = foreground ? "resume" : "suspend"
|
|
88
|
+
#if os(iOS)
|
|
89
|
+
// The system suspends the app soon after it leaves the foreground
|
|
90
|
+
// unless a background task holds it there.
|
|
91
|
+
var task = UIBackgroundTaskIdentifier.invalid
|
|
92
|
+
let finish = {
|
|
93
|
+
guard task != .invalid else { return }
|
|
94
|
+
UIApplication.shared.endBackgroundTask(task)
|
|
95
|
+
task = .invalid
|
|
96
|
+
}
|
|
97
|
+
if !foreground {
|
|
98
|
+
task = UIApplication.shared.beginBackgroundTask(
|
|
99
|
+
withName: "tokamak suspend", expirationHandler: finish)
|
|
100
|
+
}
|
|
101
|
+
let timeout = foreground ? lifecycleDeadline : suspendDeadline
|
|
102
|
+
#else
|
|
103
|
+
let finish = {}
|
|
104
|
+
let timeout = lifecycleDeadline
|
|
105
|
+
#endif
|
|
106
|
+
emit(name, event: [String: Any](), timeout: timeout, on: lifecycle) { result in
|
|
107
|
+
if case .failure(let error) = result {
|
|
108
|
+
print("tokamak \(name) failed: \(error)")
|
|
109
|
+
}
|
|
110
|
+
finish()
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#if os(iOS)
|
|
115
|
+
/// Delivers `resume` as the first scene enters the foreground.
|
|
116
|
+
func sceneWillEnterForeground() {
|
|
117
|
+
foregroundScenes += 1
|
|
118
|
+
if foregroundScenes == 1 {
|
|
119
|
+
moved(toForeground: true)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// Delivers `suspend` as the last scene leaves the foreground.
|
|
124
|
+
func sceneDidEnterBackground() {
|
|
125
|
+
foregroundScenes -= 1
|
|
126
|
+
if foregroundScenes == 0 {
|
|
127
|
+
moved(toForeground: false)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// Whether a scene is in the foreground.
|
|
132
|
+
var isInForeground: Bool {
|
|
133
|
+
foregroundScenes > 0
|
|
134
|
+
}
|
|
135
|
+
#endif
|
|
136
|
+
|
|
61
137
|
func didRegisterForRemoteNotifications(deviceToken: Data) {
|
|
62
138
|
for plugin in plugins {
|
|
63
139
|
plugin.didRegisterForRemoteNotifications(deviceToken: deviceToken)
|
|
@@ -147,7 +223,8 @@ final class RuntimeHandle {
|
|
|
147
223
|
let appHost: String
|
|
148
224
|
private var handle: UnsafeMutableRawPointer?
|
|
149
225
|
|
|
150
|
-
|
|
226
|
+
/// Starts the runtime, which delivers `start` with `foreground`.
|
|
227
|
+
init(foreground: Bool) throws {
|
|
151
228
|
guard
|
|
152
229
|
let appHost = Bundle.main.object(forInfoDictionaryKey: "TokamakHost") as? String,
|
|
153
230
|
!appHost.isEmpty
|
|
@@ -172,6 +249,7 @@ final class RuntimeHandle {
|
|
|
172
249
|
appHost,
|
|
173
250
|
endpoint,
|
|
174
251
|
sessionToken,
|
|
252
|
+
foreground,
|
|
175
253
|
error.baseAddress,
|
|
176
254
|
error.count
|
|
177
255
|
)
|
|
@@ -196,6 +274,7 @@ final class RuntimeHandle {
|
|
|
196
274
|
statePath,
|
|
197
275
|
storagePath,
|
|
198
276
|
appHost,
|
|
277
|
+
foreground,
|
|
199
278
|
error.baseAddress,
|
|
200
279
|
error.count
|
|
201
280
|
)
|
|
@@ -232,38 +311,40 @@ final class RuntimeHandle {
|
|
|
232
311
|
return port
|
|
233
312
|
}
|
|
234
313
|
|
|
235
|
-
///
|
|
236
|
-
///
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
314
|
+
/// Delivers the event `name` with `event`, JSON-serialisable, to the
|
|
315
|
+
/// Worker's listeners, blocking until they return or `timeout` passes, and
|
|
316
|
+
/// returns their reply, parsed from JSON, or nil.
|
|
317
|
+
func emit(_ name: String, event: Any, timeout: TimeInterval) throws -> Any? {
|
|
318
|
+
guard JSONSerialization.isValidJSONObject([event]) else {
|
|
319
|
+
throw RuntimeError.runtime("the \(name) event is not JSON")
|
|
240
320
|
}
|
|
241
|
-
let
|
|
242
|
-
decoding: try JSONSerialization.data(withJSONObject:
|
|
321
|
+
let event = String(
|
|
322
|
+
decoding: try JSONSerialization.data(withJSONObject: event, options: .fragmentsAllowed),
|
|
243
323
|
as: UTF8.self
|
|
244
324
|
)
|
|
245
|
-
var
|
|
325
|
+
var reply = TokamakBytes()
|
|
246
326
|
var error = [CChar](repeating: 0, count: 1024)
|
|
247
327
|
let succeeded = name.withCString { name in
|
|
248
|
-
|
|
328
|
+
event.withCString { event in
|
|
249
329
|
error.withUnsafeMutableBufferPointer { error in
|
|
250
|
-
|
|
330
|
+
tokamak_runtime_emit(
|
|
251
331
|
handle,
|
|
252
332
|
name,
|
|
253
|
-
|
|
333
|
+
event,
|
|
254
334
|
UInt64(max(timeout, 0) * 1000),
|
|
255
|
-
&
|
|
335
|
+
&reply,
|
|
256
336
|
error.baseAddress,
|
|
257
337
|
error.count
|
|
258
338
|
)
|
|
259
339
|
}
|
|
260
340
|
}
|
|
261
341
|
}
|
|
262
|
-
defer { tokamak_bytes_free(
|
|
263
|
-
guard succeeded else {
|
|
342
|
+
defer { tokamak_bytes_free(reply) }
|
|
343
|
+
guard succeeded, let json = reply.contents else {
|
|
264
344
|
throw RuntimeError.runtime(String(cString: error))
|
|
265
345
|
}
|
|
266
|
-
|
|
346
|
+
let value = try JSONSerialization.jsonObject(with: json, options: .fragmentsAllowed)
|
|
347
|
+
return value is NSNull ? nil : value
|
|
267
348
|
}
|
|
268
349
|
|
|
269
350
|
func serverAuthority(host: String) -> AuthenticationMaterial<Data> {
|
|
@@ -12,6 +12,31 @@ struct TokamakPluginError: Error {
|
|
|
12
12
|
|
|
13
13
|
typealias TokamakPluginReply = (Result<Any?, TokamakPluginError>) -> Void
|
|
14
14
|
|
|
15
|
+
/// The app as one plugin sees it.
|
|
16
|
+
final class TokamakHost {
|
|
17
|
+
private unowned let app: TokamakApp
|
|
18
|
+
private let plugin: String
|
|
19
|
+
|
|
20
|
+
init(app: TokamakApp, plugin: String) {
|
|
21
|
+
self.app = app
|
|
22
|
+
self.plugin = plugin
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Delivers the plugin's event `name`, which the Worker's listeners
|
|
26
|
+
/// receive as `<plugin>.<name>`, with `event`, JSON-serialisable, and calls
|
|
27
|
+
/// `completion` on the main thread with their reply, parsed from JSON, or
|
|
28
|
+
/// nil. Fails unless they return within `timeout`, which includes runtime
|
|
29
|
+
/// startup. Call it on the main thread.
|
|
30
|
+
func emit(
|
|
31
|
+
_ name: String,
|
|
32
|
+
event: Any,
|
|
33
|
+
timeout: TimeInterval,
|
|
34
|
+
completion: @escaping (Result<Any?, Error>) -> Void
|
|
35
|
+
) {
|
|
36
|
+
app.emit("\(plugin).\(name)", event: event, timeout: timeout, completion: completion)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
15
40
|
/// A native plugin, created once per process when the app launches.
|
|
16
41
|
protocol TokamakPlugin: AnyObject {
|
|
17
42
|
var id: String { get }
|
|
@@ -219,7 +219,7 @@ private final class NavigationDelegate: NSObject, WKNavigationDelegate, WKUIDele
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
private final class TokamakController {
|
|
222
|
-
private let
|
|
222
|
+
private let app: TokamakApp
|
|
223
223
|
private var runtime: RuntimeHandle?
|
|
224
224
|
private var navigation: NavigationDelegate?
|
|
225
225
|
private var pluginBridge: TokamakPluginBridge?
|
|
@@ -227,8 +227,8 @@ private final class TokamakController {
|
|
|
227
227
|
private var dataStore: WKWebsiteDataStore?
|
|
228
228
|
private var proxyPort: UInt16?
|
|
229
229
|
|
|
230
|
-
init(
|
|
231
|
-
self.
|
|
230
|
+
init(app: TokamakApp) {
|
|
231
|
+
self.app = app
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
deinit {
|
|
@@ -236,7 +236,7 @@ private final class TokamakController {
|
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
func start(frame: CGRect, completion: @escaping (WKWebView) -> Void) {
|
|
239
|
-
|
|
239
|
+
app.whenStarted { result in
|
|
240
240
|
switch result {
|
|
241
241
|
case .success(let runtime):
|
|
242
242
|
self.runtime = runtime
|
|
@@ -274,7 +274,7 @@ private final class TokamakController {
|
|
|
274
274
|
#endif
|
|
275
275
|
configuration.mediaTypesRequiringUserActionForPlayback = []
|
|
276
276
|
|
|
277
|
-
let pluginBridge = TokamakPluginBridge(host: runtime.appHost, plugins:
|
|
277
|
+
let pluginBridge = TokamakPluginBridge(host: runtime.appHost, plugins: app.plugins)
|
|
278
278
|
pluginBridge.install(in: configuration.userContentController)
|
|
279
279
|
let navigation = NavigationDelegate(runtime: runtime, pluginBridge: pluginBridge)
|
|
280
280
|
self.navigation = navigation
|
|
@@ -310,9 +310,11 @@ private final class TokamakController {
|
|
|
310
310
|
|
|
311
311
|
#if os(macOS)
|
|
312
312
|
private final class TokamakMacApplicationDelegate: NSObject, NSApplicationDelegate {
|
|
313
|
-
private lazy var
|
|
314
|
-
private lazy var controller = TokamakController(
|
|
313
|
+
private lazy var app = TokamakApp()
|
|
314
|
+
private lazy var controller = TokamakController(app: app)
|
|
315
315
|
private var window: NSWindow?
|
|
316
|
+
/// Whether the app is shown and its window is not miniaturised.
|
|
317
|
+
private var visible = false
|
|
316
318
|
|
|
317
319
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
318
320
|
let window = NSWindow(
|
|
@@ -327,6 +329,9 @@ private final class TokamakController {
|
|
|
327
329
|
window.makeKeyAndOrderFront(nil)
|
|
328
330
|
NSApplication.shared.activate(ignoringOtherApps: true)
|
|
329
331
|
self.window = window
|
|
332
|
+
visible = !NSApplication.shared.isHidden
|
|
333
|
+
app.start(foreground: visible)
|
|
334
|
+
observeVisibility()
|
|
330
335
|
|
|
331
336
|
guard let content = window.contentView else { return }
|
|
332
337
|
controller.start(frame: content.bounds) { webView in
|
|
@@ -343,21 +348,45 @@ private final class TokamakController {
|
|
|
343
348
|
_ application: NSApplication,
|
|
344
349
|
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
|
|
345
350
|
) {
|
|
346
|
-
|
|
351
|
+
app.didRegisterForRemoteNotifications(deviceToken: deviceToken)
|
|
347
352
|
}
|
|
348
353
|
|
|
349
354
|
func application(
|
|
350
355
|
_ application: NSApplication,
|
|
351
356
|
didFailToRegisterForRemoteNotificationsWithError error: Error
|
|
352
357
|
) {
|
|
353
|
-
|
|
358
|
+
app.didFailToRegisterForRemoteNotifications(error: error)
|
|
354
359
|
}
|
|
355
360
|
|
|
356
361
|
func application(
|
|
357
362
|
_ application: NSApplication,
|
|
358
363
|
didReceiveRemoteNotification userInfo: [String: Any]
|
|
359
364
|
) {
|
|
360
|
-
|
|
365
|
+
app.didReceiveRemoteNotification(userInfo) { _ in }
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/// Delivers `resume` and `suspend` as the app is hidden or shown, and as
|
|
369
|
+
/// its window is miniaturised or restored.
|
|
370
|
+
private func observeVisibility() {
|
|
371
|
+
let changes = [
|
|
372
|
+
NSApplication.didHideNotification,
|
|
373
|
+
NSApplication.didUnhideNotification,
|
|
374
|
+
NSWindow.didMiniaturizeNotification,
|
|
375
|
+
NSWindow.didDeminiaturizeNotification,
|
|
376
|
+
]
|
|
377
|
+
for change in changes {
|
|
378
|
+
_ = NotificationCenter.default.addObserver(forName: change, object: nil, queue: .main) {
|
|
379
|
+
[weak self] _ in
|
|
380
|
+
self?.visibilityChanged()
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
private func visibilityChanged() {
|
|
386
|
+
let visible = !NSApplication.shared.isHidden && window?.isMiniaturized == false
|
|
387
|
+
guard visible != self.visible else { return }
|
|
388
|
+
self.visible = visible
|
|
389
|
+
app.moved(toForeground: visible)
|
|
361
390
|
}
|
|
362
391
|
}
|
|
363
392
|
|
|
@@ -393,20 +422,32 @@ private final class TokamakController {
|
|
|
393
422
|
|
|
394
423
|
private class TokamakIOSApplicationDelegate: UIResponder, UIApplicationDelegate {
|
|
395
424
|
/// Exists from launch, whether or not a scene connects.
|
|
396
|
-
let
|
|
425
|
+
let app = TokamakApp()
|
|
426
|
+
|
|
427
|
+
/// Starts the runtime once launch finishes. A scene a user launches the
|
|
428
|
+
/// app into has entered the foreground by then.
|
|
429
|
+
func application(
|
|
430
|
+
_ application: UIApplication,
|
|
431
|
+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
|
432
|
+
) -> Bool {
|
|
433
|
+
DispatchQueue.main.async {
|
|
434
|
+
self.app.start(foreground: self.app.isInForeground)
|
|
435
|
+
}
|
|
436
|
+
return true
|
|
437
|
+
}
|
|
397
438
|
|
|
398
439
|
func application(
|
|
399
440
|
_ application: UIApplication,
|
|
400
441
|
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
|
|
401
442
|
) {
|
|
402
|
-
|
|
443
|
+
app.didRegisterForRemoteNotifications(deviceToken: deviceToken)
|
|
403
444
|
}
|
|
404
445
|
|
|
405
446
|
func application(
|
|
406
447
|
_ application: UIApplication,
|
|
407
448
|
didFailToRegisterForRemoteNotificationsWithError error: Error
|
|
408
449
|
) {
|
|
409
|
-
|
|
450
|
+
app.didFailToRegisterForRemoteNotifications(error: error)
|
|
410
451
|
}
|
|
411
452
|
}
|
|
412
453
|
|
|
@@ -416,7 +457,7 @@ private final class TokamakController {
|
|
|
416
457
|
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
|
|
417
458
|
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
|
|
418
459
|
) {
|
|
419
|
-
|
|
460
|
+
app.didReceiveRemoteNotification(userInfo) { result in
|
|
420
461
|
switch result {
|
|
421
462
|
case .newData: completionHandler(.newData)
|
|
422
463
|
case .noData: completionHandler(.noData)
|
|
@@ -431,6 +472,9 @@ private final class TokamakController {
|
|
|
431
472
|
private final class TokamakSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|
432
473
|
var window: UIWindow?
|
|
433
474
|
private var controller: TokamakController?
|
|
475
|
+
private var app: TokamakApp {
|
|
476
|
+
(UIApplication.shared.delegate as! TokamakIOSApplicationDelegate).app
|
|
477
|
+
}
|
|
434
478
|
|
|
435
479
|
func scene(
|
|
436
480
|
_ scene: UIScene,
|
|
@@ -438,14 +482,13 @@ private final class TokamakController {
|
|
|
438
482
|
options connectionOptions: UIScene.ConnectionOptions
|
|
439
483
|
) {
|
|
440
484
|
guard let scene = scene as? UIWindowScene else { return }
|
|
441
|
-
let host = (UIApplication.shared.delegate as! TokamakIOSApplicationDelegate).host
|
|
442
485
|
let viewController = UIViewController()
|
|
443
486
|
let window = UIWindow(windowScene: scene)
|
|
444
487
|
window.rootViewController = viewController
|
|
445
488
|
window.makeKeyAndVisible()
|
|
446
489
|
self.window = window
|
|
447
490
|
|
|
448
|
-
let controller = TokamakController(
|
|
491
|
+
let controller = TokamakController(app: app)
|
|
449
492
|
controller.start(frame: viewController.view.bounds) { webView in
|
|
450
493
|
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
451
494
|
viewController.view.addSubview(webView)
|
|
@@ -454,9 +497,14 @@ private final class TokamakController {
|
|
|
454
497
|
}
|
|
455
498
|
|
|
456
499
|
func sceneWillEnterForeground(_ scene: UIScene) {
|
|
500
|
+
app.sceneWillEnterForeground()
|
|
457
501
|
controller?.restoreGateway()
|
|
458
502
|
}
|
|
459
503
|
|
|
504
|
+
func sceneDidEnterBackground(_ scene: UIScene) {
|
|
505
|
+
app.sceneDidEnterBackground()
|
|
506
|
+
}
|
|
507
|
+
|
|
460
508
|
/// Releases the scene's window and page, which a reconnection recreates.
|
|
461
509
|
func sceneDidDisconnect(_ scene: UIScene) {
|
|
462
510
|
window = nil
|