expo-modules-core 2.2.0 → 2.2.2

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 CHANGED
@@ -10,6 +10,16 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 2.2.2 — 2025-02-14
14
+
15
+ ### 🎉 New features
16
+
17
+ - [iOS] Support async functions by SwiftUI views ([#34853](https://github.com/expo/expo/pull/34853) by [@jakex7](https://github.com/jakex7))
18
+
19
+ ## 2.2.1 — 2025-02-06
20
+
21
+ _This version does not introduce any user-facing changes._
22
+
13
23
  ## 2.2.0 — 2025-01-27
14
24
 
15
25
  ### 🎉 New features
@@ -27,6 +27,7 @@ class KotlinExpoModulesCorePlugin implements Plugin<Project> {
27
27
  "1.8.22": "1.8.22-1.0.11",
28
28
  "1.9.23": "1.9.23-1.0.20",
29
29
  "1.9.24": "1.9.24-1.0.20",
30
+ "1.9.25": "1.9.25-1.0.20",
30
31
  "2.0.21": "2.0.21-1.0.28"
31
32
  ]
32
33
 
@@ -34,7 +35,7 @@ class KotlinExpoModulesCorePlugin implements Plugin<Project> {
34
35
  ? project.rootProject.ext.get("kspVersion")
35
36
  : kspVersionsMap.containsKey(project.ext.kotlinVersion())
36
37
  ? kspVersionsMap.get(project.ext.kotlinVersion())
37
- : "1.9.24-1.0.20"
38
+ : "1.9.25-1.0.20"
38
39
  }
39
40
  }
40
41
  }
@@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
3
3
  apply plugin: 'com.android.library'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '2.2.0'
6
+ version = '2.2.2'
7
7
 
8
8
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
9
9
  apply from: expoModulesCorePlugin
