expo-modules-core 1.3.2 → 1.4.0

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/ExpoModulesCore.podspec +26 -5
  3. package/android/ExpoModulesCorePlugin.gradle +4 -0
  4. package/android/build.gradle +13 -14
  5. package/android/src/main/AndroidManifest.xml +1 -2
  6. package/android/src/main/cpp/ExpoModulesHostObject.cpp +3 -0
  7. package/android/src/main/cpp/JNIDeallocator.cpp +17 -0
  8. package/android/src/main/cpp/JNIDeallocator.h +25 -0
  9. package/android/src/main/cpp/JSIInteropModuleRegistry.cpp +8 -1
  10. package/android/src/main/cpp/JSIInteropModuleRegistry.h +6 -1
  11. package/android/src/main/cpp/JavaCallback.cpp +9 -0
  12. package/android/src/main/cpp/JavaCallback.h +12 -2
  13. package/android/src/main/cpp/JavaScriptFunction.cpp +13 -0
  14. package/android/src/main/cpp/JavaScriptFunction.h +7 -1
  15. package/android/src/main/cpp/JavaScriptModuleObject.cpp +2 -1
  16. package/android/src/main/cpp/JavaScriptObject.cpp +17 -2
  17. package/android/src/main/cpp/JavaScriptObject.h +10 -3
  18. package/android/src/main/cpp/JavaScriptRuntime.cpp +5 -4
  19. package/android/src/main/cpp/JavaScriptRuntime.h +5 -3
  20. package/android/src/main/cpp/JavaScriptTypedArray.cpp +14 -0
  21. package/android/src/main/cpp/JavaScriptTypedArray.h +6 -0
  22. package/android/src/main/cpp/JavaScriptValue.cpp +32 -4
  23. package/android/src/main/cpp/JavaScriptValue.h +10 -3
  24. package/android/src/main/cpp/MethodMetadata.cpp +1 -1
  25. package/android/src/main/cpp/types/FrontendConverter.cpp +8 -4
  26. package/android/src/main/java/expo/modules/core/interfaces/ReactActivityHandler.java +14 -0
  27. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +4 -1
  28. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +7 -3
  29. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +19 -12
  30. package/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt +70 -0
  31. package/android/src/main/java/expo/modules/kotlin/devtools/ExpoRequestCdpInterceptor.kt +72 -0
  32. package/android/src/main/java/expo/modules/kotlin/devtools/OkHttpHeadersExtension.kt +18 -0
  33. package/android/src/main/java/expo/modules/kotlin/devtools/cdp/CdpNetworkTypes.kt +257 -0
  34. package/android/src/main/java/expo/modules/kotlin/jni/JNIDeallocator.kt +24 -15
  35. package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +8 -1
  36. package/android/src/main/java/expo/modules/kotlin/jni/JavaCallback.kt +0 -4
  37. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptFunction.kt +0 -4
  38. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +5 -2
  39. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptObject.kt +0 -5
  40. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +0 -4
  41. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +1 -1
  42. package/android/src/main/java/expo/modules/kotlin/types/ColorTypeConverter.kt +3 -0
  43. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +1 -2
  44. package/android-annotation/build.gradle +2 -2
  45. package/android-annotation-processor/build.gradle +2 -2
  46. package/ios/JSI/EXJSIInstaller.h +2 -2
  47. package/ios/JSI/EXJSIInstaller.mm +6 -6
  48. package/ios/JSI/EXJavaScriptRuntime.h +0 -6
  49. package/ios/JSI/EXJavaScriptRuntime.mm +0 -23
  50. package/ios/RCTComponentData+Privates.h +17 -0
  51. package/ios/RCTComponentData+Privates.m +15 -0
  52. package/ios/Swift/AppContext.swift +20 -11
  53. package/ios/Swift/DevTools/CdpNetworkTypes.swift +163 -0
  54. package/ios/Swift/DevTools/ExpoRequestCdpInterceptor.swift +71 -0
  55. package/ios/Swift/DevTools/ExpoRequestInterceptorProtocol.swift +183 -0
  56. package/ios/Swift/DevTools/URLRequest+httpBodyData.swift +43 -0
  57. package/ios/Swift/ExpoRuntime.swift +28 -0
  58. package/ios/Swift/Modules/CoreModule.swift +7 -0
  59. package/ios/Swift/SharedObjects/SharedRef.swift +12 -0
  60. package/ios/Swift/Views/ComponentData.swift +1 -1
  61. package/ios/Tests/CoreModuleSpec.swift +27 -0
  62. package/ios/Tests/ExpoRequestCdpInterceptorSpec.swift +165 -0
  63. package/ios/Tests/SharedRefSpec.swift +60 -0
  64. package/package.json +2 -2
