expo-modules-core 1.12.14 → 1.12.16

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,19 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 1.12.16 — 2024-06-20
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fixed resource leakage from `ExpoRequestInterceptorProtocol`. ([#29798](https://github.com/expo/expo/pull/29798) by [@kudo](https://github.com/kudo))
18
+
19
+ ## 1.12.15 — 2024-06-13
20
+
21
+ ### 🐛 Bug fixes
22
+
23
+ - Fixed reload crash on Android. ([#29593](https://github.com/expo/expo/pull/29593) by [@kudo](https://github.com/kudo))
24
+ - [Android] Fixed converting from `null` to `Record` sometimes didn't work as expected. ([#29508](https://github.com/expo/expo/pull/29508) by [@lukmccall](https://github.com/lukmccall))
25
+
13
26
  ## 1.12.14 — 2024-06-06
14
27
 
15
28
  ### 🐛 Bug fixes
@@ -1,7 +1,7 @@
1
1
  apply plugin: 'com.android.library'
2
2
 
3
3
  group = 'host.exp.exponent'
4
- version = '1.12.14'
4
+ version = '1.12.16'
5
5
 
6
6
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
7
  apply from: expoModulesCorePlugin
@@ -63,7 +63,7 @@ android {
63
63
  defaultConfig {
64
64
  consumerProguardFiles 'proguard-rules.pro'
65
65
  versionCode 1
66
- versionName "1.12.14"
66
+ versionName "1.12.16"
67
67
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
68
68
 
69
69
  testInstrumentationRunner "expo.modules.TestRunner"
@@ -93,11 +93,13 @@ JSIContext::JSIContext(jni::alias_ref<jhybridobject> jThis)
93
93
  : javaPart_(jni::make_global(jThis)) {}
94
94
 
95
95
  JSIContext::~JSIContext() {
96
- unbindJSIContext(runtimeHolder->get());
97
- // The runtime would be deallocated automatically.
98
- // However, we need to enforce the order of deallocations.
99
- // The runtime has to be deallocated before the JNI part.
100
- runtimeHolder.reset();
96
+ if (runtimeHolder) {
97
+ unbindJSIContext(runtimeHolder->get());
98
+ // The runtime would be deallocated automatically.
99
+ // However, we need to enforce the order of deallocations.
100
+ // The runtime has to be deallocated before the JNI part.
101
+ runtimeHolder.reset();
102
+ }
101
103
  }
102
104
 
103
105
  void JSIContext::installJSI(
@@ -73,7 +73,7 @@ object TypeConverterProviderImpl : TypeConverterProvider {
73
73
  private val cachedConverters = createCachedConverters(false)
74
74
  private val nullableCachedConverters = createCachedConverters(true)
75
75
 
76
- private val cachedRecordConverters = mutableMapOf<KClass<*>, TypeConverter<*>>()
76
+ private val cachedRecordConverters = mutableMapOf<KType, TypeConverter<*>>()
77
77
 
78
78
  private fun getCachedConverter(inputType: KType): TypeConverter<*>? {
79
79
  return if (inputType.isMarkedNullable) {
@@ -116,14 +116,14 @@ object TypeConverterProviderImpl : TypeConverterProvider {
116
116
  return EnumTypeConverter(kClass as KClass<Enum<*>>, type.isMarkedNullable)
117
117
  }
118
118
 
119
- val cachedConverter = cachedRecordConverters[kClass]
119
+ val cachedConverter = cachedRecordConverters[type]
120
120
  if (cachedConverter != null) {
121
121
  return cachedConverter
122
122
  }
123
123
 
124
124
  if (Record::class.java.isAssignableFrom(jClass)) {
125
125
  val converter = RecordTypeConverter<Record>(this, type)
126
- cachedRecordConverters[kClass] = converter
126
+ cachedRecordConverters[type] = converter
127
127
  return converter
128
128
  }
129
129
 
@@ -9,7 +9,7 @@ import Foundation
9
9
  @objc(EXRequestCdpInterceptor)
10
10
  public final class ExpoRequestCdpInterceptor: NSObject, ExpoRequestInterceptorProtocolDelegate {
11
11
  private var delegate: ExpoRequestCdpInterceptorDelegate?
12
- internal var dispatchQueue = DispatchQueue(label: "expo.requestCdpInterceptor")
12
+ public var dispatchQueue = DispatchQueue(label: "expo.requestCdpInterceptor")
13
13
 
14
14
  override private init() {}
15
15
 
@@ -7,13 +7,13 @@ import Foundation
7
7
  */
8
8
  @objc(EXRequestInterceptorProtocol)
9
9
  public final class ExpoRequestInterceptorProtocol: URLProtocol, URLSessionDataDelegate {
10
- private static let REQUEST_ID = "ExpoRequestInterceptorProtocol.requestId"
11
- private static var requestIdProvider = RequestIdProvider()
12
- private lazy var urlSession = URLSession(
10
+ private static let sessionDelegateProxy = URLSessionSessionDelegateProxy()
11
+ private static let urlSession = URLSession(
13
12
  configuration: URLSessionConfiguration.default,
14
- delegate: self,
13
+ delegate: sessionDelegateProxy,
15
14
  delegateQueue: nil
16
15
  )
16
+ private var requestId: String?
17
17
  private var dataTask_: URLSessionDataTask?
18
18
  private let responseBody = NSMutableData()
19
19
  private var responseBodyExceedsLimit = false
@@ -32,11 +32,10 @@ public final class ExpoRequestInterceptorProtocol: URLProtocol, URLSessionDataDe
32
32
  if !["http", "https"].contains(scheme) {
33
33
  return false
34
34
  }
35
- let isNewRequest = URLProtocol.property(
36
- forKey: Self.REQUEST_ID,
35
+ return URLProtocol.property(
36
+ forKey: REQUEST_ID,
37
37
  in: request
38
38
  ) == nil
39
- return isNewRequest
40
39
  }
41
40
 
42
41
  override init(
@@ -48,13 +47,16 @@ public final class ExpoRequestInterceptorProtocol: URLProtocol, URLSessionDataDe
48
47
  // swiftlint:disable force_cast
49
48
  let mutableRequest = request as! NSMutableURLRequest
50
49
  // swiftlint:enable force_cast
51
- let requestId = Self.requestIdProvider.create()
50
+ self.requestId = Self.sessionDelegateProxy.addDelegate(delegate: self)
51
+ guard let requestId else {
52
+ fatalError("requestId should not be nil.")
53
+ }
52
54
  URLProtocol.setProperty(
53
55
  requestId,
54
- forKey: Self.REQUEST_ID,
56
+ forKey: REQUEST_ID,
55
57
  in: mutableRequest
56
58
  )
57
- let dataTask = urlSession.dataTask(with: mutableRequest as URLRequest)
59
+ let dataTask = Self.urlSession.dataTask(with: mutableRequest as URLRequest)
58
60
  Self.delegate.willSendRequest(
59
61
  requestId: requestId,
60
62
  task: dataTask,
@@ -74,6 +76,9 @@ public final class ExpoRequestInterceptorProtocol: URLProtocol, URLSessionDataDe
74
76
 
75
77
  public override func stopLoading() {
76
78
  dataTask_?.cancel()
79
+ if let requestId {
80
+ Self.sessionDelegateProxy.removeDelegate(requestId: requestId)
81
+ }
77
82
  }
78
83
 
79
84
  // MARK: URLSessionDataDelegate implementations
@@ -91,12 +96,8 @@ public final class ExpoRequestInterceptorProtocol: URLProtocol, URLSessionDataDe
91
96
  if let error = error {
92
97
  client?.urlProtocol(self, didFailWithError: error)
93
98
  } else {
94
- if let currentRequest = task.currentRequest,
95
- let response = task.response as? HTTPURLResponse,
96
- let requestId = URLProtocol.property(
97
- forKey: Self.REQUEST_ID,
98
- in: currentRequest
99
- ) as? String {
99
+ if let response = task.response as? HTTPURLResponse,
100
+ let requestId {
100
101
  let contentType = response.value(forHTTPHeaderField: "Content-Type")
101
102
  let isText = (contentType?.starts(with: "text/") ?? false) || contentType == "application/json"
102
103
  Self.delegate.didReceiveResponse(
@@ -123,7 +124,7 @@ public final class ExpoRequestInterceptorProtocol: URLProtocol, URLSessionDataDe
123
124
  newRequest request: URLRequest,
124
125
  completionHandler: @escaping (URLRequest?) -> Void
125
126
  ) {
126
- if let requestId = URLProtocol.property(forKey: Self.REQUEST_ID, in: request) as? String {
127
+ if let requestId {
127
128
  Self.delegate.willSendRequest(
128
129
  requestId: requestId,
129
130
  task: task,
@@ -141,6 +142,118 @@ public final class ExpoRequestInterceptorProtocol: URLProtocol, URLSessionDataDe
141
142
  let requestId: String
142
143
  let redirectResponse: HTTPURLResponse
143
144
  }
145
+ }
146
+
147
+ /**
148
+ The delegate to dispatch network request events
149
+ */
150
+ @objc(EXRequestInterceptorProtocolDelegate)
151
+ protocol ExpoRequestInterceptorProtocolDelegate {
152
+ @objc
153
+ func willSendRequest(requestId: String, task: URLSessionTask, request: URLRequest, redirectResponse: HTTPURLResponse?)
154
+
155
+ @objc
156
+ func didReceiveResponse(requestId: String, task: URLSessionTask, responseBody: Data, isText: Bool, responseBodyExceedsLimit: Bool)
157
+ }
158
+
159
+ /**
160
+ Shared URLSessionDataDelegate instance and delete calls back to ExpoRequestInterceptorProtocol instances.
161
+ */
162
+ private class URLSessionSessionDelegateProxy: NSObject, URLSessionDataDelegate {
163
+ private var requestIdProvider = RequestIdProvider()
164
+ private var delegateMap: [String: URLSessionDataDelegate] = [:]
165
+ private let dispatchQueue = ExpoRequestCdpInterceptor.shared.dispatchQueue
166
+
167
+ func addDelegate(delegate: URLSessionDataDelegate) -> String {
168
+ let requestId = self.requestIdProvider.create()
169
+ self.dispatchQueue.async {
170
+ self.delegateMap[requestId] = delegate
171
+ }
172
+ return requestId
173
+ }
174
+
175
+ func removeDelegate(requestId: String) {
176
+ self.dispatchQueue.async {
177
+ self.delegateMap.removeValue(forKey: requestId)
178
+ }
179
+ }
180
+
181
+ private func getRequestId(task: URLSessionTask) -> String? {
182
+ if let currentRequest = task.currentRequest,
183
+ let requestId = URLProtocol.property(
184
+ forKey: REQUEST_ID,
185
+ in: currentRequest
186
+ ) as? String {
187
+ return requestId
188
+ }
189
+ return nil
190
+ }
191
+
192
+ private func getDelegate(requestId: String) -> URLSessionDataDelegate? {
193
+ return self.dispatchQueue.sync {
194
+ return self.delegateMap[requestId]
195
+ }
196
+ }
197
+
198
+ private func getDelegate(task: URLSessionTask) -> URLSessionDataDelegate? {
199
+ guard let requestId = self.getRequestId(task: task) else {
200
+ return nil
201
+ }
202
+ return self.getDelegate(requestId: requestId)
203
+ }
204
+
205
+ // MARK: URLSessionDataDelegate implementations
206
+
207
+ func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive: Data) {
208
+ if let delegate = getDelegate(task: dataTask) {
209
+ delegate.urlSession?(
210
+ session,
211
+ dataTask: dataTask,
212
+ didReceive: didReceive)
213
+ }
214
+ }
215
+
216
+ func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError: Error?) {
217
+ if let requestId = self.getRequestId(task: task), let delegate = getDelegate(requestId: requestId) {
218
+ delegate.urlSession?(
219
+ session,
220
+ task: task,
221
+ didCompleteWithError: didCompleteWithError)
222
+ self.removeDelegate(requestId: requestId)
223
+ }
224
+ }
225
+
226
+ func urlSession(
227
+ _ session: URLSession,
228
+ dataTask: URLSessionDataTask,
229
+ didReceive: URLResponse,
230
+ completionHandler: @escaping (URLSession.ResponseDisposition) -> Void
231
+ ) {
232
+ if let delegate = getDelegate(task: dataTask) {
233
+ delegate.urlSession?(
234
+ session,
235
+ dataTask: dataTask,
236
+ didReceive: didReceive,
237
+ completionHandler: completionHandler)
238
+ }
239
+ }
240
+
241
+ func urlSession(
242
+ _ session: URLSession,
243
+ task: URLSessionTask,
244
+ willPerformHTTPRedirection: HTTPURLResponse,
245
+ newRequest: URLRequest,
246
+ completionHandler: @escaping (URLRequest?) -> Void
247
+ ) {
248
+ if let delegate = getDelegate(task: task) {
249
+ delegate.urlSession?(
250
+ session,
251
+ task: task,
252
+ willPerformHTTPRedirection: willPerformHTTPRedirection,
253
+ newRequest: newRequest,
254
+ completionHandler: completionHandler)
255
+ }
256
+ }
144
257
 
145
258
  /**
146
259
  A helper class to create a unique request ID
@@ -157,14 +270,4 @@ public final class ExpoRequestInterceptorProtocol: URLProtocol, URLSessionDataDe
157
270
  }
158
271
  }
159
272
 
160
- /**
161
- The delegate to dispatch network request events
162
- */
163
- @objc(EXRequestInterceptorProtocolDelegate)
164
- protocol ExpoRequestInterceptorProtocolDelegate {
165
- @objc
166
- func willSendRequest(requestId: String, task: URLSessionTask, request: URLRequest, redirectResponse: HTTPURLResponse?)
167
-
168
- @objc
169
- func didReceiveResponse(requestId: String, task: URLSessionTask, responseBody: Data, isText: Bool, responseBodyExceedsLimit: Bool)
170
- }
273
+ private let REQUEST_ID = "ExpoRequestInterceptorProtocol.requestId"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "1.12.14",
3
+ "version": "1.12.16",
4
4
  "description": "The core of Expo Modules architecture",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -44,5 +44,5 @@
44
44
  "@testing-library/react-hooks": "^7.0.1",
45
45
  "expo-module-scripts": "^3.0.0"
46
46
  },
47
- "gitHead": "f424c7ea50d8787fa8d3354a33fe402160b63032"
47
+ "gitHead": "0d1a3567cb0fce9c54e1185654be88bd0c7842d4"
48
48
  }