@@ -86,7 +86,7 @@ android {
86
86
  defaultConfig {
87
87
  consumerProguardFiles 'proguard-rules.pro'
88
88
  versionCode 1
89
- versionName "2.2.0"
89
+ versionName "2.2.2"
90
90
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
91
91
 
92
92
  testInstrumentationRunner "expo.modules.TestRunner"
@@ -118,7 +118,11 @@ android {
118
118
 
119
119
  if (KOTLIN_MAJOR_VERSION < 2) {
120
120
  composeOptions {
121
- kotlinCompilerExtensionVersion = "1.5.14"
121
+ def versionsMap = [
122
+ "1.9.24": "1.5.14",
123
+ "1.9.25": "1.5.15",
124
+ ]
125
+ kotlinCompilerExtensionVersion = versionsMap[kotlinVersion()]
122
126
  }
123
127
  }
124
128
 
@@ -19,6 +19,13 @@ public func View<Props: ExpoSwiftUI.ViewProps, ViewType: ExpoSwiftUI.View<Props>
19
19
  return ExpoSwiftUI.ViewDefinition(ViewType.self)
20
20
  }
21
21
 
22
+ public func View<Props: ExpoSwiftUI.ViewProps, ViewType: ExpoSwiftUI.View<Props>>(
23
+ _ viewType: ViewType.Type,
24
+ @ExpoSwiftUI.ViewDefinitionBuilder<ViewType> _ elements: @escaping () -> [AnyViewDefinitionElement]
25
+ ) -> ExpoSwiftUI.ViewDefinition<Props, ViewType> {
26
+ return ExpoSwiftUI.ViewDefinition(ViewType.self, elements: elements())
27
+ }
28
+
22
29
  // MARK: Props
23
30
 
24
31
  /**
@@ -0,0 +1,59 @@
1
+ // Copyright 2025-present 650 Industries. All rights reserved.
2
+
3
+ internal struct DynamicSwiftUIViewType<ViewType: ExpoSwiftUIView>: AnyDynamicType {
4
+ let innerType: ViewType.Type
5
+
6
+ func wraps<InnerType>(_ type: InnerType.Type) -> Bool {
7
+ return innerType == InnerType.self
8
+ }
9
+
10
+ func equals(_ type: AnyDynamicType) -> Bool {
11
+ if let viewType = type as? Self {
12
+ return viewType.innerType == innerType
13
+ }
14
+ return false
15
+ }
16
+
17
+ /**
18
+ Casts from the React component instance to the view tag (`Int`).
19
+ */
20
+ func cast(jsValue: JavaScriptValue, appContext: AppContext) throws -> Any {
21
+ guard let viewTag = findViewTag(jsValue) else {
22
+ throw InvalidViewTagException()
23
+ }
24
+ return viewTag
25
+ }
26
+
27
+ /**
28
+ Converts a value of type `Int` to a native view with that tag in the given app context.
29
+ */
30
+ func cast<ValueType>(_ value: ValueType, appContext: AppContext) throws -> Any {
31
+ guard let viewTag = value as? Int else {
32
+ throw InvalidViewTagException()
33
+ }
34
+ guard Thread.isMainThread else {
35
+ throw NonMainThreadException()
36
+ }
37
+ guard let view = appContext.findView(withTag: viewTag, ofType: AnyExpoSwiftUIHostingView.self) else {
38
+ throw Exceptions.SwiftUIViewNotFound((tag: viewTag, type: innerType.self))
39
+ }
40
+ return view.getContentView()
41
+ }
42
+
43
+ var description: String {
44
+ return "View<\(innerType)>"
45
+ }
46
+ }
47
+
48
+ private func findViewTag(_ value: JavaScriptValue) -> Int? {
49
+ if value.isNumber() {
50
+ return value.getInt()
51
+ }
52
+ if value.isObject() {
53
+ let nativeTag = value.getObject().getProperty("nativeTag")
54
+ if nativeTag.isNumber() {
55
+ return nativeTag.getInt()
56
+ }
57
+ }
58
+ return nil
59
+ }
@@ -39,6 +39,11 @@ public struct Exceptions {
39
39
  "Unable to find the '\(param.type)' view with tag '\(param.tag)'"
40
40
  }
41
41
  }
42
+ public final class SwiftUIViewNotFound<ViewType: ExpoSwiftUIView>: GenericException<(tag: Int, type: ViewType.Type)> {
43
+ override public var reason: String {
44
+ "Unable to find the '\(param.type)' view with tag '\(param.tag)'"
45
+ }
46
+ }
42
47
 
43
48
  /**
44
49
  An exception to throw when there is no module implementing the `EXFileSystemInterface` interface.
@@ -7,6 +7,7 @@ import SwiftUI
7
7
  */
8
8
  internal protocol AnyExpoSwiftUIHostingView {
9
9
  func updateProps(_ rawProps: [String: Any])
10
+ func getContentView() -> any ExpoSwiftUI.View
10
11
  }
11
12
 
