@lucent-lang/lucent 0.0.4 → 0.1.1
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/README.md +97 -0
- package/app.plugin.js +46 -10
- package/dist/{bench-CR3E7e2e.js → bench-eEez4jhQ.js} +10 -6
- package/dist/{build-CAIrRSYA.js → build-CpUjeHFz.js} +1 -1
- package/dist/{build-check-QozuNJm5.js → build-check-BA1ZEp9s.js} +28 -13
- package/dist/{check-1SCmYlqA.js → check-BZx3FgI6.js} +1 -1
- package/dist/cli.js +62 -13
- package/dist/{codes-CIuwXt1S.js → codes-D-6LxUqD.js} +163 -7
- package/dist/compiler.js +4 -4
- package/dist/{dashboard-B-NzjjaN.js → dashboard-CC-HXhcd.js} +2 -2
- package/dist/{dev-BYWMP7oC.js → dev-DI5yL8zx.js} +62 -23
- package/dist/{diagnostic-BM9sXmwl.js → diagnostic-BvwLZQS1.js} +3 -1
- package/dist/{doctor-C0PrfWT6.js → doctor-CTAX3euc.js} +1 -1
- package/dist/{doctor-DdR4JnVc.js → doctor-CrgOAV5L.js} +1 -1
- package/dist/{explain--vV1oaRl.js → explain-Byit07F-.js} +1 -1
- package/dist/{packages-HyskKIsY.js → packages-CSmKeaAh.js} +2 -33
- package/dist/pipeline-BM_VM2X_.js +700 -0
- package/dist/project-BKNoTEEp.js +342 -0
- package/dist/sdk-coverage-iiv3JqMp.js +113 -0
- package/dist/sdk-diff-ByJ1Gw-F.js +109 -0
- package/dist/sdk-lock-BQn7IyRs.js +38 -0
- package/dist/{sdk-prefetch-mo04JDZV.js → sdk-prefetch-Bv2pNega.js} +2 -2
- package/dist/{sdk-search-Iv4K3-8a.js → sdk-search-CPGJm_Ne.js} +2 -2
- package/dist/{sdk-show-CkrdlqlP.js → sdk-show-BUkLqRnA.js} +5 -4
- package/dist/sdk-usage-0ZFMAYg1.js +95 -0
- package/dist/src-ChnmWLwn.js +40851 -0
- package/dist/trace-Bi4hkmPx.js +139 -0
- package/lib/core.cjs +368 -1
- package/lib/sdk/android.d.ts +54 -1
- package/lib/sdk/compose.d.ts +94 -0
- package/lib/sdk/compose.schemas.json.gz +0 -0
- package/lib/sdk/core.d.ts +201 -0
- package/lib/sdk/ios.d.ts +75 -2
- package/lib/sdk/ui.d.ts +129 -0
- package/metro/transformer.cjs +11 -2
- package/package.json +4 -3
- package/runtime/cpp/lucent/abort.cpp +59 -11
- package/runtime/cpp/lucent/abort.h +22 -6
- package/runtime/cpp/lucent/array.h +12 -2
- package/runtime/cpp/lucent/async.h +101 -21
- package/runtime/cpp/lucent/bigint.cpp +898 -0
- package/runtime/cpp/lucent/bigint.h +302 -0
- package/runtime/cpp/lucent/buffer.cpp +228 -0
- package/runtime/cpp/lucent/buffer.h +328 -0
- package/runtime/cpp/lucent/builtins.cpp +4 -1
- package/runtime/cpp/lucent/bytes.h +21 -2
- package/runtime/cpp/lucent/callback.cpp +29 -0
- package/runtime/cpp/lucent/callback.h +230 -0
- package/runtime/cpp/lucent/compute.cpp +370 -0
- package/runtime/cpp/lucent/compute.h +455 -0
- package/runtime/cpp/lucent/conversions.h +111 -0
- package/runtime/cpp/lucent/core.h +16 -5
- package/runtime/cpp/lucent/equality.h +107 -28
- package/runtime/cpp/lucent/execution.cpp +416 -0
- package/runtime/cpp/lucent/execution.h +270 -0
- package/runtime/cpp/lucent/extension.cpp +97 -0
- package/runtime/cpp/lucent/extension.h +210 -0
- package/runtime/cpp/lucent/helpers.h +12 -5
- package/runtime/cpp/lucent/items.h +52 -0
- package/runtime/cpp/lucent/jserror.cpp +9 -0
- package/runtime/cpp/lucent/jserror.h +11 -0
- package/runtime/cpp/lucent/jsi/convert.cpp +138 -0
- package/runtime/cpp/lucent/jsi/convert.h +128 -59
- package/runtime/cpp/lucent/jsi/host.cpp +213 -35
- package/runtime/cpp/lucent/jsi/host.h +116 -16
- package/runtime/cpp/lucent/json.h +19 -1
- package/runtime/cpp/lucent/lifecycle.cpp +260 -0
- package/runtime/cpp/lucent/lifecycle.h +124 -0
- package/runtime/cpp/lucent/lucent.h +7 -0
- package/runtime/cpp/lucent/map.h +41 -7
- package/runtime/cpp/lucent/native.cpp +35 -47
- package/runtime/cpp/lucent/native.h +116 -26
- package/runtime/cpp/lucent/number.cpp +38 -38
- package/runtime/cpp/lucent/number.h +33 -0
- package/runtime/cpp/lucent/operation.h +65 -0
- package/runtime/cpp/lucent/ops.h +14 -6
- package/runtime/cpp/lucent/platform/android.cpp +223 -10
- package/runtime/cpp/lucent/platform/android.h +227 -4
- package/runtime/cpp/lucent/platform/android_activity.cpp +258 -0
- package/runtime/cpp/lucent/platform/android_requests.h +140 -0
- package/runtime/cpp/lucent/platform/compose.h +256 -0
- package/runtime/cpp/lucent/platform/ios.h +325 -25
- package/runtime/cpp/lucent/platform/ios.mm +1 -14
- package/runtime/cpp/lucent/platform/ios_ui.h +192 -0
- package/runtime/cpp/lucent/platform/ios_ui.mm +454 -0
- package/runtime/cpp/lucent/platform/swiftui.h +190 -0
- package/runtime/cpp/lucent/presentation.cpp +11 -0
- package/runtime/cpp/lucent/presentation.h +109 -0
- package/runtime/cpp/lucent/reactive.cpp +406 -0
- package/runtime/cpp/lucent/reactive.h +566 -0
- package/runtime/cpp/lucent/report.cpp +40 -0
- package/runtime/cpp/lucent/report.h +16 -0
- package/runtime/cpp/lucent/resource.cpp +83 -0
- package/runtime/cpp/lucent/resource.h +75 -0
- package/runtime/cpp/lucent/scheduler.cpp +38 -124
- package/runtime/cpp/lucent/scheduler.h +102 -83
- package/runtime/cpp/lucent/scope.cpp +308 -0
- package/runtime/cpp/lucent/scope.h +452 -0
- package/runtime/cpp/lucent/sizing.h +263 -0
- package/runtime/cpp/lucent/slots.h +231 -0
- package/runtime/cpp/lucent/trace.cpp +539 -0
- package/runtime/cpp/lucent/trace.h +251 -0
- package/runtime/cpp/lucent/transport.cpp +9 -0
- package/runtime/cpp/lucent/transport.h +341 -0
- package/runtime/cpp/lucent/view.cpp +123 -0
- package/runtime/cpp/lucent/view.h +157 -0
- package/runtime/cpp/rn/LucentComponentView.h +121 -0
- package/runtime/cpp/rn/LucentComponentView.mm +479 -0
- package/runtime/cpp/rn/LucentModule.cpp +37 -7
- package/runtime/cpp/rn/LucentModule.h +3 -0
- package/runtime/cpp/rn/LucentViewChildren.h +80 -0
- package/runtime/cpp/rn/LucentViewChildren.mm +211 -0
- package/runtime/cpp/rn/LucentViewRegistry.h +62 -0
- package/runtime/cpp/rn/LucentViewRequests.cpp +91 -0
- package/runtime/cpp/rn/LucentViewRequests.h +104 -0
- package/runtime/cpp/rn/LucentViewSizing.h +345 -0
- package/runtime/cpp/rn/LucentViewSlots.h +242 -0
- package/runtime/cpp/rn/LucentViewValues.h +125 -0
- package/runtime/cpp/rn/LucentViews.h +440 -0
- package/runtime/cpp/rn/LucentViewsAndroid.cpp +439 -0
- package/runtime/cpp/rn/LucentViewsAndroid.h +138 -0
- package/runtime/cpp/third_party/dragonbox/LICENSE-Boost +23 -0
- package/runtime/cpp/third_party/dragonbox/README.md +16 -0
- package/runtime/cpp/third_party/dragonbox/dragonbox.h +4440 -0
- package/runtime/js/index.d.ts +11 -1
- package/runtime/js/index.js +84 -8
- package/runtime/js/views.d.ts +38 -0
- package/runtime/js/views.js +226 -0
- package/runtime/native/android/CMakeLists.txt +4 -1
- package/runtime/native/android/src/compose/java/dev/lucent/compose/CompositionRules.kt +44 -0
- package/runtime/native/android/src/compose/java/dev/lucent/compose/LucentComposition.kt +161 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentActivities.java +292 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentChildren.java +174 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentHostView.java +232 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentInitializer.java +46 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +4 -3
- package/runtime/native/android/src/main/java/dev/lucent/LucentRequestActivity.java +92 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentSlotView.java +102 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViewManager.java +149 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViewManagers.java +16 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViews.java +44 -0
- package/runtime/test/jsi/harness.cpp +9 -0
- package/schemas/build.schema.json +96 -1
- package/schemas/check.schema.json +56 -1
- package/schemas/lucent.schema.json +495 -9
- package/schemas/sdk-diff.schema.json +70 -0
- package/schemas/sdk-lock.schema.json +67 -0
- package/ts-plugin/index.js +85 -4
- package/dist/pipeline-CH7-2cxu.js +0 -232
- package/dist/project-D8tEet0k.js +0 -150
- package/dist/sdk-coverage-BoW_3Efd.js +0 -64
- package/dist/src-BD7f1_aZ.js +0 -11493
|
@@ -5,27 +5,40 @@
|
|
|
5
5
|
#import <objc/runtime.h>
|
|
6
6
|
#import <Foundation/Foundation.h>
|
|
7
7
|
|
|
8
|
+
#include <functional>
|
|
9
|
+
#include <initializer_list>
|
|
10
|
+
#include <memory>
|
|
8
11
|
#include <variant>
|
|
9
12
|
|
|
10
13
|
#include "../array.h"
|
|
14
|
+
#include "../bigint.h"
|
|
11
15
|
#include "../bytes.h"
|
|
12
16
|
#include "../date.h"
|
|
13
17
|
#include "../jsstring.h"
|
|
14
18
|
#include "../map.h"
|
|
15
19
|
#include "../native.h"
|
|
20
|
+
#include "../report.h"
|
|
21
|
+
|
|
22
|
+
// Lucent code checks the OS before using an API newer than the deployment
|
|
23
|
+
// target (LUCENT3007), with available(), which Clang does not see as a check:
|
|
24
|
+
// the glue that includes this header uses them unwarned.
|
|
25
|
+
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
|
26
|
+
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
|
|
16
27
|
|
|
17
28
|
namespace lucent::objc {
|
|
18
29
|
|
|
19
|
-
void
|
|
30
|
+
inline void releaseObject(void* retained) { CFRelease(retained); }
|
|
20
31
|
|
|
21
|
-
/// A Lucent reference to `obj` (retained)
|
|
32
|
+
/// A Lucent reference to `obj` (retained), released on the main thread:
|
|
33
|
+
/// UIKit objects must be deallocated there, and the last Lucent reference
|
|
34
|
+
/// can go on any thread. Throws TypeError for nil.
|
|
22
35
|
inline NativeRef wrap(id obj, const char* what) {
|
|
23
36
|
if (!obj) throw Exception(makeError(String::fromLatin1("TypeError"), String::fromUtf8(std::string(what) + " returned nil")));
|
|
24
|
-
return NativeRef((__bridge_retained void*)obj,
|
|
37
|
+
return NativeRef((__bridge_retained void*)obj, releaseObject, nullptr, &ExecutionContext::main());
|
|
25
38
|
}
|
|
26
39
|
inline Opt<NativeRef> wrapOpt(id obj) {
|
|
27
40
|
if (!obj) return Opt<NativeRef>(null);
|
|
28
|
-
return
|
|
41
|
+
return wrap(obj, "");
|
|
29
42
|
}
|
|
30
43
|
inline id unwrap(const NativeRef& r) { return (__bridge id)r.get(); }
|
|
31
44
|
inline id unwrap(const Opt<NativeRef>& r) { return r.has() ? (__bridge id)r.get().get() : nil; }
|
|
@@ -137,10 +150,14 @@ Opt<Dict<V>> fromNSDictionaryOpt(NSDictionary* d, F fromObject) {
|
|
|
137
150
|
return d ? Opt<Dict<V>>(fromNSDictionary<V>(d, fromObject, "")) : Opt<Dict<V>>(null);
|
|
138
151
|
}
|
|
139
152
|
|
|
140
|
-
/// Applies `f` to a value that may be absent; absent gives a null pointer
|
|
153
|
+
/// Applies `f` to a value that may be absent; absent gives a null pointer
|
|
154
|
+
/// (of f's result type: a nil object, block or CoreFoundation reference).
|
|
141
155
|
template <class T, class F>
|
|
142
156
|
auto ifPresent(const Opt<T>& v, F f) -> decltype(f(v.get())) {
|
|
143
|
-
|
|
157
|
+
using R = decltype(f(v.get()));
|
|
158
|
+
if (v.has()) return f(v.get());
|
|
159
|
+
|
|
160
|
+
return R(nullptr);
|
|
144
161
|
}
|
|
145
162
|
template <class T, class F>
|
|
146
163
|
requires(!IsOpt<T>::value)
|
|
@@ -197,15 +214,93 @@ inline Opt<Date> asDate(const Opt<NativeRef>& o) {
|
|
|
197
214
|
return [x isKindOfClass:[NSDate class]] ? Opt<Date>(fromNSDate(x, "")) : Opt<Date>(null);
|
|
198
215
|
}
|
|
199
216
|
|
|
217
|
+
// --- C structs, as Swift shims pass them: their bytes ----------------------------------
|
|
218
|
+
|
|
219
|
+
template <class S>
|
|
220
|
+
NSData* structBytes(const S& s) {
|
|
221
|
+
return [NSData dataWithBytes:&s length:sizeof(S)];
|
|
222
|
+
}
|
|
223
|
+
template <class S>
|
|
224
|
+
S structFromBytes(id data) {
|
|
225
|
+
S s{};
|
|
226
|
+
[(NSData*)data getBytes:&s length:sizeof(S)];
|
|
227
|
+
return s;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// --- Swift enums with payloads ---------------------------------------------------------
|
|
231
|
+
|
|
232
|
+
/// A case of a Swift enum with payloads, as the shims pass it: {"kind": case, field: payload…}.
|
|
233
|
+
inline id swiftCase(NSString* kind, std::initializer_list<std::pair<const char*, id>> payload) {
|
|
234
|
+
NSMutableDictionary* d = [NSMutableDictionary dictionaryWithObject:kind forKey:@"kind"];
|
|
235
|
+
// An absent optional payload is left out.
|
|
236
|
+
for (const auto& [field, value] : payload)
|
|
237
|
+
if (value) d[@(field)] = value;
|
|
238
|
+
return d;
|
|
239
|
+
}
|
|
240
|
+
inline id swiftCase(const char* kind, std::initializer_list<std::pair<const char*, id>> payload) {
|
|
241
|
+
return swiftCase(@(kind), payload);
|
|
242
|
+
}
|
|
243
|
+
/// Cases whose payloads have the same fields share an object type in Lucent: its kind says which.
|
|
244
|
+
inline id swiftCase(const String& kind, std::initializer_list<std::pair<const char*, id>> payload) {
|
|
245
|
+
return swiftCase(toNSString(kind), payload);
|
|
246
|
+
}
|
|
247
|
+
inline bool isSwiftCase(id c, const char* kind) {
|
|
248
|
+
return [((NSDictionary*)c)[@"kind"] isEqualToString:@(kind)];
|
|
249
|
+
}
|
|
250
|
+
/// A payload of a case, nil for an absent optional one (NSNull from Swift).
|
|
251
|
+
inline id swiftPayload(id c, const char* field) {
|
|
252
|
+
id v = ((NSDictionary*)c)[@(field)];
|
|
253
|
+
return v == [NSNull null] ? nil : v;
|
|
254
|
+
}
|
|
255
|
+
[[noreturn]] inline void unknownSwiftCase(id c, const char* type) {
|
|
256
|
+
throw Exception(makeError(String::fromLatin1("TypeError"),
|
|
257
|
+
String::fromUtf8(std::string(type) + " has no case " + [[((NSDictionary*)c)[@"kind"] description] UTF8String])));
|
|
258
|
+
}
|
|
259
|
+
|
|
200
260
|
// --- errors ----------------------------------------------------------------------------
|
|
201
261
|
|
|
202
|
-
///
|
|
262
|
+
/// The domain of the NSErrors Lucent errors become (toNSError).
|
|
263
|
+
inline NSString* const lucentErrorDomain = @"Lucent";
|
|
264
|
+
|
|
265
|
+
/// An NSError as a Lucent error: code "<domain>:<code>". One toNSError made
|
|
266
|
+
/// is the Lucent error it was made from again: its name, message and code.
|
|
203
267
|
inline Error fromNSError(NSError* e, const char* what) {
|
|
204
268
|
if (!e) throw Exception(makeError(String::fromLatin1("TypeError"), String::fromUtf8(std::string(what) + " got no error")));
|
|
269
|
+
|
|
270
|
+
NSString* name = [e.domain isEqualToString:lucentErrorDomain] ? e.userInfo[@"LucentErrorName"] : nil;
|
|
271
|
+
if (name) {
|
|
272
|
+
Error err = makeError(fromNSString(name, ""), fromNSString(e.localizedDescription ?: @"", ""));
|
|
273
|
+
if (NSString* code = e.userInfo[@"LucentErrorCode"]) err->code = fromNSString(code, "");
|
|
274
|
+
return err;
|
|
275
|
+
}
|
|
276
|
+
|
|
205
277
|
Error err = makeError(String::fromLatin1("Error"), fromNSString(e.localizedDescription ?: @"", ""));
|
|
206
278
|
err->code = fromNSString([NSString stringWithFormat:@"%@:%ld", e.domain, (long)e.code], "");
|
|
207
279
|
return err;
|
|
208
280
|
}
|
|
281
|
+
|
|
282
|
+
/// A Lucent error as an NSError, for Swift to throw where Lucent code
|
|
283
|
+
/// implements a throwing requirement: its message as the description, its
|
|
284
|
+
/// name and code kept for the way back (fromNSError).
|
|
285
|
+
inline NSError* toNSError(const Error& e) {
|
|
286
|
+
NSMutableDictionary* info = [NSMutableDictionary dictionary];
|
|
287
|
+
info[NSLocalizedDescriptionKey] = toNSString(e->message);
|
|
288
|
+
info[@"LucentErrorName"] = toNSString(e->name);
|
|
289
|
+
if (e->code.has()) info[@"LucentErrorCode"] = toNSString(e->code.get());
|
|
290
|
+
|
|
291
|
+
return [NSError errorWithDomain:lucentErrorDomain code:0 userInfo:info];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/// In a catch: what was thrown, as the NSError a Swift requirement's error
|
|
295
|
+
/// out-parameter takes (retained).
|
|
296
|
+
inline void setError(void** out) {
|
|
297
|
+
*out = (__bridge_retained void*)toNSError(currentError(std::current_exception()));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/// Reports an error Swift has no way to receive (a requirement that does not throw).
|
|
301
|
+
inline void reportError(const Error& e, const char* where) {
|
|
302
|
+
reportUncaught(std::make_exception_ptr(Exception(e)), where);
|
|
303
|
+
}
|
|
209
304
|
inline Opt<Error> fromNSErrorOpt(NSError* e) { return e ? Opt<Error>(fromNSError(e, "")) : Opt<Error>(null); }
|
|
210
305
|
|
|
211
306
|
/// A method's NSError** result as a thrown Lucent error.
|
|
@@ -216,8 +311,10 @@ inline void throwIfError(NSError* e) {
|
|
|
216
311
|
// --- blocks ----------------------------------------------------------------------------
|
|
217
312
|
|
|
218
313
|
/// One Objective-C object per `key` (a Lucent object) while the object is
|
|
219
|
-
/// alive: `make` creates it. Callers hold the Lucent lock.
|
|
220
|
-
|
|
314
|
+
/// alive: `make` creates it. Callers hold the Lucent lock. Each `Table`
|
|
315
|
+
/// has its own: a Lucent object's Objective-C and Swift delegates differ.
|
|
316
|
+
template <int Table = 0>
|
|
317
|
+
id cachedObject(const void* key, id (^make)(void)) {
|
|
221
318
|
static NSMapTable* objects = [NSMapTable strongToWeakObjectsMapTable];
|
|
222
319
|
NSValue* k = [NSValue valueWithPointer:key];
|
|
223
320
|
id o = [objects objectForKey:k];
|
|
@@ -228,6 +325,43 @@ inline id cachedObject(const void* key, id (^make)(void)) {
|
|
|
228
325
|
return o;
|
|
229
326
|
}
|
|
230
327
|
|
|
328
|
+
// --- Lucent classes extending Objective-C classes ------------------------------------
|
|
329
|
+
//
|
|
330
|
+
// A generated subclass of the base stands for each instance: it holds the
|
|
331
|
+
// Lucent object, and Lucent references to the object hold it (nativeOwned).
|
|
332
|
+
|
|
333
|
+
/// The native object of each Lucent object extending an Objective-C class,
|
|
334
|
+
/// by the Lucent object's address; held weakly, as the native one holds it.
|
|
335
|
+
inline NSMapTable* subclassObjects() {
|
|
336
|
+
static NSMapTable* objects = [NSMapTable strongToWeakObjectsMapTable];
|
|
337
|
+
return objects;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
inline void adoptSubclassObject(const void* lucent, id object) {
|
|
341
|
+
[subclassObjects() setObject:object forKey:[NSValue valueWithPointer:lucent]];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
inline id subclassObject(const void* lucent) {
|
|
345
|
+
return [subclassObjects() objectForKey:[NSValue valueWithPointer:lucent]];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/// The native object `lucent` is, for the members it inherits.
|
|
349
|
+
inline NativeRef nativeOfSubclass(const void* lucent, const char* what) {
|
|
350
|
+
id o = subclassObject(lucent);
|
|
351
|
+
if (!o) throw Exception(makeError(String::fromLatin1("TypeError"), String::fromUtf8(std::string(what) + ": its native object is gone")));
|
|
352
|
+
return wrap(o, what);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/// A reference to `o` that holds its native object, which holds `o`: an
|
|
356
|
+
/// aliasing reference, so the two go together. Before the native object is
|
|
357
|
+
/// made (in the constructor, before super(…)), the Lucent object's own.
|
|
358
|
+
template <class T>
|
|
359
|
+
Ref<T> nativeOwned(T* o) {
|
|
360
|
+
id x = subclassObject(o);
|
|
361
|
+
if (!x) return std::static_pointer_cast<T>(static_cast<Object*>(o)->shared_from_this());
|
|
362
|
+
return Ref<T>(std::make_shared<NativeRef>(wrap(x, "")), o);
|
|
363
|
+
}
|
|
364
|
+
|
|
231
365
|
/// A C++ callable as a heap block of type `Block` (the platform may keep it).
|
|
232
366
|
template <class Block, class F>
|
|
233
367
|
Block block(F f) {
|
|
@@ -237,16 +371,25 @@ Block block(F f) {
|
|
|
237
371
|
|
|
238
372
|
// --- out-parameters --------------------------------------------------------------------
|
|
239
373
|
|
|
240
|
-
/// `new Out()` from lucent:ios: a
|
|
374
|
+
/// `new Out()` from lucent:ios: what a method writes through a pointer, and
|
|
375
|
+
/// (for inout pointers) reads first. An object as a CFTypeRef; a number, an
|
|
376
|
+
/// enum or a BOOL as a number; a 64-bit integer as a bigint; a C struct as
|
|
377
|
+
/// the Lucent object it converts to.
|
|
241
378
|
struct OutSlot {
|
|
242
379
|
CFTypeRef value = nullptr;
|
|
243
380
|
bool owned = false;
|
|
381
|
+
Opt<double> number = Opt<double>(null);
|
|
382
|
+
Opt<BigInt> integer = Opt<BigInt>(null);
|
|
383
|
+
std::shared_ptr<Object> record;
|
|
244
384
|
~OutSlot() {
|
|
245
385
|
if (value && owned) CFRelease(value);
|
|
246
386
|
}
|
|
247
387
|
};
|
|
388
|
+
inline OutSlot* slotOf(const NativeRef& r) { return static_cast<OutSlot*>(r.get()); }
|
|
389
|
+
inline OutSlot* slotOf(const Opt<NativeRef>& r) { return r.has() ? slotOf(r.get()) : nullptr; }
|
|
390
|
+
/// Released on the main thread, like the object it may hold.
|
|
248
391
|
inline NativeRef makeOut() {
|
|
249
|
-
return NativeRef(new OutSlot(), [](void* p) { delete static_cast<OutSlot*>(p); }, nullptr);
|
|
392
|
+
return NativeRef(new OutSlot(), [](void* p) { delete static_cast<OutSlot*>(p); }, nullptr, &ExecutionContext::main());
|
|
250
393
|
}
|
|
251
394
|
/// The slot for a call; `owned` per CoreFoundation's Create/Copy rule.
|
|
252
395
|
inline CFTypeRef* outSlot(const NativeRef& r, bool owned) {
|
|
@@ -257,33 +400,190 @@ inline CFTypeRef* outSlot(const NativeRef& r, bool owned) {
|
|
|
257
400
|
return &s->value;
|
|
258
401
|
}
|
|
259
402
|
inline CFTypeRef* outSlot(const Opt<NativeRef>& r, bool owned) { return r.has() ? outSlot(r.get(), owned) : nullptr; }
|
|
260
|
-
inline Opt<NativeRef> outValue(const NativeRef& r) { return wrapOpt((__bridge id)
|
|
403
|
+
inline Opt<NativeRef> outValue(const NativeRef& r) { return wrapOpt((__bridge id) slotOf(r)->value); }
|
|
261
404
|
|
|
262
405
|
/**
|
|
263
|
-
* An
|
|
264
|
-
* autoreleased
|
|
265
|
-
* expression ends (before any autorelease pool
|
|
406
|
+
* An object pointer argument (`NSDate **`, `NSError **`) writing into an
|
|
407
|
+
* Out: the method writes an autoreleased object, which the slot retains
|
|
408
|
+
* when the call's full expression ends (before any autorelease pool
|
|
409
|
+
* drains). `T` is the pointee, `NSDate*` or `id`.
|
|
266
410
|
*/
|
|
267
|
-
class
|
|
411
|
+
template <class T>
|
|
412
|
+
class ObjectOut {
|
|
268
413
|
public:
|
|
269
|
-
explicit
|
|
270
|
-
explicit
|
|
271
|
-
~
|
|
414
|
+
explicit ObjectOut(const NativeRef& out) : ObjectOut(slotOf(out)) {}
|
|
415
|
+
explicit ObjectOut(const Opt<NativeRef>& out) : ObjectOut(slotOf(out)) {}
|
|
416
|
+
~ObjectOut() {
|
|
272
417
|
if (!slot_) return;
|
|
273
418
|
if (slot_->value && slot_->owned) CFRelease(slot_->value);
|
|
274
|
-
slot_->value =
|
|
419
|
+
slot_->value = object_ ? CFBridgingRetain(object_) : nullptr;
|
|
275
420
|
slot_->owned = true;
|
|
276
421
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
422
|
+
ObjectOut(const ObjectOut&) = delete;
|
|
423
|
+
ObjectOut& operator=(const ObjectOut&) = delete;
|
|
424
|
+
T __autoreleasing* ptr() { return slot_ ? reinterpret_cast<T __autoreleasing*>(&object_) : nullptr; }
|
|
425
|
+
|
|
426
|
+
private:
|
|
427
|
+
explicit ObjectOut(OutSlot* slot) : slot_(slot), object_(slot ? (__bridge T)slot->value : nil) {}
|
|
428
|
+
OutSlot* slot_;
|
|
429
|
+
T __unsafe_unretained object_;
|
|
430
|
+
};
|
|
431
|
+
using ErrorOut = ObjectOut<NSError*>;
|
|
432
|
+
|
|
433
|
+
/// A 64-bit integer (NSInteger, int64_t, NSUInteger): a bigint in Lucent.
|
|
434
|
+
template <class T>
|
|
435
|
+
constexpr bool kWideInteger = std::is_integral_v<T> && sizeof(T) == 8;
|
|
436
|
+
|
|
437
|
+
/// A number, enum or BOOL pointer argument (`CGFloat *`): the slot's number
|
|
438
|
+
/// goes in, and the method's comes out; a 64-bit integer's (`NSInteger *`)
|
|
439
|
+
/// as a bigint, exactly.
|
|
440
|
+
template <class T>
|
|
441
|
+
class NumberOut {
|
|
442
|
+
public:
|
|
443
|
+
explicit NumberOut(const NativeRef& out) : NumberOut(slotOf(out)) {}
|
|
444
|
+
explicit NumberOut(const Opt<NativeRef>& out) : NumberOut(slotOf(out)) {}
|
|
445
|
+
|
|
446
|
+
~NumberOut() {
|
|
447
|
+
if (!slot_) return;
|
|
448
|
+
|
|
449
|
+
if constexpr (kWideInteger<T>) slot_->integer = Opt<BigInt>(BigInt(value_));
|
|
450
|
+
else slot_->number = Opt<double>(static_cast<double>(value_));
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
NumberOut(const NumberOut&) = delete;
|
|
454
|
+
NumberOut& operator=(const NumberOut&) = delete;
|
|
455
|
+
T* ptr() { return slot_ ? &value_ : nullptr; }
|
|
456
|
+
|
|
457
|
+
private:
|
|
458
|
+
static T in(OutSlot* slot) {
|
|
459
|
+
if constexpr (kWideInteger<T>) {
|
|
460
|
+
return slot && slot->integer.has() ? toNativeInteger<T>(slot->integer.get(), "an Out's value") : T{};
|
|
461
|
+
} else {
|
|
462
|
+
return slot && slot->number.has() ? static_cast<T>(slot->number.get()) : T{};
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
explicit NumberOut(OutSlot* slot) : slot_(slot), value_(in(slot)) {}
|
|
467
|
+
|
|
468
|
+
OutSlot* slot_;
|
|
469
|
+
T value_;
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
/// A C struct pointer argument (`NSRange *`), converted to and from the Lucent object the slot holds.
|
|
473
|
+
template <class T>
|
|
474
|
+
class StructOut {
|
|
475
|
+
public:
|
|
476
|
+
using ToC = std::function<T(const std::shared_ptr<Object>&)>;
|
|
477
|
+
using FromC = std::function<std::shared_ptr<Object>(const T&)>;
|
|
478
|
+
StructOut(const NativeRef& out, ToC toC, FromC fromC) : StructOut(slotOf(out), toC, std::move(fromC)) {}
|
|
479
|
+
StructOut(const Opt<NativeRef>& out, ToC toC, FromC fromC) : StructOut(slotOf(out), toC, std::move(fromC)) {}
|
|
480
|
+
~StructOut() {
|
|
481
|
+
if (slot_) slot_->record = fromC_(value_);
|
|
482
|
+
}
|
|
483
|
+
StructOut(const StructOut&) = delete;
|
|
484
|
+
StructOut& operator=(const StructOut&) = delete;
|
|
485
|
+
T* ptr() { return slot_ ? &value_ : nullptr; }
|
|
280
486
|
|
|
281
487
|
private:
|
|
488
|
+
StructOut(OutSlot* slot, const ToC& toC, FromC fromC)
|
|
489
|
+
: slot_(slot), value_(slot && slot->record ? toC(slot->record) : T{}), fromC_(std::move(fromC)) {}
|
|
282
490
|
OutSlot* slot_;
|
|
283
|
-
|
|
491
|
+
T value_;
|
|
492
|
+
FromC fromC_;
|
|
284
493
|
};
|
|
285
494
|
|
|
286
|
-
inline Opt<Error> outError(const NativeRef& r) { return fromNSErrorOpt((__bridge NSError*)
|
|
495
|
+
inline Opt<Error> outError(const NativeRef& r) { return fromNSErrorOpt((__bridge NSError*) slotOf(r)->value); }
|
|
496
|
+
inline Opt<double> outNumber(const NativeRef& r) { return slotOf(r)->number; }
|
|
497
|
+
inline Opt<BigInt> outBigInt(const NativeRef& r) { return slotOf(r)->integer; }
|
|
498
|
+
inline Opt<bool> outBool(const NativeRef& r) {
|
|
499
|
+
const Opt<double>& n = slotOf(r)->number;
|
|
500
|
+
return n.has() ? Opt<bool>(n.get() != 0) : Opt<bool>(null);
|
|
501
|
+
}
|
|
502
|
+
/// The struct a method wrote, as the Lucent object type `S` it converts to.
|
|
503
|
+
template <class S>
|
|
504
|
+
Opt<Ref<S>> outRecord(const NativeRef& r) {
|
|
505
|
+
const auto& o = slotOf(r)->record;
|
|
506
|
+
return o ? Opt<Ref<S>>(std::static_pointer_cast<S>(o)) : Opt<Ref<S>>(null);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
inline void setOutObject(const NativeRef& r, id v) {
|
|
510
|
+
OutSlot* s = slotOf(r);
|
|
511
|
+
if (s->value && s->owned) CFRelease(s->value);
|
|
512
|
+
s->value = v ? CFBridgingRetain(v) : nullptr;
|
|
513
|
+
s->owned = true;
|
|
514
|
+
}
|
|
515
|
+
/// A pointer a block receives (`BOOL *stop`) as an Out, holding what it points to.
|
|
516
|
+
template <class T>
|
|
517
|
+
NativeRef outOf(T* p) {
|
|
518
|
+
NativeRef out = makeOut();
|
|
519
|
+
if (!p) return out;
|
|
520
|
+
|
|
521
|
+
if constexpr (kWideInteger<T>) slotOf(out)->integer = Opt<BigInt>(BigInt(*p));
|
|
522
|
+
else slotOf(out)->number = Opt<double>(static_cast<double>(*p));
|
|
523
|
+
return out;
|
|
524
|
+
}
|
|
525
|
+
/// What the Lucent function set, written back through the block's pointer.
|
|
526
|
+
template <class T>
|
|
527
|
+
void writeOut(const NativeRef& out, T* p) {
|
|
528
|
+
if (!p) return;
|
|
529
|
+
|
|
530
|
+
if constexpr (kWideInteger<T>) {
|
|
531
|
+
const Opt<BigInt>& n = slotOf(out)->integer;
|
|
532
|
+
if (n.has()) *p = toNativeInteger<T>(n.get(), "an Out's value");
|
|
533
|
+
} else {
|
|
534
|
+
const Opt<double>& n = slotOf(out)->number;
|
|
535
|
+
if (n.has()) *p = static_cast<T>(n.get());
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// `out.value = x`: what an inout pointer passes in. Each gives the value assigned.
|
|
540
|
+
inline double setOut(const NativeRef& r, double v) {
|
|
541
|
+
slotOf(r)->number = Opt<double>(v);
|
|
542
|
+
return v;
|
|
543
|
+
}
|
|
544
|
+
inline BigInt setOut(const NativeRef& r, BigInt v) {
|
|
545
|
+
slotOf(r)->integer = Opt<BigInt>(v);
|
|
546
|
+
return v;
|
|
547
|
+
}
|
|
548
|
+
inline bool setOut(const NativeRef& r, bool v) {
|
|
549
|
+
slotOf(r)->number = Opt<double>(v ? 1.0 : 0.0);
|
|
550
|
+
return v;
|
|
551
|
+
}
|
|
552
|
+
template <class S>
|
|
553
|
+
Ref<S> setOut(const NativeRef& r, Ref<S> v) {
|
|
554
|
+
slotOf(r)->record = std::static_pointer_cast<Object>(v);
|
|
555
|
+
return v;
|
|
556
|
+
}
|
|
557
|
+
inline NativeRef setOut(const NativeRef& r, NativeRef v) {
|
|
558
|
+
setOutObject(r, unwrap(v));
|
|
559
|
+
return v;
|
|
560
|
+
}
|
|
561
|
+
inline String setOut(const NativeRef& r, String v) {
|
|
562
|
+
setOutObject(r, toNSString(v));
|
|
563
|
+
return v;
|
|
564
|
+
}
|
|
565
|
+
inline Date setOut(const NativeRef& r, Date v) {
|
|
566
|
+
setOutObject(r, toNSDate(v));
|
|
567
|
+
return v;
|
|
568
|
+
}
|
|
569
|
+
inline Bytes setOut(const NativeRef& r, Bytes v) {
|
|
570
|
+
setOutObject(r, toNSData(v));
|
|
571
|
+
return v;
|
|
572
|
+
}
|
|
573
|
+
template <class T>
|
|
574
|
+
Opt<T> setOut(const NativeRef& r, Opt<T> v) {
|
|
575
|
+
if (v.has()) {
|
|
576
|
+
setOut(r, v.get());
|
|
577
|
+
} else {
|
|
578
|
+
OutSlot* s = slotOf(r);
|
|
579
|
+
if (s->value && s->owned) CFRelease(s->value);
|
|
580
|
+
s->value = nullptr;
|
|
581
|
+
s->number = Opt<double>(null);
|
|
582
|
+
s->integer = Opt<BigInt>(null);
|
|
583
|
+
s->record.reset();
|
|
584
|
+
}
|
|
585
|
+
return v;
|
|
586
|
+
}
|
|
287
587
|
|
|
288
588
|
/// `mainQueue()` from lucent:ios: the main dispatch queue (an OS object).
|
|
289
589
|
inline NativeRef mainQueue() { return wrap(dispatch_get_main_queue(), "mainQueue"); }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Lucent runtime — iOS: the main queue
|
|
1
|
+
// Lucent runtime — iOS: the main queue.
|
|
2
2
|
#include "ios.h"
|
|
3
3
|
|
|
4
4
|
#import <dispatch/dispatch.h>
|
|
@@ -15,17 +15,4 @@ void postToMain(std::function<void()> job) {
|
|
|
15
15
|
|
|
16
16
|
bool onMainThread() { return [NSThread isMainThread]; }
|
|
17
17
|
|
|
18
|
-
namespace objc {
|
|
19
|
-
|
|
20
|
-
// UIKit objects must be deallocated on the main thread; the last Lucent
|
|
21
|
-
// reference can go on any thread.
|
|
22
|
-
void releaseOnMain(void* retained) {
|
|
23
|
-
if ([NSThread isMainThread]) {
|
|
24
|
-
CFRelease(retained);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
dispatch_async_f(dispatch_get_main_queue(), retained, [](void* p) { CFRelease(p); });
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
} // namespace objc
|
|
31
18
|
} // namespace lucent
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// Lucent runtime — UIKit glue: the scene to present from, presentations,
|
|
2
|
+
// and lifecycle subscriptions, for *.ios.lucent.ts units and native
|
|
3
|
+
// wrappers (Objective-C++ with ARC, iOS only).
|
|
4
|
+
//
|
|
5
|
+
// Nothing here keeps a window or view controller: the presentation context
|
|
6
|
+
// is looked up in UIKit's scenes each time it is asked for, and scenes are
|
|
7
|
+
// named by SceneIds (lucent/lifecycle.h). UIKit's notifications feed the
|
|
8
|
+
// shared Lifecycle from the moment the binary loads; no app or scene
|
|
9
|
+
// delegate is replaced, so the app's own, and other modules', keep working.
|
|
10
|
+
#pragma once
|
|
11
|
+
|
|
12
|
+
#import <UIKit/UIKit.h>
|
|
13
|
+
|
|
14
|
+
#include <functional>
|
|
15
|
+
#include <memory>
|
|
16
|
+
#include <optional>
|
|
17
|
+
#include <type_traits>
|
|
18
|
+
|
|
19
|
+
#include "../lifecycle.h"
|
|
20
|
+
#include "../presentation.h"
|
|
21
|
+
#include "ios.h"
|
|
22
|
+
|
|
23
|
+
namespace lucent::objc {
|
|
24
|
+
|
|
25
|
+
/// Where to present from, as UIKit has it now.
|
|
26
|
+
struct PresentationContext {
|
|
27
|
+
SceneId id = 0;
|
|
28
|
+
UIWindowScene* scene = nil;
|
|
29
|
+
UIWindow* window = nil;
|
|
30
|
+
/// The key window's root view controller, or the view controller it
|
|
31
|
+
/// presents (and so on), minus one being dismissed.
|
|
32
|
+
UIViewController* top = nil;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The presentation context of the scene the person is using: the most
|
|
37
|
+
* recently activated window scene in the foreground (active before
|
|
38
|
+
* inactive), its key window and top view controller. None while every
|
|
39
|
+
* scene is in the background, or it has no window with a root view
|
|
40
|
+
* controller. On the main thread only (InvalidStateError elsewhere). Keep
|
|
41
|
+
* none of it past the current turn of the main thread: ask again.
|
|
42
|
+
*/
|
|
43
|
+
std::optional<PresentationContext> presentationContext();
|
|
44
|
+
|
|
45
|
+
/// The id of a connected scene (given when UIKit connects it, or when it
|
|
46
|
+
/// is first seen); 0 for nil. Main thread.
|
|
47
|
+
SceneId sceneIdOf(UIScene* scene);
|
|
48
|
+
|
|
49
|
+
/// The connected scene with that id, else nil. Main thread.
|
|
50
|
+
UIScene* sceneOf(SceneId id);
|
|
51
|
+
|
|
52
|
+
namespace detail {
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Presents `viewController` from the top view controller of `scene`'s key
|
|
56
|
+
* window, and returns what dismisses it: at once if it is shown, else once
|
|
57
|
+
* UIKit has shown it (some view controllers appear later than the call).
|
|
58
|
+
* `dismissed` runs once if, before that, the person dismisses it (a sheet
|
|
59
|
+
* swiped down) or it is released. Throws InvalidStateError if it is
|
|
60
|
+
* already shown, another presentation is under way, or UIKit throws.
|
|
61
|
+
* Main thread.
|
|
62
|
+
*/
|
|
63
|
+
std::function<void()> show(SceneId scene, UIViewController* viewController, bool animated, std::function<void()> dismissed);
|
|
64
|
+
|
|
65
|
+
/// What cancels a presentation the person dismissed: an AbortError.
|
|
66
|
+
Error dismissedError();
|
|
67
|
+
|
|
68
|
+
/// The root scope of the calling context: what Lucent code's presentations
|
|
69
|
+
/// and subscriptions belong to.
|
|
70
|
+
std::shared_ptr<Scope> callerScope();
|
|
71
|
+
|
|
72
|
+
} // namespace detail
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* For native wrappers: presents, on the main thread, the view controller
|
|
76
|
+
* `build` returns, from the presentation context now, as an operation under
|
|
77
|
+
* `scope` (lucent/presentation.h: it settles once, and whatever settles it
|
|
78
|
+
* dismisses the view controller). `build` runs here, before anything is
|
|
79
|
+
* shown, and wires the view controller's own outcome (a delegate, a
|
|
80
|
+
* completion handler) to the operation; it may settle it at once, and then
|
|
81
|
+
* nothing is shown. The person dismissing it, or its release, cancels the
|
|
82
|
+
* operation with an AbortError; so does its scene disconnecting.
|
|
83
|
+
*/
|
|
84
|
+
template <class T>
|
|
85
|
+
std::shared_ptr<Operation<T>> presentOperation(const std::shared_ptr<Scope>& scope, Opt<AbortSignal> signal,
|
|
86
|
+
const std::function<UIViewController*(const std::shared_ptr<Operation<T>>&)>& build,
|
|
87
|
+
bool animated = true) {
|
|
88
|
+
std::optional<PresentationContext> context = presentationContext();
|
|
89
|
+
SceneId scene = context ? context->id : 0;
|
|
90
|
+
|
|
91
|
+
return presentIn<T>(Lifecycle::shared(), scene, scope, signal, [&](const std::shared_ptr<Operation<T>>& op) -> std::function<void()> {
|
|
92
|
+
UIViewController* viewController = build(op);
|
|
93
|
+
if (op->state() != OperationState::Pending) return {};
|
|
94
|
+
if (!viewController) returnedNil("present's view controller");
|
|
95
|
+
|
|
96
|
+
std::weak_ptr<Operation<T>> weak = op;
|
|
97
|
+
return detail::show(scene, viewController, animated, [weak] {
|
|
98
|
+
if (auto op = weak.lock()) op->cancel(detail::dismissedError());
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
namespace detail {
|
|
104
|
+
template <class T>
|
|
105
|
+
struct ResolveOf {
|
|
106
|
+
using type = Fn<void(T)>;
|
|
107
|
+
};
|
|
108
|
+
template <>
|
|
109
|
+
struct ResolveOf<void> {
|
|
110
|
+
using type = Fn<void()>;
|
|
111
|
+
};
|
|
112
|
+
} // namespace detail
|
|
113
|
+
|
|
114
|
+
/// What `present` gives its function to settle with a value.
|
|
115
|
+
template <class T>
|
|
116
|
+
using Resolve = typename detail::ResolveOf<T>::type;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* `present(build, signal)` from lucent:ios: on the main thread, holding the
|
|
120
|
+
* Lucent lock (build is module code), presents the view controller `build`
|
|
121
|
+
* returns (presentOperation) under the calling context's root scope, and
|
|
122
|
+
* settles the promise, which belongs to the calling context, with the
|
|
123
|
+
* outcome.
|
|
124
|
+
*/
|
|
125
|
+
template <class T>
|
|
126
|
+
Promise<T> present(Fn<NativeRef(Resolve<T>, Fn<void(Error)>)> build, Opt<AbortSignal> signal = {}) {
|
|
127
|
+
Promise<T> promise;
|
|
128
|
+
std::shared_ptr<Scope> scope = detail::callerScope();
|
|
129
|
+
|
|
130
|
+
postToMain([promise, build = std::move(build), signal, scope]() mutable {
|
|
131
|
+
LucentScope lucent;
|
|
132
|
+
|
|
133
|
+
std::shared_ptr<Operation<T>> op;
|
|
134
|
+
try {
|
|
135
|
+
op = presentOperation<T>(scope, signal, [&](const std::shared_ptr<Operation<T>>& op) -> UIViewController* {
|
|
136
|
+
std::weak_ptr<Operation<T>> weak = op;
|
|
137
|
+
|
|
138
|
+
Resolve<T> resolve;
|
|
139
|
+
if constexpr (std::is_void_v<T>) {
|
|
140
|
+
resolve = Resolve<T>([weak] {
|
|
141
|
+
if (auto op = weak.lock()) op->succeed();
|
|
142
|
+
});
|
|
143
|
+
} else {
|
|
144
|
+
resolve = Resolve<T>([weak](T value) {
|
|
145
|
+
if (auto op = weak.lock()) op->succeed(std::move(value));
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
Fn<void(Error)> reject([weak](Error error) {
|
|
150
|
+
if (auto op = weak.lock()) op->fail(std::move(error));
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
id made = unwrap(build(resolve, reject));
|
|
154
|
+
if (made && ![made isKindOfClass:[UIViewController class]])
|
|
155
|
+
throw Exception(makeError(String::fromLatin1("TypeError"), String::fromLatin1("present's function must return a UIViewController")));
|
|
156
|
+
|
|
157
|
+
return (UIViewController*)made;
|
|
158
|
+
});
|
|
159
|
+
} catch (...) {
|
|
160
|
+
promise.reject(currentError(std::current_exception()));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
op->onSettled([promise](const typename Operation<T>::Outcome& outcome) {
|
|
165
|
+
if (outcome.state != OperationState::Succeeded) {
|
|
166
|
+
promise.reject(outcome.error);
|
|
167
|
+
} else if constexpr (std::is_void_v<T>) {
|
|
168
|
+
promise.resolve(undefined);
|
|
169
|
+
} else {
|
|
170
|
+
promise.resolve(*outcome.value);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return promise;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* `onAppEvent(event, listener, signal)` from lucent:ios: calls `listener`
|
|
180
|
+
* on each of UIApplication's `event` notifications ("didBecomeActive"…),
|
|
181
|
+
* on the main thread holding the Lucent lock, until the returned function
|
|
182
|
+
* is called or `signal` aborts. What it throws is reported; UIKit and the
|
|
183
|
+
* other observers go on.
|
|
184
|
+
*/
|
|
185
|
+
Fn<void()> onAppEvent(const String& event, Fn<void()> listener, Opt<AbortSignal> signal = {});
|
|
186
|
+
|
|
187
|
+
/// `onSceneEvent(event, listener, signal)` from lucent:ios: as onAppEvent,
|
|
188
|
+
/// for UIScene's notifications ("willConnect"…); `listener` gets the
|
|
189
|
+
/// scene's session's persistent identifier.
|
|
190
|
+
Fn<void()> onSceneEvent(const String& event, Fn<void(String)> listener, Opt<AbortSignal> signal = {});
|
|
191
|
+
|
|
192
|
+
} // namespace lucent::objc
|