expo-modules-core 3.0.23 → 3.0.24
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/CHANGELOG.md +11 -0
- package/android/build.gradle +2 -2
- package/ios/Api/Builders/ViewDefinitionBuilder.swift +1 -1
- package/ios/AppDelegates/ExpoAppDelegateSubscriber.swift +8 -0
- package/ios/AppDelegates/ExpoAppDelegateSubscriberRepository.swift +1 -0
- package/ios/Core/Arguments/Either.swift +5 -0
- package/ios/Core/DynamicTypes/DynamicEitherType.swift +17 -0
- package/ios/Core/Records/Record.swift +14 -1
- package/ios/Core/Views/SwiftUI/SwiftUIViewDefinition.swift +3 -4
- package/ios/Core/Views/SwiftUI/SwiftUIViewDefinitionBuilder.swift +7 -0
- package/ios/Core/Views/SwiftUI/SwiftUIViewProps.swift +3 -2
- package/ios/Tests/FunctionSpec.swift +15 -1
- package/ios/Tests/RecordSpec.swift +25 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 3.0.24 — 2025-11-03
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- [iOS] Added `subscriberDidRegister` function to AppDelegate subscribers. ([#40684](https://github.com/expo/expo/pull/40684) by [@tsapeta](https://github.com/tsapeta))
|
|
18
|
+
|
|
19
|
+
### 🐛 Bug fixes
|
|
20
|
+
|
|
21
|
+
- [iOS] Fix `Either` conversion in `Record` ([#40655](https://github.com/expo/expo/pull/40655) by [@vonovak](https://github.com/vonovak))
|
|
22
|
+
|
|
13
23
|
## 3.0.23 — 2025-10-28
|
|
14
24
|
|
|
15
25
|
### 🎉 New features
|
|
@@ -25,6 +35,7 @@
|
|
|
25
35
|
### 💡 Others
|
|
26
36
|
|
|
27
37
|
- [iOS] Removed some runtime type checks for dynamic types. ([#40611](https://github.com/expo/expo/pull/40611) by [@tsapeta](https://github.com/tsapeta))
|
|
38
|
+
- [iOS] Added base props support for SwiftUI integration. ([#40492](https://github.com/expo/expo/pull/40492) by [@kudo](https://github.com/kudo))
|
|
28
39
|
|
|
29
40
|
## 3.0.22 — 2025-10-20
|
|
30
41
|
|
package/android/build.gradle
CHANGED
|
@@ -29,7 +29,7 @@ if (shouldIncludeCompose) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
group = 'host.exp.exponent'
|
|
32
|
-
version = '3.0.
|
|
32
|
+
version = '3.0.24'
|
|
33
33
|
|
|
34
34
|
def isExpoModulesCoreTests = {
|
|
35
35
|
Gradle gradle = getGradle()
|
|
@@ -79,7 +79,7 @@ android {
|
|
|
79
79
|
defaultConfig {
|
|
80
80
|
consumerProguardFiles 'proguard-rules.pro'
|
|
81
81
|
versionCode 1
|
|
82
|
-
versionName "3.0.
|
|
82
|
+
versionName "3.0.24"
|
|
83
83
|
buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
|
|
84
84
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
|
|
85
85
|
|
|
@@ -15,7 +15,7 @@ public struct ViewDefinitionBuilder<ViewType: UIView> {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
Accepts `
|
|
18
|
+
Accepts `ViewName` definition element of `View`.
|
|
19
19
|
*/
|
|
20
20
|
public static func buildExpression(_ element: ViewNameDefinition) -> AnyViewDefinitionElement {
|
|
21
21
|
return element
|
|
@@ -24,6 +24,14 @@ open class BaseExpoAppDelegateSubscriber: UIResponder {
|
|
|
24
24
|
@objc(EXAppDelegateSubscriberProtocol)
|
|
25
25
|
public protocol ExpoAppDelegateSubscriberProtocol: UIApplicationDelegate {
|
|
26
26
|
@objc optional func customizeRootView(_ rootView: UIView)
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
Function that is called automatically by the `ExpoAppDelegate` once the subscriber is successfully registered.
|
|
30
|
+
If the subscriber is loaded from the modules provider, it is executed just before the main code of the app,
|
|
31
|
+
thus even before any other `UIApplicationDelegate` function. Use it if your subscriber needs to run some code as early as possible,
|
|
32
|
+
but keep in mind that this affects the application loading time.
|
|
33
|
+
*/
|
|
34
|
+
@objc optional func subscriberDidRegister()
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
/**
|
|
@@ -32,6 +32,7 @@ public class ExpoAppDelegateSubscriberRepository: NSObject {
|
|
|
32
32
|
fatalError("Given app delegate subscriber `\(String(describing: subscriber))` is already registered.")
|
|
33
33
|
}
|
|
34
34
|
_subscribers.append(subscriber)
|
|
35
|
+
subscriber.subscriberDidRegister?()
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
@objc
|
|
@@ -40,6 +40,23 @@ internal struct DynamicEitherType<EitherType: AnyEither>: AnyDynamicType {
|
|
|
40
40
|
throw NeitherTypeException(types)
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
func convertResult<ResultType>(_ result: ResultType, appContext: AppContext) throws -> Any {
|
|
44
|
+
guard let either = result as? EitherType else {
|
|
45
|
+
throw Conversions.CastingException<EitherType>(result)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let types = eitherType.dynamicTypes()
|
|
49
|
+
|
|
50
|
+
// Try each type - one should succeed
|
|
51
|
+
for type in types {
|
|
52
|
+
if let converted = try? type.convertResult(either.value, appContext: appContext) {
|
|
53
|
+
return converted
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
throw NeitherTypeException(types)
|
|
58
|
+
}
|
|
59
|
+
|
|
43
60
|
var description: String {
|
|
44
61
|
let types = eitherType.dynamicTypes()
|
|
45
62
|
return "Either<\(types.map(\.description).joined(separator: ", "))>"
|
|
@@ -68,12 +68,25 @@ public extension Record {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
Recursively collects all children from a Mirror, including inherited properties from superclasses.
|
|
73
|
+
*/
|
|
74
|
+
internal func allMirrorChildren(_ mirror: Mirror) -> [Mirror.Child] {
|
|
75
|
+
var children: [Mirror.Child] = Array(mirror.children)
|
|
76
|
+
if let superclassMirror = mirror.superclassMirror {
|
|
77
|
+
children.append(contentsOf: allMirrorChildren(superclassMirror))
|
|
78
|
+
}
|
|
79
|
+
return children
|
|
80
|
+
}
|
|
81
|
+
|
|
71
82
|
/**
|
|
72
83
|
Returns an array of fields found in record's mirror. If the field is missing the `key`,
|
|
73
84
|
it gets assigned to the property label, so after all it's safe to enforce unwrapping it (using `key!`).
|
|
85
|
+
This function now supports inheritance by recursively traversing the superclass hierarchy.
|
|
74
86
|
*/
|
|
75
87
|
internal func fieldsOf(_ record: Record) -> [AnyFieldInternal] {
|
|
76
|
-
|
|
88
|
+
let mirror = Mirror(reflecting: record)
|
|
89
|
+
return allMirrorChildren(mirror).compactMap { (label: String?, value: Any) in
|
|
77
90
|
guard var field = value as? AnyFieldInternal, let key = field.key ?? convertLabelToKey(label) else {
|
|
78
91
|
return nil
|
|
79
92
|
}
|
|
@@ -113,7 +113,7 @@ extension ExpoSwiftUI {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
public override func getSupportedPropNames() -> [String] {
|
|
116
|
-
return dummyPropsMirror.
|
|
116
|
+
return allMirrorChildren(dummyPropsMirror).compactMap { (label: String?, value: Any) in
|
|
117
117
|
guard let field = value as? AnyFieldInternal else {
|
|
118
118
|
return nil
|
|
119
119
|
}
|
|
@@ -122,14 +122,13 @@ extension ExpoSwiftUI {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
public override func getSupportedEventNames() -> [String] {
|
|
125
|
-
let
|
|
126
|
-
let propEventNames: [String] = dummyPropsMirror.children.compactMap { (label: String?, value: Any) in
|
|
125
|
+
let propEventNames: [String] = allMirrorChildren(dummyPropsMirror).compactMap { (label: String?, value: Any) in
|
|
127
126
|
guard let event = value as? EventDispatcher else {
|
|
128
127
|
return nil
|
|
129
128
|
}
|
|
130
129
|
return event.customName ?? convertLabelToKey(label)
|
|
131
130
|
}
|
|
132
|
-
return
|
|
131
|
+
return propEventNames
|
|
133
132
|
}
|
|
134
133
|
}
|
|
135
134
|
}
|
|
@@ -10,6 +10,13 @@ extension ExpoSwiftUI {
|
|
|
10
10
|
return elements
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
Accepts `ViewName` definition element of `View`.
|
|
15
|
+
*/
|
|
16
|
+
public static func buildExpression(_ element: ViewNameDefinition) -> AnyViewDefinitionElement {
|
|
17
|
+
return element
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
/**
|
|
14
21
|
Accepts functions as a view definition elements.
|
|
15
22
|
*/
|
|
@@ -15,7 +15,7 @@ extension ExpoSwiftUI {
|
|
|
15
15
|
/**
|
|
16
16
|
An array of views passed by React as children.
|
|
17
17
|
*/
|
|
18
|
-
|
|
18
|
+
public var children: [any AnyChild]?
|
|
19
19
|
|
|
20
20
|
public internal(set) weak var appContext: AppContext?
|
|
21
21
|
|
|
@@ -37,7 +37,8 @@ extension ExpoSwiftUI {
|
|
|
37
37
|
dispatcher(GLOBAL_EVENT_NAME, payload)
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
Mirror(reflecting: self)
|
|
40
|
+
let mirror = Mirror(reflecting: self)
|
|
41
|
+
allMirrorChildren(mirror).forEach { (label: String?, value: Any) in
|
|
41
42
|
guard let event = value as? EventDispatcher else {
|
|
42
43
|
return
|
|
43
44
|
}
|
|
@@ -334,6 +334,10 @@ class FunctionSpec: ExpoSpec {
|
|
|
334
334
|
p.resolve(SharedString("Test with Promise"))
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
+
Function("withEither") { (either: Either<Bool, String>) in
|
|
338
|
+
return either
|
|
339
|
+
}
|
|
340
|
+
|
|
337
341
|
AsyncFunction("withURLAsync") {
|
|
338
342
|
return TestURLRecord.defaultURL
|
|
339
343
|
}
|
|
@@ -504,7 +508,17 @@ class FunctionSpec: ExpoSpec {
|
|
|
504
508
|
expect(result.kind) == .string
|
|
505
509
|
expect(result.getString()) == "Test with Promise"
|
|
506
510
|
}
|
|
507
|
-
|
|
511
|
+
|
|
512
|
+
it("accepts and returns Either value") {
|
|
513
|
+
let stringResult = try runtime.eval("expo.modules.TestModule.withEither('test string')")
|
|
514
|
+
expect(stringResult.kind) == .string
|
|
515
|
+
expect(stringResult.getString()) == "test string"
|
|
516
|
+
|
|
517
|
+
let boolResult = try runtime.eval("expo.modules.TestModule.withEither(true)")
|
|
518
|
+
expect(boolResult.kind) == .bool
|
|
519
|
+
expect(boolResult.getBool()) == true
|
|
520
|
+
}
|
|
521
|
+
|
|
508
522
|
// For async tests, this is a safe way to repeatedly evaluate JS
|
|
509
523
|
// and catch both Swift and ObjC exceptions
|
|
510
524
|
func safeBoolEval(_ js: String) -> Bool {
|
|
@@ -45,6 +45,31 @@ class RecordSpec: ExpoSpec {
|
|
|
45
45
|
expect(record.toDictionary()["b"] as? Int).to(equal(dict["b"]! as! Int))
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
it("works back and forth with Either") {
|
|
49
|
+
struct TestRecord: Record {
|
|
50
|
+
@Field var stringValue: Either<Bool, String>?
|
|
51
|
+
@Field var boolValue: Either<Bool, String>?
|
|
52
|
+
@Field var intValue: Either<Int, String>?
|
|
53
|
+
@Field var nilValue: Either<Int, String>?
|
|
54
|
+
}
|
|
55
|
+
let dict: [String: Any] = [
|
|
56
|
+
"stringValue": "custom",
|
|
57
|
+
"boolValue": true,
|
|
58
|
+
"intValue": 42,
|
|
59
|
+
]
|
|
60
|
+
let record = try TestRecord(from: dict, appContext: appContext)
|
|
61
|
+
expect(record.stringValue?.get() as String?).to(equal("custom"))
|
|
62
|
+
expect(record.boolValue?.get() as Bool?).to(equal(true))
|
|
63
|
+
expect(record.intValue?.get() as Int?).to(equal(42))
|
|
64
|
+
expect(record.nilValue).to(beNil())
|
|
65
|
+
|
|
66
|
+
let asDict = record.toDictionary(appContext: appContext)
|
|
67
|
+
expect(asDict["stringValue"] as? String).to(equal("custom"))
|
|
68
|
+
expect(asDict["boolValue"] as? Bool).to(equal(true))
|
|
69
|
+
expect(asDict["intValue"] as? Int).to(equal(42))
|
|
70
|
+
expect(asDict["nilValue"] as? Int).to(beNil())
|
|
71
|
+
}
|
|
72
|
+
|
|
48
73
|
it("works back and forth with a keyed field") {
|
|
49
74
|
struct TestRecord: Record {
|
|
50
75
|
@Field("key") var a: String?
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.24",
|
|
4
4
|
"description": "The core of Expo Modules architecture",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -65,5 +65,5 @@
|
|
|
65
65
|
"@testing-library/react-native": "^13.2.0",
|
|
66
66
|
"expo-module-scripts": "^5.0.7"
|
|
67
67
|
},
|
|
68
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "1bba12a43e14a442f2cf1c73fe21968e0ef097c1"
|
|
69
69
|
}
|