@@ -8,7 +8,7 @@ public final class AppContext: NSObject {
8
8
  internal static func create() -> AppContext {
9
9
  let appContext = AppContext()
10
10
 
11
- appContext._runtime = JavaScriptRuntime()
11
+ appContext._runtime = ExpoRuntime()
12
12
  return appContext
13
13
  }
14
14
 
@@ -44,7 +44,7 @@ public final class AppContext: NSObject {
44
44
  Underlying JSI runtime of the running app.
45
45
  */
46
46
  @objc
47
- public var _runtime: JavaScriptRuntime? {
47
+ public var _runtime: ExpoRuntime? {
48
48
  didSet {
49
49
  if _runtime == nil {
50
50
  // When the runtime is unpinned from the context (e.g. deallocated),
@@ -52,9 +52,8 @@ public final class AppContext: NSObject {
52
52
  // Otherwise the JSCRuntime asserts may fail on deallocation.
53
53
  releaseRuntimeObjects()
54
54
  } else if _runtime != oldValue {
55
- // Try to install ExpoModules host object automatically when the runtime changes.
56
- // TODO: Should we uninstall in the old runtime? (@tsapeta)
57
- try? installExpoModulesHostObject()
55
+ // Try to install the core object automatically when the runtime changes.
56
+ try? prepareRuntime()
58
57
  }
59
58
  }
60
59
  }
@@ -62,7 +61,7 @@ public final class AppContext: NSObject {
62
61
  /**
63
62
  JSI runtime of the running app.
64
63
  */
65
- public var runtime: JavaScriptRuntime {
64
+ public var runtime: ExpoRuntime {
66
65
  get throws {
67
66
  if let runtime = _runtime {
68
67
  return runtime
@@ -71,6 +70,11 @@ public final class AppContext: NSObject {
71
70
  }
72
71
  }
73
72
 
73
+ /**
74
+ The core module that defines the `expo` object in the global scope of Expo runtime.
75
+ */
76
+ internal private(set) lazy var coreModule = CoreModule(appContext: self)
77
+
74
78
  /**
75
79
  Designated initializer without modules provider.
76
80
  */
@@ -127,7 +131,8 @@ public final class AppContext: NSObject {
127
131
  */
128
132
  internal func newObject(nativeClassId: ObjectIdentifier) throws -> JavaScriptObject? {
129
133
  guard let jsClass = classRegistry.getJavaScriptClass(nativeClassId: nativeClassId) else {
130
- return nil
134
+ // TODO: Define a JS class for SharedRef in the CoreModule and then use it here instead of a raw object (?)
135
+ return try runtime.createObject()
131
136
  }
132
137
  let prototype = try jsClass.getProperty("prototype").asObject()
133
138
  let object = try runtime.createObject(withPrototype: prototype)
@@ -342,10 +347,14 @@ public final class AppContext: NSObject {
342
347
 
343
348
  // MARK: - Runtime
344
349
 
345
- internal func installExpoModulesHostObject() throws {
346
- guard _runtime != nil else {
347
- throw RuntimeLostException()
348
- }
350
+ internal func prepareRuntime() throws {
351
+ let runtime = try runtime
352
+ let coreObject = try coreModule.definition().build(appContext: self)
353
+
354
+ // Initialize `global.expo`.
355
+ try runtime.initializeCoreObject(coreObject)
356
+
357
+ // Install the modules host object as the `global.expo.modules`.
349
358
  EXJavaScriptRuntimeManager.installExpoModulesHostObject(self)
350
359
  }
351
360
 
@@ -0,0 +1,163 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+
3
+ import Foundation
4
+
5
+ struct CdpNetwork {
6
+ // MARK: Types
7
+
8
+ typealias Headers = [String: String]?
9
+ typealias MonotonicTime = TimeInterval
10
+ typealias RequestId = String
11
+ typealias TimeSinceEpoch = TimeInterval
12
+
13
+ enum ResourceType: String, Encodable {
14
+ case image = "Image"
15
+ case media = "Media"
16
+ case font = "Font"
17
+ case script = "Script"
18
+ case fetch = "Fetch"
19
+ case other = "Other"
20
+ }
21
+
22
+ struct ConnectTiming: Encodable {
23
+ let requestTime: MonotonicTime
24
+ }
25
+
26
+ struct Request: Encodable {
27
+ let url: String
28
+ let method: String
29
+ let headers: Headers
30
+ let postData: String?
31
+
32
+ init(_ request: URLRequest) {
33
+ self.url = request.url?.absoluteString ?? ""
34
+ self.method = request.httpMethod ?? "GET"
35
+ self.headers = request.allHTTPHeaderFields ?? [:]
36
+ if let httpBody = request.httpBodyData() {
37
+ self.postData = String(data: httpBody, encoding: .utf8)
38
+ } else {
39
+ self.postData = nil
40
+ }
41
+ }
42
+ }
43
+
44
+ struct Response: Encodable {
45
+ let url: String
46
+ let status: Int
47
+ let statusText: String
48
+ let headers: Headers
49
+ let mimeType: String
50
+ let encodedDataLength: Int64
51
+
52
+ init(_ response: HTTPURLResponse) {
53
+ self.url = response.url?.absoluteString ?? ""
54
+ self.status = response.statusCode
55
+ self.statusText = ""
56
+ let headers = response.allHeaderFields.reduce(into: [String: String]()) { result, header in
57
+ if let key = header.key as? String, let value = header.value as? String {
58
+ result[key] = value
59
+ }
60
+ }
61
+ self.headers = headers
62
+ self.mimeType = response.value(forHTTPHeaderField: "Content-Type") ?? ""
63
+ self.encodedDataLength = response.expectedContentLength
64
+ }
65
+ }
66
+
67
+ // MARK: Events
68
+
69
+ struct RequestWillBeSentParams: EventParms {
70
+ let requestId: RequestId
71
+ var loaderId = ""
72
+ var documentURL = "mobile"
73
+ let request: Request
74
+ let timestamp: MonotonicTime
75
+ let wallTime: TimeSinceEpoch
76
+ var initiator = ["type": "script"]
77
+ var redirectHasExtraInfo: Bool {
78
+ return self.redirectResponse != nil
79
+ }
80
+ let redirectResponse: Response?
81
+ var referrerPolicy = "no-referrer"
82
+ let type: ResourceType
83
+
84
+ init(now: TimeInterval, requestId: RequestId, request: URLRequest, redirectResponse: HTTPURLResponse?) {
85
+ self.requestId = requestId
86
+ self.request = Request(request)
87
+ self.timestamp = now
88
+ self.wallTime = now
89
+ if let redirectResponse = redirectResponse {
90
+ self.redirectResponse = Response(redirectResponse)
91
+ } else {
92
+ self.redirectResponse = nil
93
+ }
94
+ self.type = ResourceType.fetch
95
+ }
96
+ }
97
+
98
+ struct RequestWillBeSentExtraInfoParams: EventParms {
99
+ let requestId: RequestId
100
+ var associatedCookies = [String: String]()
101
+ let headers: Headers
102
+ let connectTiming: ConnectTiming
103
+
104
+ init(now: TimeInterval, requestId: RequestId, request: URLRequest) {
105
+ self.requestId = requestId
106
+ self.headers = request.allHTTPHeaderFields ?? [:]
107
+ self.connectTiming = ConnectTiming(requestTime: now)
108
+ }
109
+ }
110
+
111
+ struct ResponseReceivedParams: EventParms {
112
+ let requestId: RequestId
113
+ var loaderId = ""
114
+ let timestamp: MonotonicTime
115
+ let type: ResourceType
116
+ let response: Response
117
+ var hasExtraInfo = false
118
+
119
+ init(now: TimeInterval, requestId: RequestId, request: URLRequest, response: HTTPURLResponse) {
120
+ self.requestId = requestId
121
+ self.timestamp = now
122
+ self.type = ResourceType.fetch
123
+ self.response = Response(response)
124
+ }
125
+ }
126
+
127
+ struct LoadingFinishedParams: EventParms {
128
+ let requestId: RequestId
129
+ let timestamp: MonotonicTime
130
+ let encodedDataLength: Int64
131
+
132
+ init(now: TimeInterval, requestId: RequestId, request: URLRequest, response: HTTPURLResponse) {
133
+ self.requestId = requestId
134
+ self.timestamp = now
135
+ self.encodedDataLength = response.expectedContentLength
136
+ }
137
+ }
138
+
139
+ struct ExpoReceivedResponseBodyParams: EventParms {
140
+ let requestId: RequestId
141
+ let body: String
142
+ let base64Encoded: Bool
143
+
144
+ init(now: TimeInterval, requestId: RequestId, responseBody: Data, isText: Bool) {
145
+ self.requestId = requestId
146
+ let bodyString = isText ? String(data: responseBody, encoding: .utf8) : responseBody.base64EncodedString()
147
+ if let bodyString = bodyString {
148
+ self.body = bodyString
149
+ self.base64Encoded = !isText
150
+ } else {
151
+ self.body = ""
152
+ self.base64Encoded = false
153
+ }
154
+ }
155
+ }
156
+
157
+ typealias EventParms = Encodable
158
+
159
+ struct Event<T: EventParms>: Encodable {
160
+ let method: String
161
+ let params: T
162
+ }
163
+ }
@@ -0,0 +1,71 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+
3
+ import Foundation
4
+
5
+ /**
6
+ The `ExpoRequestInterceptorProtocolDelegate` implementation to
7
+ dispatch CDP (Chrome DevTools Protocol: https://chromedevtools.github.io/devtools-protocol/) events.
8
+ */
9
+ @objc(EXRequestCdpInterceptor)
10
+ public final class ExpoRequestCdpInterceptor: NSObject, ExpoRequestInterceptorProtocolDelegate {
11
+ private var delegate: ExpoRequestCdpInterceptorDelegate?
12
+ internal var dispatchQueue = DispatchQueue(label: "expo.requestCdpInterceptor")
13
+
14
+ override private init() {}
15
+
16
+ @objc
17
+ public static let shared = ExpoRequestCdpInterceptor()
18
+
19
+ @objc
20
+ public func setDelegate(_ newValue: ExpoRequestCdpInterceptorDelegate?) {
21
+ dispatchQueue.async {
22
+ self.delegate = newValue
23
+ }
24
+ }
25
+
26
+ private func dispatchEvent<T: CdpNetwork.EventParms>(_ event: CdpNetwork.Event<T>) {
27
+ dispatchQueue.async {
28
+ let encoder = JSONEncoder()
29
+ if let jsonData = try? encoder.encode(event), let payload = String(data: jsonData, encoding: .utf8) {
30
+ self.delegate?.dispatch(payload)
31
+ }
32
+ }
33
+ }
34
+
35
+ // MARK: ExpoRequestInterceptorProtocolDelegate implementations
36
+
37
+ func willSendRequest(requestId: String, request: URLRequest, redirectResponse: HTTPURLResponse?) {
38
+ let now = Date().timeIntervalSince1970
39
+
40
+ let params = CdpNetwork.RequestWillBeSentParams(now: now, requestId: requestId, request: request, redirectResponse: redirectResponse)
41
+ dispatchEvent(CdpNetwork.Event(method: "Network.requestWillBeSent", params: params))
42
+
43
+ let params2 = CdpNetwork.RequestWillBeSentExtraInfoParams(now: now, requestId: requestId, request: request)
44
+ dispatchEvent(CdpNetwork.Event(method: "Network.requestWillBeSentExtraInfo", params: params2))
45
+ }
46
+
47
+ func didReceiveResponse(requestId: String, request: URLRequest, response: HTTPURLResponse) {
48
+ let now = Date().timeIntervalSince1970
49
+
50
+ let params = CdpNetwork.ResponseReceivedParams(now: now, requestId: requestId, request: request, response: response)
51
+ dispatchEvent(CdpNetwork.Event(method: "Network.responseReceived", params: params))
52
+
53
+ let params2 = CdpNetwork.LoadingFinishedParams(now: now, requestId: requestId, request: request, response: response)
54
+ dispatchEvent(CdpNetwork.Event(method: "Network.loadingFinished", params: params2))
55
+ }
56
+
57
+ func didReceiveResponseBody(requestId: String, responseBody: Data, isText: Bool) {
58
+ let now = Date().timeIntervalSince1970
59
+ let params = CdpNetwork.ExpoReceivedResponseBodyParams(now: now, requestId: requestId, responseBody: responseBody, isText: isText)
60
+ dispatchEvent(CdpNetwork.Event(method: "Expo(Network.receivedResponseBody)", params: params))
61
+ }
62
+ }
63
+
64
+ /**
65
+ The delegate to dispatch CDP events for ExpoRequestCdpInterceptor
66
+ */
67
+ @objc(EXRequestCdpInterceptorDelegate)
68
+ public protocol ExpoRequestCdpInterceptorDelegate {
69
+ @objc
70
+ func dispatch(_ event: String)
71
+ }
@@ -0,0 +1,183 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+
3
+ import Foundation
4
+
5
+ /**
6
+ A `URLSession` interceptor which passes network events to its delegate
7
+ */
8
+ @objc(EXRequestInterceptorProtocol)
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(
13
+ configuration: URLSessionConfiguration.default,
14
+ delegate: self,
15
+ delegateQueue: nil
16
+ )
17
+ private var dataTask_: URLSessionDataTask?
18
+ private let responseBody = NSMutableData()
19
+ private var responseIsText = false
20
+ private var responseContentLength: Int64 = 0
21
+
22
+ static let MAX_BODY_SIZE = 1_048_576
23
+
24
+ // Currently keeps the delegate fixed for ExpoRequestCdpInterceptor and be thread-safe
25
+ static let delegate: ExpoRequestInterceptorProtocolDelegate = ExpoRequestCdpInterceptor.shared
26
+
27
+ // MARK: URLProtocol implementations
28
+
29
+ public override class func canInit(with request: URLRequest) -> Bool {
30
+ guard let scheme = request.url?.scheme else {
31
+ return false
32
+ }
33
+ if !["http", "https"].contains(scheme) {
34
+ return false
35
+ }
36
+ let isNewRequest = URLProtocol.property(
37
+ forKey: Self.REQUEST_ID,
38
+ in: request
39
+ ) == nil
40
+ return isNewRequest
41
+ }
42
+
43
+ override init(
44
+ request: URLRequest,
45
+ cachedResponse: CachedURLResponse?,
46
+ client: URLProtocolClient?
47
+ ) {
48
+ super.init(request: request, cachedResponse: cachedResponse, client: client)
49
+ // swiftlint:disable force_cast
50
+ let mutableRequest = request as! NSMutableURLRequest
51
+ // swiftlint:enable force_cast
52
+ let requestId = Self.requestIdProvider.create()
53
+ URLProtocol.setProperty(
54
+ requestId,
55
+ forKey: Self.REQUEST_ID,
56
+ in: mutableRequest
57
+ )
58
+ Self.delegate.willSendRequest(
59
+ requestId: requestId,
60
+ request: mutableRequest as URLRequest,
61
+ redirectResponse: nil
62
+ )
63
+ dataTask_ = urlSession.dataTask(with: mutableRequest as URLRequest)
64
+ }
65
+
66
+ public override class func canonicalRequest(for request: URLRequest) -> URLRequest {
67
+ request
68
+ }
69
+
70
+ public override func startLoading() {
71
+ dataTask_?.resume()
72
+ }
73
+
74
+ public override func stopLoading() {
75
+ dataTask_?.cancel()
76
+ }
77
+
78
+ // MARK: URLSessionDataDelegate implementations
79
+
80
+ public func urlSession(_: URLSession, dataTask _: URLSessionDataTask, didReceive data: Data) {
81
+ client?.urlProtocol(self, didLoad: data)
82
+ if responseBody.length + data.count <= Self.MAX_BODY_SIZE {
83
+ responseBody.append(data)
84
+ }
85
+ }
86
+
87
+ public func urlSession(
88
+ _: URLSession,
89
+ dataTask: URLSessionDataTask,
90
+ didReceive response: URLResponse,
91
+ completionHandler: @escaping (URLSession.ResponseDisposition) -> Void
92
+ ) {
93
+ if let resp = response as? HTTPURLResponse,
94
+ let currentRequest = dataTask.currentRequest,
95
+ let requestId = URLProtocol.property(
96
+ forKey: Self.REQUEST_ID,
97
+ in: currentRequest
98
+ ) as? String {
99
+ Self.delegate.didReceiveResponse(
100
+ requestId: requestId,
101
+ request: currentRequest,
102
+ response: resp
103
+ )
104
+
105
+ let contentType = resp.value(forHTTPHeaderField: "Content-Type")
106
+ responseIsText = (contentType?.starts(with: "text/") ?? false) || contentType == "application/json"
107
+ responseContentLength = resp.expectedContentLength
108
+ }
109
+ completionHandler(.allow)
110
+ client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .allowed)
111
+ }
112
+
113
+ public func urlSession(_: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
114
+ if let error = error {
115
+ client?.urlProtocol(self, didFailWithError: error)
116
+ } else {
117
+ if responseContentLength > 0 && responseContentLength <= Self.MAX_BODY_SIZE,
118
+ let currentRequest = task.currentRequest,
119
+ let requestId = URLProtocol.property(
120
+ forKey: Self.REQUEST_ID,
121
+ in: currentRequest
122
+ ) as? String {
123
+ Self.delegate.didReceiveResponseBody(
124
+ requestId: requestId, responseBody: responseBody as Data, isText: responseIsText)
125
+ }
126
+ client?.urlProtocolDidFinishLoading(self)
127
+ }
128
+ }
129
+
130
+ public func urlSession(
131
+ _: URLSession,
132
+ task _: URLSessionTask,
133
+ willPerformHTTPRedirection response: HTTPURLResponse,
134
+ newRequest request: URLRequest,
135
+ completionHandler: @escaping (URLRequest?) -> Void
136
+ ) {
137
+ if let requestId = URLProtocol.property(forKey: Self.REQUEST_ID, in: request) as? String {
138
+ Self.delegate.willSendRequest(
139
+ requestId: requestId,
140
+ request: request,
141
+ redirectResponse: response
142
+ )
143
+ }
144
+ completionHandler(request)
145
+ }
146
+
147
+ /**
148
+ Data structure to save the response for redirection
149
+ */
150
+ private struct RedirectResponse {
151
+ let requestId: String
152
+ let redirectResponse: HTTPURLResponse
153
+ }
154
+
155
+ /**
156
+ A helper class to create a unique request ID
157
+ */
158
+ private struct RequestIdProvider {
159
+ private var value: UInt64 = 0
160
+
161
+ mutating func create() -> String {
162
+ // We could ensure the increment thread safety,
163
+ // because we access this function from the same thread (com.apple.CFNetwork.CustomProtocols).
164
+ value += 1
165
+ return String(value)
166
+ }
167
+ }
168
+ }
169
+
170
+ /**
171
+ The delegate to dispatch network request events
172
+ */
173
+ @objc(EXRequestInterceptorProtocolDelegate)
174
+ protocol ExpoRequestInterceptorProtocolDelegate {
175
+ @objc
176
+ func willSendRequest(requestId: String, request: URLRequest, redirectResponse: HTTPURLResponse?)
177
+
178
+ @objc
179
+ func didReceiveResponse(requestId: String, request: URLRequest, response: HTTPURLResponse)
180
+
181
+ @objc
182
+ func didReceiveResponseBody(requestId: String, responseBody: Data, isText: Bool)
183
+ }
@@ -0,0 +1,43 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+
3
+ import Foundation
4
+
5
+ /**
6
+ `URLRequest.httpBodyData()` extension to read the underlying `httpBodyStream` as Data.
7
+ */
8
+ extension URLRequest {
9
+ func httpBodyData(limit: Int = ExpoRequestInterceptorProtocol.MAX_BODY_SIZE) -> Data? {
10
+ if let httpBody = self.httpBody {
11
+ return httpBody
12
+ }
13
+
14
+ if let contentLength = self.allHTTPHeaderFields?["Content-Length"],
15
+ let contentLengthInt = Int(contentLength),
16
+ contentLengthInt > limit {
17
+ return nil
18
+ }
19
+ guard let stream = self.httpBodyStream else {
20
+ return nil
21
+ }
22
+
23
+ let bufferSize: Int = 8192
24
+ let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
25
+
26
+ stream.open()
27
+ defer {
28
+ buffer.deallocate()
29
+ stream.close()
30
+ }
31
+
32
+ var data = Data()
33
+ while stream.hasBytesAvailable {
34
+ let chunkSize = stream.read(buffer, maxLength: bufferSize)
35
+ if data.count + chunkSize > limit {
36
+ return nil
37
+ }
38
+ data.append(buffer, count: chunkSize)
39
+ }
40
+
41
+ return data
42
+ }
43
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ Property name of the core object in the global scope of the Expo JS runtime.
3
+ */
4
+ private let coreObjectPropertyName = "expo"
5
+
6
+ @objc(ExpoRuntime)
7
+ public final class ExpoRuntime: JavaScriptRuntime {
8
+ /**
9
+ The core object of the Expo runtime that is used to scope native Expo-specific functionalities.
10
+ It gets installed into the runtime as the `global.expo` object.
11
+ */
12
+ @objc
13
+ public private(set) var coreObject: JavaScriptObject?
14
+
15
+ internal func initializeCoreObject(_ coreObject: JavaScriptObject) throws {
16
+ guard self.coreObject == nil else {
17
+ throw CoreObjectInitializedException()
18
+ }
19
+ self.coreObject = coreObject
20
+ global().defineProperty(coreObjectPropertyName, value: coreObject, options: .enumerable)
21
+ }
22
+ }
23
+
24
+ private final class CoreObjectInitializedException: Exception {
25
+ override var reason: String {
26
+ "The core Expo object was already initialized"
27
+ }
28
+ }
@@ -0,0 +1,7 @@
1
+ // The core module that describes the `global.expo` object.
2
+ internal final class CoreModule: Module {
3
+ internal func definition() -> ModuleDefinition {
4
+ // Nothing so far, but eventually we will expose some common classes
5
+ // and maybe even the `modules` host object.
6
+ }
7
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ Shared object (ref) that holds a pointer to any native object. Allows passing references
3
+ to native instances among different independent libraries.
4
+ */
5
+ open class SharedRef<PointerType>: SharedObject {
6
+ public let pointer: PointerType
7
+
8
+ init(_ pointer: PointerType) {
9
+ self.pointer = pointer
10
+ super.init()
11
+ }
12
+ }
@@ -7,7 +7,7 @@ import React
7
7
  but it also simplifies capturing the view config so we can omit some reflections that React Native executes.
8
8
  */
9
9
  @objc(EXComponentData)
10
- public final class ComponentData: RCTComponentData {
10
+ public final class ComponentData: RCTComponentDataSwiftAdapter {
11
11
  /**
12
12
  Weak pointer to the holder of a module that the component data was created for.
13
13
  */
@@ -0,0 +1,27 @@
1
+ import ExpoModulesTestCore
2
+
3
+ @testable import ExpoModulesCore
4
+
5
+ final class CoreModuleSpec: ExpoSpec {
6
+ override func spec() {
7
+ let appContext = AppContext.create()
8
+ let runtime = try! appContext.runtime
9
+
10
+ describe("core module") {
11
+ it("is initialized") {
12
+ expect(appContext.coreModule).notTo(beNil())
13
+ }
14
+ }
15
+
16
+ describe("core object") {
17
+ it("is initialized") {
18
+ expect(runtime.coreObject).notTo(beNil())
19
+ }
20
+
21
+ it("is installed to global scope") {
22
+ let coreObjectValue = try runtime.eval("expo")
23
+ expect(coreObjectValue.kind) == .object
24
+ }
25
+ }
26
+ }
27
+ }