expo-modules-core 0.6.4 → 0.8.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.
- package/CHANGELOG.md +53 -0
- package/README.md +1 -1
- package/android/ExpoModulesCorePlugin.gradle +15 -0
- package/android/build.gradle +46 -32
- package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +5 -5
- package/android/src/main/java/expo/modules/adapters/react/services/UIManagerModuleWrapper.java +13 -0
- package/android/src/main/java/expo/modules/core/ViewManager.java +9 -0
- package/android/src/main/java/expo/modules/core/interfaces/JavaScriptContextProvider.java +4 -0
- package/android/src/main/java/expo/modules/core/interfaces/ReactActivityHandler.java +37 -1
- package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.java +19 -0
- package/android/src/main/java/expo/modules/core/interfaces/services/UIManager.java +2 -0
- package/android/src/main/java/expo/modules/kotlin/AppContext.kt +23 -5
- package/android/src/main/java/expo/modules/kotlin/DynamicExtenstions.kt +5 -3
- package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +3 -8
- package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +24 -9
- package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +12 -7
- package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +23 -1
- package/android/src/main/java/expo/modules/kotlin/Promise.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/callbacks/Callback.kt +5 -0
- package/android/src/main/java/expo/modules/kotlin/callbacks/ViewCallback.kt +39 -0
- package/android/src/main/java/expo/modules/kotlin/callbacks/ViewCallbackDelegate.kt +27 -0
- package/android/src/main/java/expo/modules/kotlin/defaultmodules/ErrorManagerModule.kt +25 -0
- package/android/src/main/java/expo/modules/kotlin/events/EventEmitter.kt +13 -0
- package/android/src/main/java/expo/modules/kotlin/events/KModuleEventEmitterWrapper.kt +102 -0
- package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +93 -9
- package/android/src/main/java/expo/modules/kotlin/exception/ExceptionDecorator.kt +11 -0
- package/android/src/main/java/expo/modules/kotlin/{methods/AnyMethod.kt → functions/AnyFunction.kt} +18 -18
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +15 -0
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +61 -0
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromise.kt +15 -0
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncSuspendFunction.kt +36 -0
- package/android/src/main/java/expo/modules/kotlin/modules/DefinitionMarker.kt +4 -0
- package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +17 -2
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +219 -30
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +2 -2
- package/android/src/main/java/expo/modules/kotlin/records/FieldValidator.kt +139 -0
- package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +71 -15
- package/android/src/main/java/expo/modules/kotlin/records/Required.kt +5 -0
- package/android/src/main/java/expo/modules/kotlin/records/ValidationBinder.kt +110 -0
- package/android/src/main/java/expo/modules/kotlin/records/Validators.kt +61 -0
- package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +11 -5
- package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverter.kt +35 -0
- package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverterHelper.kt +148 -0
- package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +10 -4
- package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +12 -6
- package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +29 -13
- package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +2 -1
- package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +9 -1
- package/android/src/main/java/expo/modules/kotlin/views/CallbacksDefinition.kt +3 -0
- package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +71 -0
- package/android/src/main/java/expo/modules/kotlin/views/SimpleViewManagerWrapper.kt +22 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinition.kt +18 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +64 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +30 -2
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +42 -1
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +62 -2
- package/build/EventEmitter.d.ts +1 -0
- package/build/EventEmitter.d.ts.map +1 -0
- package/build/NativeModulesProxy.d.ts +1 -0
- package/build/NativeModulesProxy.d.ts.map +1 -0
- package/build/NativeModulesProxy.native.d.ts +1 -4
- package/build/NativeModulesProxy.native.d.ts.map +1 -0
- package/build/NativeModulesProxy.native.js +1 -14
- package/build/NativeModulesProxy.native.js.map +1 -1
- package/build/NativeModulesProxy.types.d.ts +1 -3
- package/build/NativeModulesProxy.types.d.ts.map +1 -0
- package/build/NativeModulesProxy.types.js.map +1 -1
- package/build/NativeViewManagerAdapter.d.ts +1 -0
- package/build/NativeViewManagerAdapter.d.ts.map +1 -0
- package/build/NativeViewManagerAdapter.native.d.ts +1 -0
- package/build/NativeViewManagerAdapter.native.d.ts.map +1 -0
- package/build/NativeViewManagerAdapter.native.js +9 -33
- package/build/NativeViewManagerAdapter.native.js.map +1 -1
- package/build/PermissionsHook.d.ts +1 -0
- package/build/PermissionsHook.d.ts.map +1 -0
- package/build/PermissionsInterface.d.ts +1 -0
- package/build/PermissionsInterface.d.ts.map +1 -0
- package/build/Platform.d.ts +1 -0
- package/build/Platform.d.ts.map +1 -0
- package/build/SyntheticPlatformEmitter.d.ts +1 -0
- package/build/SyntheticPlatformEmitter.d.ts.map +1 -0
- package/build/SyntheticPlatformEmitter.web.d.ts +1 -0
- package/build/SyntheticPlatformEmitter.web.d.ts.map +1 -0
- package/build/deprecate.d.ts +1 -0
- package/build/deprecate.d.ts.map +1 -0
- package/build/environment/browser.d.ts +1 -0
- package/build/environment/browser.d.ts.map +1 -0
- package/build/environment/browser.web.d.ts +1 -0
- package/build/environment/browser.web.d.ts.map +1 -0
- package/build/errors/CodedError.d.ts +1 -0
- package/build/errors/CodedError.d.ts.map +1 -0
- package/build/errors/UnavailabilityError.d.ts +1 -0
- package/build/errors/UnavailabilityError.d.ts.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/requireNativeModule.d.ts +16 -0
- package/build/requireNativeModule.d.ts.map +1 -0
- package/build/requireNativeModule.js +18 -0
- package/build/requireNativeModule.js.map +1 -0
- package/build/sweet/NativeErrorManager.d.ts +3 -0
- package/build/sweet/NativeErrorManager.d.ts.map +1 -0
- package/build/sweet/NativeErrorManager.js +3 -0
- package/build/sweet/NativeErrorManager.js.map +1 -0
- package/build/sweet/setUpErrorManager.fx.d.ts +2 -0
- package/build/sweet/setUpErrorManager.fx.d.ts.map +1 -0
- package/build/sweet/setUpErrorManager.fx.js +11 -0
- package/build/sweet/setUpErrorManager.fx.js.map +1 -0
- package/ios/AppDelegates/EXAppDelegatesLoader.m +4 -8
- package/ios/AppDelegates/ExpoAppDelegate.swift +22 -20
- package/ios/EXAppDefines.h +1 -0
- package/ios/EXAppDefines.m +6 -0
- package/ios/EXUtilities.h +2 -0
- package/ios/EXUtilities.m +12 -0
- package/ios/ExpoModulesCore.h +4 -0
- package/ios/ExpoModulesCore.podspec +4 -2
- package/ios/Interfaces/FileSystem/EXFileSystemInterface.h +1 -1
- package/ios/Interfaces/TaskManager/EXTaskServiceInterface.h +1 -0
- package/ios/JSI/{JSIConversions.h → EXJSIConversions.h} +5 -0
- package/ios/JSI/{JSIConversions.mm → EXJSIConversions.mm} +21 -1
- package/ios/JSI/{JSIInstaller.h → EXJSIInstaller.h} +10 -0
- package/ios/JSI/EXJSIInstaller.mm +17 -0
- package/ios/JSI/EXJSIUtils.h +19 -0
- package/ios/JSI/EXJSIUtils.mm +89 -0
- package/ios/JSI/EXJavaScriptObject.h +97 -0
- package/ios/JSI/EXJavaScriptObject.mm +121 -0
- package/ios/JSI/EXJavaScriptRuntime.h +73 -0
- package/ios/JSI/EXJavaScriptRuntime.mm +153 -0
- package/ios/JSI/EXJavaScriptValue.h +57 -0
- package/ios/JSI/EXJavaScriptValue.mm +166 -0
- package/ios/JSI/ExpoModulesHostObject.h +33 -0
- package/ios/JSI/ExpoModulesHostObject.mm +41 -0
- package/ios/JSI/JavaScriptRuntime.swift +32 -0
- package/ios/JSI/JavaScriptValue.swift +94 -0
- package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +3 -23
- package/ios/NativeModulesProxy/EXNativeModulesProxy.h +2 -2
- package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +101 -75
- package/ios/RCTComponentData+Privates.h +12 -0
- package/ios/ReactDelegates/EXReactCompatibleHelpers.h +18 -0
- package/ios/ReactDelegates/EXReactCompatibleHelpers.m +19 -0
- package/ios/ReactDelegates/ExpoReactDelegate.swift +3 -3
- package/ios/ReactDelegates/ExpoReactDelegateHandler.swift +4 -4
- package/ios/ReactDelegates/ModulePriorities.swift +1 -1
- package/ios/Swift/AppContext.swift +64 -4
- package/ios/Swift/Arguments/ArgumentType.swift +4 -0
- package/ios/Swift/Arguments/Convertibles.swift +13 -13
- package/ios/Swift/Arguments/Types/EnumArgumentType.swift +11 -17
- package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +1 -1
- package/ios/Swift/Arguments/Types/RawArgumentType.swift +2 -2
- package/ios/Swift/Conversions.swift +51 -56
- package/ios/Swift/EventListener.swift +8 -10
- package/ios/Swift/Events/Callback.swift +66 -0
- package/ios/Swift/Events/Event.swift +43 -0
- package/ios/Swift/Exceptions/ChainableException.swift +51 -0
- package/ios/Swift/{CodedError.swift → Exceptions/CodedError.swift} +1 -12
- package/ios/Swift/Exceptions/Exception.swift +62 -0
- package/ios/Swift/Exceptions/ExceptionOrigin.swift +28 -0
- package/ios/Swift/Exceptions/GenericException.swift +20 -0
- package/ios/Swift/Exceptions/UnexpectedException.swift +16 -0
- package/ios/Swift/Functions/AnyFunction.swift +11 -1
- package/ios/Swift/Functions/AsyncFunction.swift +17 -0
- package/ios/Swift/Functions/ConcreteFunction.swift +43 -17
- package/ios/Swift/JavaScriptUtils.swift +54 -0
- package/ios/Swift/ModuleHolder.swift +66 -16
- package/ios/Swift/ModuleRegistry.swift +4 -1
- package/ios/Swift/Modules/AnyModule.swift +0 -1
- package/ios/Swift/Modules/ModuleDefinition.swift +4 -13
- package/ios/Swift/Modules/ModuleDefinitionBuilder.swift +0 -1
- package/ios/Swift/Modules/ModuleDefinitionComponents.swift +0 -188
- package/ios/Swift/ModulesProvider.swift +3 -11
- package/ios/Swift/Objects/ObjectDefinition.swift +30 -0
- package/ios/Swift/Objects/ObjectDefinitionComponents.swift +384 -0
- package/ios/Swift/Promise.swift +8 -3
- package/ios/Swift/Records/AnyField.swift +7 -0
- package/ios/Swift/Records/Field.swift +24 -19
- package/ios/Swift/Records/FieldOption.swift +1 -1
- package/ios/Swift/Records/Record.swift +12 -4
- package/ios/Swift/SwiftInteropBridge.swift +53 -15
- package/ios/Swift/Views/AnyViewProp.swift +1 -1
- package/ios/Swift/Views/ComponentData.swift +96 -0
- package/ios/Swift/Views/ConcreteViewProp.swift +6 -8
- package/ios/Swift/Views/ExpoView.swift +8 -0
- package/ios/Swift/Views/ViewFactory.swift +1 -1
- package/ios/Swift/Views/ViewManagerDefinition.swift +23 -2
- package/ios/Swift/Views/ViewManagerDefinitionBuilder.swift +0 -1
- package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +26 -0
- package/ios/Swift/Views/ViewModuleWrapper.swift +5 -2
- package/ios/Swift.h +5 -0
- package/ios/Tests/ArgumentTypeSpec.swift +5 -7
- package/ios/Tests/ConstantsSpec.swift +6 -7
- package/ios/Tests/ConvertiblesSpec.swift +35 -36
- package/ios/Tests/ExceptionsSpec.swift +111 -0
- package/ios/Tests/ExpoModulesSpec.swift +76 -0
- package/ios/Tests/FunctionSpec.swift +22 -25
- package/ios/Tests/FunctionWithConvertiblesSpec.swift +4 -5
- package/ios/Tests/JavaScriptObjectSpec.swift +97 -0
- package/ios/Tests/JavaScriptRuntimeSpec.swift +94 -0
- package/ios/Tests/Mocks/ModuleMocks.swift +1 -1
- package/ios/Tests/Mocks/ModulesProviderMock.swift +0 -1
- package/ios/Tests/ModuleEventListenersSpec.swift +3 -4
- package/ios/Tests/ModuleRegistrySpec.swift +2 -3
- package/ios/Tests/RecordSpec.swift +9 -20
- package/package.json +3 -3
- package/src/NativeModulesProxy.native.ts +2 -22
- package/src/NativeModulesProxy.types.ts +0 -8
- package/src/NativeViewManagerAdapter.native.tsx +12 -28
- package/src/index.ts +4 -0
- package/src/requireNativeModule.ts +29 -0
- package/src/sweet/NativeErrorManager.ts +2 -0
- package/src/sweet/setUpErrorManager.fx.ts +12 -0
- package/android/src/main/java/expo/modules/kotlin/events/KEventEmitterWrapper.kt +0 -26
- package/android/src/main/java/expo/modules/kotlin/methods/Method.kt +0 -14
- package/android/src/main/java/expo/modules/kotlin/methods/PromiseMethod.kt +0 -15
- package/ios/JSI/ExpoModulesProxySpec.h +0 -24
- package/ios/JSI/ExpoModulesProxySpec.mm +0 -135
- package/ios/JSI/JSIInstaller.mm +0 -22
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Copyright 2021-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import React
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
Custom component data extending `RCTComponentData`. Its main purpose is to handle event-based props (callbacks),
|
|
7
|
+
but it also simplifies capturing the view config so we can omit some reflections that React Native executes.
|
|
8
|
+
*/
|
|
9
|
+
@objc(EXComponentData)
|
|
10
|
+
public final class ComponentData: RCTComponentData {
|
|
11
|
+
/**
|
|
12
|
+
Weak pointer to the holder of a module that the component data was created for.
|
|
13
|
+
*/
|
|
14
|
+
weak var moduleHolder: ModuleHolder?
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
Initializer that additionally takes the original view module to have access to its definition.
|
|
18
|
+
*/
|
|
19
|
+
@objc
|
|
20
|
+
public init(viewModule: ViewModuleWrapper, managerClass: ViewModuleWrapper.Type, bridge: RCTBridge) {
|
|
21
|
+
self.moduleHolder = viewModule.wrappedModuleHolder
|
|
22
|
+
super.init(managerClass: managerClass, bridge: bridge, eventDispatcher: bridge.eventDispatcher())
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// MARK: RCTComponentData
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
Creates a setter for the specific prop. For non-event props we just let React Native do its job.
|
|
29
|
+
Events are handled differently to conveniently use them in Swift.
|
|
30
|
+
*/
|
|
31
|
+
public override func createPropBlock(_ propName: String, isShadowView: Bool) -> RCTPropBlockAlias {
|
|
32
|
+
// Expo Modules Core doesn't support shadow views yet, so fall back to the default implementation.
|
|
33
|
+
if isShadowView {
|
|
34
|
+
return super.createPropBlock(propName, isShadowView: isShadowView)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// If the prop is defined as an event, create our own event setter.
|
|
38
|
+
if moduleHolder?.viewManager?.eventNames.contains(propName) == true {
|
|
39
|
+
return createEventSetter(eventName: propName, bridge: self.manager?.bridge)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Otherwise also fall back to the default implementation.
|
|
43
|
+
return super.createPropBlock(propName, isShadowView: isShadowView)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
The base `RCTComponentData` class does some Objective-C dynamic calls in this function, but we don't
|
|
48
|
+
need to do these slow operations since the Sweet API gives us necessary details without reflections.
|
|
49
|
+
*/
|
|
50
|
+
public override func viewConfig() -> [String: Any] {
|
|
51
|
+
var propTypes: [String: Any] = [:]
|
|
52
|
+
var directEvents: [String] = []
|
|
53
|
+
let superClass: AnyClass? = managerClass.superclass()
|
|
54
|
+
|
|
55
|
+
if let eventNames = moduleHolder?.viewManager?.eventNames {
|
|
56
|
+
for eventName in eventNames {
|
|
57
|
+
directEvents.append(RCTNormalizeInputEventName(eventName))
|
|
58
|
+
propTypes[eventName] = "BOOL"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return [
|
|
63
|
+
"propTypes": propTypes,
|
|
64
|
+
"directEvents": directEvents,
|
|
65
|
+
"bubblingEvents": [String](),
|
|
66
|
+
"baseModuleName": superClass?.moduleName() as Any
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
Creates a setter for the event prop.
|
|
73
|
+
*/
|
|
74
|
+
private func createEventSetter(eventName: String, bridge: RCTBridge?) -> RCTPropBlockAlias {
|
|
75
|
+
return { [weak bridge] (target: RCTComponent, value: Any) in
|
|
76
|
+
// Find view's property that is named as the prop and is wrapped by `Event`.
|
|
77
|
+
let child = Mirror(reflecting: target).children.first {
|
|
78
|
+
$0.label == "_\(eventName)"
|
|
79
|
+
}
|
|
80
|
+
guard let event = child?.value as? AnyEventInternal else {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// For callbacks React Native passes a bool value whether the prop is specified or not.
|
|
85
|
+
if value as? Bool == true {
|
|
86
|
+
event.settle { [weak target] (body: Any) in
|
|
87
|
+
if let target = target {
|
|
88
|
+
let componentEvent = RCTComponentEvent(name: eventName, viewTag: target.reactTag, body: ["payload": body])
|
|
89
|
+
bridge?.eventDispatcher().send(componentEvent)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
event.invalidate()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -36,22 +36,20 @@ public final class ConcreteViewProp<ViewType: UIView, PropType: AnyArgument>: An
|
|
|
36
36
|
// Method's signature must be type-erased to conform to `AnyViewProp` protocol.
|
|
37
37
|
// Given view must be castable to the generic `ViewType` type.
|
|
38
38
|
guard let view = view as? ViewType else {
|
|
39
|
-
throw
|
|
39
|
+
throw IncompatibleViewException((propName: name, viewType: ViewType.self))
|
|
40
40
|
}
|
|
41
41
|
guard let value = try propType.cast(value) as? PropType else {
|
|
42
|
-
throw Conversions.
|
|
42
|
+
throw Conversions.CastingException<PropType>(value)
|
|
43
43
|
}
|
|
44
44
|
setter(view, value)
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
An
|
|
49
|
+
An exception that is thrown when the view passed to prop's setter doesn't match the type in setter's definition.
|
|
50
50
|
*/
|
|
51
|
-
internal
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
var description: String {
|
|
55
|
-
"Tried to set prop `\(propName)` on the view that isn't `\(viewType)`"
|
|
51
|
+
internal class IncompatibleViewException: GenericException<(propName: String, viewType: UIView.Type)> {
|
|
52
|
+
override var reason: String {
|
|
53
|
+
"Tried to set prop '\(param.propName)' on the view that isn't \(param.viewType)"
|
|
56
54
|
}
|
|
57
55
|
}
|
|
@@ -3,7 +3,7 @@ import UIKit
|
|
|
3
3
|
/**
|
|
4
4
|
A definition of the view factory that creates views.
|
|
5
5
|
*/
|
|
6
|
-
internal struct ViewFactory:
|
|
6
|
+
internal struct ViewFactory: ViewManagerDefinitionComponent {
|
|
7
7
|
typealias FactoryClosureType = () -> UIView
|
|
8
8
|
|
|
9
9
|
let factory: FactoryClosureType
|
|
@@ -3,7 +3,7 @@ import UIKit
|
|
|
3
3
|
/**
|
|
4
4
|
The definition of the view manager. It's part of the module definition to scope only view-related definitions.
|
|
5
5
|
*/
|
|
6
|
-
public
|
|
6
|
+
public final class ViewManagerDefinition: ObjectDefinition {
|
|
7
7
|
/**
|
|
8
8
|
The view factory that lets us create views.
|
|
9
9
|
*/
|
|
@@ -14,13 +14,29 @@ public struct ViewManagerDefinition: AnyDefinition {
|
|
|
14
14
|
*/
|
|
15
15
|
let props: [AnyViewProp]
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
Names of the events that the view can send to JavaScript.
|
|
19
|
+
*/
|
|
20
|
+
let eventNames: [String]
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
Default initializer receiving children definitions from the result builder.
|
|
24
|
+
*/
|
|
25
|
+
override init(definitions: [AnyDefinition]) {
|
|
18
26
|
self.factory = definitions
|
|
19
27
|
.compactMap { $0 as? ViewFactory }
|
|
20
28
|
.last
|
|
21
29
|
|
|
22
30
|
self.props = definitions
|
|
23
31
|
.compactMap { $0 as? AnyViewProp }
|
|
32
|
+
|
|
33
|
+
self.eventNames = Array(
|
|
34
|
+
definitions
|
|
35
|
+
.compactMap { ($0 as? EventsDefinition)?.names }
|
|
36
|
+
.joined()
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
super.init(definitions: definitions)
|
|
24
40
|
}
|
|
25
41
|
|
|
26
42
|
/**
|
|
@@ -39,3 +55,8 @@ public struct ViewManagerDefinition: AnyDefinition {
|
|
|
39
55
|
}
|
|
40
56
|
}
|
|
41
57
|
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
The protocol for definition components that can only be handled by the view manager builder.
|
|
61
|
+
*/
|
|
62
|
+
public protocol ViewManagerDefinitionComponent: AnyDefinition {}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/// Here we implement the components exclusive for view managers.
|
|
2
|
+
|
|
3
|
+
// MARK: View factory
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
Defines the factory creating a native view when the module is used as a view.
|
|
7
|
+
*/
|
|
8
|
+
public func view(_ closure: @escaping () -> UIView) -> ViewManagerDefinitionComponent {
|
|
9
|
+
return ViewFactory(closure)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// MARK: Props
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
Creates a view prop that defines its name and setter.
|
|
16
|
+
*/
|
|
17
|
+
public func prop<ViewType: UIView, PropType: AnyArgument>(
|
|
18
|
+
_ name: String,
|
|
19
|
+
_ setter: @escaping (ViewType, PropType) -> Void
|
|
20
|
+
) -> ViewManagerDefinitionComponent {
|
|
21
|
+
return ConcreteViewProp(
|
|
22
|
+
name: name,
|
|
23
|
+
propType: ArgumentType(PropType.self),
|
|
24
|
+
setter: setter
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -93,7 +93,7 @@ public final class ViewModuleWrapper: RCTViewManager, DynamicModuleWrapperProtoc
|
|
|
93
93
|
*/
|
|
94
94
|
@objc
|
|
95
95
|
public class func propConfig_proxiedProperties() -> [String] {
|
|
96
|
-
return ["NSDictionary", "__custom__"]
|
|
96
|
+
return ["NSDictionary", "__custom__"]
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|
|
@@ -126,7 +126,7 @@ public final class ViewModuleWrapper: RCTViewManager, DynamicModuleWrapperProtoc
|
|
|
126
126
|
let prefixedViewName = "ViewManagerAdapter_\(module.name())"
|
|
127
127
|
|
|
128
128
|
return prefixedViewName.withCString { viewNamePtr in
|
|
129
|
-
// Create a new
|
|
129
|
+
// Create a new class that inherits from `ViewModuleWrapper`. The class name passed here, doesn't work for Swift classes,
|
|
130
130
|
// so we also have to override `moduleName` class method.
|
|
131
131
|
let wrapperClass: AnyClass? = objc_allocateClassPair(ViewModuleWrapper.self, viewNamePtr, 0)
|
|
132
132
|
|
|
@@ -141,3 +141,6 @@ public final class ViewModuleWrapper: RCTViewManager, DynamicModuleWrapperProtoc
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
|
+
|
|
145
|
+
// The direct event implementation can be cached and lazy-loaded (global and static variables are lazy by default in Swift).
|
|
146
|
+
let directEventBlockImplementation = imp_implementationWithBlock({ ["RCTDirectEventBlock"] } as @convention(block) () -> [String])
|
package/ios/Swift.h
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// Copyright 2018-present 650 Industries. All rights reserved.
|
|
2
2
|
|
|
3
|
+
// The generated swift header may depend on some Objective-C declarations,
|
|
4
|
+
// adding dependency imports here to prevent declarations not found errors.
|
|
5
|
+
#import <ExpoModulesCore/EXDefines.h>
|
|
6
|
+
#import <ExpoModulesCore/RCTComponentData+Privates.h>
|
|
7
|
+
|
|
3
8
|
// When `use_frameworks!` is used, the generated Swift header is inside ExpoModulesCore module.
|
|
4
9
|
// Otherwise, it's available only locally with double-quoted imports.
|
|
5
10
|
#if __has_include(<ExpoModulesCore/ExpoModulesCore-Swift.h>)
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
// Copyright 2021-present 650 Industries. All rights reserved.
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import Nimble
|
|
3
|
+
import ExpoModulesTestCore
|
|
5
4
|
|
|
6
5
|
@testable import ExpoModulesCore
|
|
7
6
|
|
|
8
|
-
class ArgumentTypeSpec:
|
|
7
|
+
class ArgumentTypeSpec: ExpoSpec {
|
|
9
8
|
override func spec() {
|
|
10
|
-
|
|
11
9
|
it("casts primitives") {
|
|
12
10
|
let type = ArgumentType(Int.self)
|
|
13
11
|
let value = 123
|
|
@@ -34,7 +32,7 @@ class ArgumentTypeSpec: QuickSpec {
|
|
|
34
32
|
let value: Double? = nil
|
|
35
33
|
let anyValue = value as Any
|
|
36
34
|
|
|
37
|
-
expect { try type.cast(anyValue) }.to(throwError(errorType: Conversions.
|
|
35
|
+
expect { try type.cast(anyValue) }.to(throwError(errorType: Conversions.NullCastException<Double>.self))
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
it("casts arrays") {
|
|
@@ -104,7 +102,7 @@ class ArgumentTypeSpec: QuickSpec {
|
|
|
104
102
|
|
|
105
103
|
// "841" is not a raw value of any `IntTestEnum` case
|
|
106
104
|
expect { try type.cast("string instead of int") }.to(throwError {
|
|
107
|
-
expect($0).to(beAKindOf(
|
|
105
|
+
expect($0).to(beAKindOf(EnumCastingException.self))
|
|
108
106
|
})
|
|
109
107
|
}
|
|
110
108
|
|
|
@@ -113,7 +111,7 @@ class ArgumentTypeSpec: QuickSpec {
|
|
|
113
111
|
|
|
114
112
|
// "841" is not a raw value of any `IntTestEnum` case
|
|
115
113
|
expect { try type.cast(841) }.to(throwError {
|
|
116
|
-
expect($0).to(beAKindOf(
|
|
114
|
+
expect($0).to(beAKindOf(EnumNoSuchValueException.self))
|
|
117
115
|
})
|
|
118
116
|
}
|
|
119
117
|
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import Nimble
|
|
1
|
+
import ExpoModulesTestCore
|
|
3
2
|
|
|
4
3
|
@testable import ExpoModulesCore
|
|
5
4
|
|
|
6
|
-
class ConstantsSpec:
|
|
5
|
+
class ConstantsSpec: ExpoSpec {
|
|
7
6
|
override func spec() {
|
|
8
7
|
let appContext = AppContext()
|
|
9
8
|
|
|
10
9
|
it("takes closure resolving to dictionary") {
|
|
11
10
|
let holder = mockModuleHolder(appContext) {
|
|
12
|
-
|
|
11
|
+
constants {
|
|
13
12
|
return ["test": 123]
|
|
14
13
|
}
|
|
15
14
|
}
|
|
@@ -18,15 +17,15 @@ class ConstantsSpec: QuickSpec {
|
|
|
18
17
|
|
|
19
18
|
it("takes the dictionary") {
|
|
20
19
|
let holder = mockModuleHolder(appContext) {
|
|
21
|
-
|
|
20
|
+
constants(["test": 123])
|
|
22
21
|
}
|
|
23
22
|
expect(holder.getConstants()["test"] as? Int) == 123
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
it("merges multiple constants definitions") {
|
|
27
26
|
let holder = mockModuleHolder(appContext) {
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
constants(["test": 456, "test2": 789])
|
|
28
|
+
constants(["test": 123])
|
|
30
29
|
}
|
|
31
30
|
let consts = holder.getConstants()
|
|
32
31
|
expect(consts["test"] as? Int) == 123
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// Copyright 2018-present 650 Industries. All rights reserved.
|
|
2
2
|
|
|
3
3
|
import CoreGraphics
|
|
4
|
-
import
|
|
5
|
-
import Nimble
|
|
4
|
+
import ExpoModulesTestCore
|
|
6
5
|
|
|
7
6
|
@testable import ExpoModulesCore
|
|
8
7
|
|
|
9
|
-
class ConvertiblesSpec:
|
|
8
|
+
class ConvertiblesSpec: ExpoSpec {
|
|
10
9
|
override func spec() {
|
|
11
10
|
describe("URL") {
|
|
12
11
|
it("converts from remote url") {
|
|
@@ -37,7 +36,7 @@ class ConvertiblesSpec: QuickSpec {
|
|
|
37
36
|
|
|
38
37
|
it("throws when no string") {
|
|
39
38
|
expect { try URL.convert(from: 29.5) }.to(
|
|
40
|
-
throwError(errorType: Conversions.
|
|
39
|
+
throwError(errorType: Conversions.ConvertingException<URL>.self)
|
|
41
40
|
)
|
|
42
41
|
}
|
|
43
42
|
}
|
|
@@ -61,22 +60,22 @@ class ConvertiblesSpec: QuickSpec {
|
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
it("throws when array size is unexpected") { // different than two
|
|
64
|
-
expect { try CGPoint.convert(from: []) }.to(throwError(errorType: Conversions.
|
|
65
|
-
expect { try CGPoint.convert(from: [x]) }.to(throwError(errorType: Conversions.
|
|
66
|
-
expect { try CGPoint.convert(from: [x, y, x]) }.to(throwError(errorType: Conversions.
|
|
63
|
+
expect { try CGPoint.convert(from: []) }.to(throwError(errorType: Conversions.ConvertingException<CGPoint>.self))
|
|
64
|
+
expect { try CGPoint.convert(from: [x]) }.to(throwError(errorType: Conversions.ConvertingException<CGPoint>.self))
|
|
65
|
+
expect { try CGPoint.convert(from: [x, y, x]) }.to(throwError(errorType: Conversions.ConvertingException<CGPoint>.self))
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
it("throws when dict is missing some keys") {
|
|
70
69
|
expect { try CGPoint.convert(from: ["test": x]) }.to(throwError {
|
|
71
|
-
expect($0).to(beAKindOf(Conversions.
|
|
72
|
-
expect(($0 as! CodedError).description) == Conversions.
|
|
70
|
+
expect($0).to(beAKindOf(Conversions.MissingKeysException<Double>.self))
|
|
71
|
+
expect(($0 as! CodedError).description) == Conversions.MissingKeysException<Double>(["x", "y"]).description
|
|
73
72
|
})
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
it("throws when dict has uncastable keys") {
|
|
77
76
|
expect { try CGPoint.convert(from: ["x": x, "y": "string"]) }.to(throwError {
|
|
78
|
-
expect($0).to(beAKindOf(Conversions.
|
|
79
|
-
expect(($0 as! CodedError).description) == Conversions.
|
|
77
|
+
expect($0).to(beAKindOf(Conversions.CastingValuesException<Double>.self))
|
|
78
|
+
expect(($0 as! CodedError).description) == Conversions.CastingValuesException<Double>(["y"]).description
|
|
80
79
|
})
|
|
81
80
|
}
|
|
82
81
|
}
|
|
@@ -100,22 +99,22 @@ class ConvertiblesSpec: QuickSpec {
|
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
it("throws when array size is unexpected") { // different than two
|
|
103
|
-
expect { try CGSize.convert(from: []) }.to(throwError(errorType: Conversions.
|
|
104
|
-
expect { try CGSize.convert(from: [width]) }.to(throwError(errorType: Conversions.
|
|
105
|
-
expect { try CGSize.convert(from: [width, height, width]) }.to(throwError(errorType: Conversions.
|
|
102
|
+
expect { try CGSize.convert(from: []) }.to(throwError(errorType: Conversions.ConvertingException<CGSize>.self))
|
|
103
|
+
expect { try CGSize.convert(from: [width]) }.to(throwError(errorType: Conversions.ConvertingException<CGSize>.self))
|
|
104
|
+
expect { try CGSize.convert(from: [width, height, width]) }.to(throwError(errorType: Conversions.ConvertingException<CGSize>.self))
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
it("throws when dict is missing some keys") {
|
|
109
108
|
expect { try CGSize.convert(from: ["width": width]) }.to(throwError {
|
|
110
|
-
expect($0).to(beAKindOf(Conversions.
|
|
111
|
-
expect(($0 as! CodedError).description) == Conversions.
|
|
109
|
+
expect($0).to(beAKindOf(Conversions.MissingKeysException<Double>.self))
|
|
110
|
+
expect(($0 as! CodedError).description) == Conversions.MissingKeysException<Double>(["height"]).description
|
|
112
111
|
})
|
|
113
112
|
}
|
|
114
113
|
|
|
115
114
|
it("throws when dict has uncastable keys") {
|
|
116
115
|
expect { try CGSize.convert(from: ["width": "test", "height": height]) }.to(throwError {
|
|
117
|
-
expect($0).to(beAKindOf(Conversions.
|
|
118
|
-
expect(($0 as! CodedError).description) == Conversions.
|
|
116
|
+
expect($0).to(beAKindOf(Conversions.CastingValuesException<Double>.self))
|
|
117
|
+
expect(($0 as! CodedError).description) == Conversions.CastingValuesException<Double>(["width"]).description
|
|
119
118
|
})
|
|
120
119
|
}
|
|
121
120
|
}
|
|
@@ -139,22 +138,22 @@ class ConvertiblesSpec: QuickSpec {
|
|
|
139
138
|
}
|
|
140
139
|
|
|
141
140
|
it("throws when array size is unexpected") { // different than two
|
|
142
|
-
expect { try CGVector.convert(from: []) }.to(throwError(errorType: Conversions.
|
|
143
|
-
expect { try CGVector.convert(from: [dx]) }.to(throwError(errorType: Conversions.
|
|
144
|
-
expect { try CGVector.convert(from: [dx, dy, dx]) }.to(throwError(errorType: Conversions.
|
|
141
|
+
expect { try CGVector.convert(from: []) }.to(throwError(errorType: Conversions.ConvertingException<CGVector>.self))
|
|
142
|
+
expect { try CGVector.convert(from: [dx]) }.to(throwError(errorType: Conversions.ConvertingException<CGVector>.self))
|
|
143
|
+
expect { try CGVector.convert(from: [dx, dy, dx]) }.to(throwError(errorType: Conversions.ConvertingException<CGVector>.self))
|
|
145
144
|
}
|
|
146
145
|
|
|
147
146
|
it("throws when dict is missing some keys") {
|
|
148
147
|
expect { try CGVector.convert(from: ["dx": dx]) }.to(throwError {
|
|
149
|
-
expect($0).to(beAKindOf(Conversions.
|
|
150
|
-
expect(($0 as! CodedError).description) == Conversions.
|
|
148
|
+
expect($0).to(beAKindOf(Conversions.MissingKeysException<Double>.self))
|
|
149
|
+
expect(($0 as! CodedError).description) == Conversions.MissingKeysException<Double>(["dy"]).description
|
|
151
150
|
})
|
|
152
151
|
}
|
|
153
152
|
|
|
154
153
|
it("throws when dict has uncastable keys") {
|
|
155
154
|
expect { try CGVector.convert(from: ["dx": "dx", "dy": dy]) }.to(throwError {
|
|
156
|
-
expect($0).to(beAKindOf(Conversions.
|
|
157
|
-
expect(($0 as! CodedError).description) == Conversions.
|
|
155
|
+
expect($0).to(beAKindOf(Conversions.CastingValuesException<Double>.self))
|
|
156
|
+
expect(($0 as! CodedError).description) == Conversions.CastingValuesException<Double>(["dx"]).description
|
|
158
157
|
})
|
|
159
158
|
}
|
|
160
159
|
}
|
|
@@ -184,22 +183,22 @@ class ConvertiblesSpec: QuickSpec {
|
|
|
184
183
|
}
|
|
185
184
|
|
|
186
185
|
it("throws when array size is unexpected") { // different than four
|
|
187
|
-
expect { try CGRect.convert(from: [x]) }.to(throwError(errorType: Conversions.
|
|
188
|
-
expect { try CGRect.convert(from: [x, y]) }.to(throwError(errorType: Conversions.
|
|
189
|
-
expect { try CGRect.convert(from: [x, y, width, height, y]) }.to(throwError(errorType: Conversions.
|
|
186
|
+
expect { try CGRect.convert(from: [x]) }.to(throwError(errorType: Conversions.ConvertingException<CGRect>.self))
|
|
187
|
+
expect { try CGRect.convert(from: [x, y]) }.to(throwError(errorType: Conversions.ConvertingException<CGRect>.self))
|
|
188
|
+
expect { try CGRect.convert(from: [x, y, width, height, y]) }.to(throwError(errorType: Conversions.ConvertingException<CGRect>.self))
|
|
190
189
|
}
|
|
191
190
|
|
|
192
191
|
it("throws when dict is missing some keys") {
|
|
193
192
|
expect { try CGRect.convert(from: ["x": x]) }.to(throwError {
|
|
194
|
-
expect($0).to(beAKindOf(Conversions.
|
|
195
|
-
expect(($0 as! CodedError).description) == Conversions.
|
|
193
|
+
expect($0).to(beAKindOf(Conversions.MissingKeysException<Double>.self))
|
|
194
|
+
expect(($0 as! CodedError).description) == Conversions.MissingKeysException<Double>(["y", "width", "height"]).description
|
|
196
195
|
})
|
|
197
196
|
}
|
|
198
197
|
|
|
199
198
|
it("throws when dict has uncastable keys") {
|
|
200
199
|
expect { try CGRect.convert(from: ["x": x, "y": nil, "width": width, "height": "\(height)"]) }.to(throwError {
|
|
201
|
-
expect($0).to(beAKindOf(Conversions.
|
|
202
|
-
expect(($0 as! CodedError).description) == Conversions.
|
|
200
|
+
expect($0).to(beAKindOf(Conversions.CastingValuesException<Double>.self))
|
|
201
|
+
expect(($0 as! CodedError).description) == Conversions.CastingValuesException<Double>(["y", "height"]).description
|
|
203
202
|
})
|
|
204
203
|
}
|
|
205
204
|
}
|
|
@@ -213,8 +212,8 @@ class ConvertiblesSpec: QuickSpec {
|
|
|
213
212
|
}
|
|
214
213
|
func testInvalidHexColor(_ hex: String) {
|
|
215
214
|
expect { try CGColor.convert(from: hex) }.to(throwError {
|
|
216
|
-
expect($0).to(beAKindOf(Conversions.
|
|
217
|
-
expect(($0 as! CodedError).description) == Conversions.
|
|
215
|
+
expect($0).to(beAKindOf(Conversions.InvalidHexColorException.self))
|
|
216
|
+
expect(($0 as! CodedError).description) == Conversions.InvalidHexColorException(hex).description
|
|
218
217
|
})
|
|
219
218
|
}
|
|
220
219
|
|
|
@@ -256,8 +255,8 @@ class ConvertiblesSpec: QuickSpec {
|
|
|
256
255
|
it("throws when int overflows") {
|
|
257
256
|
let hex = 0xBBAA88FF2
|
|
258
257
|
expect { try CGColor.convert(from: hex) }.to(throwError {
|
|
259
|
-
expect($0).to(beAKindOf(Conversions.
|
|
260
|
-
expect(($0 as! CodedError).description) == Conversions.
|
|
258
|
+
expect($0).to(beAKindOf(Conversions.HexColorOverflowException.self))
|
|
259
|
+
expect(($0 as! CodedError).description) == Conversions.HexColorOverflowException(UInt64(hex)).description
|
|
261
260
|
})
|
|
262
261
|
}
|
|
263
262
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Copyright 2021-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import ExpoModulesTestCore
|
|
4
|
+
|
|
5
|
+
@testable import ExpoModulesCore
|
|
6
|
+
|
|
7
|
+
final class ExceptionsSpec: ExpoSpec {
|
|
8
|
+
override func spec() {
|
|
9
|
+
it("has name") {
|
|
10
|
+
let error = TestException()
|
|
11
|
+
expect(error.name) == "TestException"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
it("has code") {
|
|
15
|
+
let error = TestException()
|
|
16
|
+
expect(error.code) == "ERR_TEST"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
it("has reason") {
|
|
20
|
+
let error = TestException()
|
|
21
|
+
expect(error.reason) == "This is the test exception"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
it("can be chained once") {
|
|
25
|
+
func throwable() throws {
|
|
26
|
+
do {
|
|
27
|
+
throw TestExceptionCause()
|
|
28
|
+
} catch {
|
|
29
|
+
throw TestException().causedBy(error)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
expect { try throwable() }.to(throwError { error in
|
|
33
|
+
testChainedExceptionTypes(error: error, types: [TestException.self, TestExceptionCause.self])
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
it("can be chained twice") {
|
|
38
|
+
func throwable() throws {
|
|
39
|
+
do {
|
|
40
|
+
do {
|
|
41
|
+
throw TestExceptionCause()
|
|
42
|
+
} catch {
|
|
43
|
+
throw TestExceptionCause().causedBy(error)
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
throw TestException().causedBy(error)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
expect { try throwable() }.to(throwError { error in
|
|
50
|
+
testChainedExceptionTypes(error: error, types: [TestException.self, TestExceptionCause.self, TestExceptionCause.self])
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
it("includes cause description") {
|
|
55
|
+
func throwable() throws {
|
|
56
|
+
do {
|
|
57
|
+
throw TestExceptionCause()
|
|
58
|
+
} catch {
|
|
59
|
+
throw TestException().causedBy(error)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
expect { try throwable() }.to(throwError { error in
|
|
63
|
+
if let error = error as? TestException, let cause = error.cause as? TestExceptionCause {
|
|
64
|
+
expect(error.description).to(contain(cause.description))
|
|
65
|
+
} else {
|
|
66
|
+
fail("Error and its cause are not of expected types.")
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
it("has root cause") {
|
|
72
|
+
let a = TestException()
|
|
73
|
+
let b = TestException().causedBy(a)
|
|
74
|
+
let c = TestException().causedBy(b)
|
|
75
|
+
|
|
76
|
+
expect(c.rootCause) === a
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class TestException: Exception {
|
|
82
|
+
override var reason: String {
|
|
83
|
+
"This is the test exception"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
class TestExceptionCause: Exception {
|
|
88
|
+
override var reason: String {
|
|
89
|
+
"This is the cause of the test exception"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
Tests whether the exception chain matches given types and their order.
|
|
95
|
+
*/
|
|
96
|
+
private func testChainedExceptionTypes(error: Error, types: [Error.Type]) {
|
|
97
|
+
var next: Error? = error
|
|
98
|
+
|
|
99
|
+
for errorType in types {
|
|
100
|
+
let expectedErrorTypeName = String(describing: errorType)
|
|
101
|
+
let currentErrorTypeName = String(describing: type(of: next!))
|
|
102
|
+
|
|
103
|
+
expect(currentErrorTypeName).to(equal(expectedErrorTypeName), description: "The cause is not of type \(expectedErrorTypeName)")
|
|
104
|
+
|
|
105
|
+
if let chainableException = next as? ChainableException {
|
|
106
|
+
next = chainableException.cause
|
|
107
|
+
} else {
|
|
108
|
+
next = nil
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|