12
13
  extension ExpoSwiftUI {
@@ -21,6 +22,7 @@ extension ExpoSwiftUI {
21
22
  It's an environment object that is observed by the content view.
22
23
  */
23
24
  private let props: Props
25
+ private let contentView: any ExpoSwiftUI.View
24
26
 
25
27
  /**
26
28
  View controller that embeds the content view into the UIKit view hierarchy.
@@ -31,8 +33,8 @@ extension ExpoSwiftUI {
31
33
  Initializes a SwiftUI hosting view with the given SwiftUI view type.
32
34
  */
33
35
  init(viewType: ContentView.Type, props: Props, appContext: AppContext) {
34
- let rootView = ContentView().environmentObject(props)
35
-
36
+ self.contentView = ContentView()
37
+ let rootView = AnyView(contentView.environmentObject(props))
36
38
  self.props = props
37
39
  self.hostingController = UIHostingController(rootView: rootView)
38
40
 
@@ -67,6 +69,13 @@ extension ExpoSwiftUI {
67
69
  }
68
70
  }
69
71
 
72
+ /**
73
+ Returns inner SwiftUI view.
74
+ */
75
+ public func getContentView() -> any ExpoSwiftUI.View {
76
+ return contentView
77
+ }
78
+
70
79
  /**
71
80
  Returns a bool value whether the view supports prop with the given name.
72
81
  */
@@ -6,10 +6,11 @@ import Combine
6
6
  /**
7
7
  A protocol for SwiftUI views that need to access props.
8
8
  */
9
- public protocol ExpoSwiftUIView<Props>: SwiftUI.View {
9
+ public protocol ExpoSwiftUIView<Props>: SwiftUI.View, AnyArgument {
10
10
  associatedtype Props: ExpoSwiftUI.ViewProps
11
11
 
12
12
  var props: Props { get }
13
+ static func getDynamicType() -> AnyDynamicType
13
14
 
14
15
  init()
15
16
  }
@@ -23,6 +24,10 @@ public extension ExpoSwiftUIView {
23
24
  ForEach(props.children ?? []) { $0 }
24
25
  }
25
26
  }
27
+
28
+ static func getDynamicType() -> AnyDynamicType {
29
+ return DynamicSwiftUIViewType(innerType: Self.self)
30
+ }
26
31
  }
27
32
 
28
33
  extension ExpoSwiftUI {
@@ -36,6 +41,10 @@ extension ExpoSwiftUI {
36
41
  super.init(HostingView<Props, ViewType>.self, elements: [])
37
42
  }
38
43
 
44
+ init(_ viewType: ViewType.Type, elements: [AnyViewDefinitionElement]) {
45
+ super.init(HostingView<Props, ViewType>.self, elements: elements)
46
+ }
47
+
39
48
  public override func createView(appContext: AppContext) -> UIView? {
40
49
  let props = Props()
41
50
  return HostingView(viewType: ViewType.self, props: props, appContext: appContext)
@@ -0,0 +1,36 @@
1
+ // Copyright 2025-present 650 Industries. All rights reserved.
2
+
3
+ extension ExpoSwiftUI {
4
+ /**
5
+ A result builder for the view elements such as prop setters or view events.
6
+ */
7
+ @resultBuilder
8
+ public struct ViewDefinitionBuilder<ViewType: ExpoSwiftUI.View> {
9
+ public static func buildBlock(_ elements: AnyViewDefinitionElement...) -> [AnyViewDefinitionElement] {
10
+ return elements
11
+ }
12
+
13
+ /**
14
+ Accepts functions as a view definition elements.
15
+ */
16
+ public static func buildExpression<ElementType: ViewDefinitionFunctionElement>(
17
+ _ element: ElementType
18
+ ) -> AnyViewDefinitionElement {
19
+ return element
20
+ }
21
+
22
+ /**
23
+ Accepts functions that take the owner as a view definition elements.
24
+ */
25
+ public static func buildExpression<ElementType: ViewDefinitionFunctionElement>(
26
+ _ element: ElementType
27
+ ) -> AnyViewDefinitionElement where ElementType.ViewType == ViewType {
28
+ // Enforce async functions to run on the main queue
29
+ if var function = element as? AnyAsyncFunctionDefinition {
30
+ function.runOnQueue(.main)
31
+ function.takesOwner = true
32
+ }
33
+ return element
34
+ }
35
+ }
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "The core of Expo Modules architecture",
5
5
  "main": "src/index.ts",
6
6
  "types": "build/index.d.ts",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "devDependencies": {
44
44
  "@testing-library/react-native": "^12.5.2",
45
- "expo-module-scripts": "^4.0.3"
45
+ "expo-module-scripts": "^4.0.4"
46
46
  },
47
- "gitHead": "3fd37fd31fd818d833e85c4723b1578010da6967"
47
+ "gitHead": "7080126694798ca950d5dc3ef33483a17fa401bb"
48
48
  }