expo-modules-core 0.4.8 → 0.5.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 +14 -1
- package/android/build.gradle +30 -2
- package/android/src/main/java/expo/modules/adapters/react/ModuleRegistryAdapter.java +27 -5
- package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +49 -5
- package/android/src/main/java/expo/modules/core/BasePackage.java +6 -0
- package/android/src/main/java/expo/modules/core/ModulePriorities.kt +25 -0
- package/android/src/main/java/expo/modules/core/interfaces/ActivityEventListener.java +3 -1
- package/android/src/main/java/expo/modules/core/interfaces/Package.java +4 -0
- package/android/src/main/java/expo/modules/core/interfaces/ReactActivityHandler.kt +18 -0
- package/android/src/main/java/expo/modules/core/interfaces/ReactNativeHostHandler.kt +14 -0
- package/android/src/main/java/expo/modules/core/utilities/KotlinUtilities.kt +23 -0
- package/android/src/main/java/expo/modules/kotlin/AppContext.kt +166 -0
- package/android/src/main/java/expo/modules/kotlin/DynamicExtenstions.kt +9 -0
- package/android/src/main/java/expo/modules/kotlin/ExpoModulesHelper.kt +18 -0
- package/android/src/main/java/expo/modules/kotlin/KPromiseWrapper.kt +23 -0
- package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +98 -0
- package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +41 -0
- package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +56 -0
- package/android/src/main/java/expo/modules/kotlin/ModulesProvider.kt +7 -0
- package/android/src/main/java/expo/modules/kotlin/Promise.kt +13 -0
- package/android/src/main/java/expo/modules/kotlin/ReactLifecycleDelegate.kt +39 -0
- package/android/src/main/java/expo/modules/kotlin/ReadableArrayIterator.kt +14 -0
- package/android/src/main/java/expo/modules/kotlin/ReadableTypeExtensions.kt +18 -0
- package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructor.kt +5 -0
- package/android/src/main/java/expo/modules/kotlin/allocators/ObjectConstructorFactory.kt +31 -0
- package/android/src/main/java/expo/modules/kotlin/allocators/UnsafeAllocator.kt +49 -0
- package/android/src/main/java/expo/modules/kotlin/events/EventListener.kt +39 -0
- package/android/src/main/java/expo/modules/kotlin/events/EventName.kt +31 -0
- package/android/src/main/java/expo/modules/kotlin/events/EventsDefinition.kt +3 -0
- package/android/src/main/java/expo/modules/kotlin/events/KEventEmitterWrapper.kt +26 -0
- package/android/src/main/java/expo/modules/kotlin/events/OnActivityResultPayload.kt +8 -0
- package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +70 -0
- package/android/src/main/java/expo/modules/kotlin/methods/AnyMethod.kt +50 -0
- package/android/src/main/java/expo/modules/kotlin/methods/Method.kt +14 -0
- package/android/src/main/java/expo/modules/kotlin/methods/PromiseMethod.kt +15 -0
- package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +24 -0
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +227 -0
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +16 -0
- package/android/src/main/java/expo/modules/kotlin/records/Field.kt +5 -0
- package/android/src/main/java/expo/modules/kotlin/records/Record.kt +3 -0
- package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +55 -0
- package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +14 -0
- package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +44 -0
- package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +60 -0
- package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +84 -0
- package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +25 -0
- package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +39 -0
- package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +28 -0
- package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +19 -0
- package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +107 -0
- package/android/src/main/java/expo/modules/kotlin/views/AnyViewProp.kt +10 -0
- package/android/src/main/java/expo/modules/kotlin/views/ConcreteViewProp.kt +17 -0
- package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +22 -0
- package/android/src/main/java/expo/modules/kotlin/views/SimpleViewManagerWrapper.kt +21 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +36 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +40 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +21 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewWrapperDelegateHolder.kt +5 -0
- package/build/NativeModulesProxy.native.d.ts +4 -0
- package/build/NativeModulesProxy.native.js +14 -1
- package/build/NativeModulesProxy.native.js.map +1 -1
- package/build/NativeModulesProxy.types.d.ts +3 -0
- package/build/NativeModulesProxy.types.js.map +1 -1
- package/ios/AppDelegates/EXAppDelegateWrapper.h +16 -0
- package/ios/AppDelegates/EXAppDelegateWrapper.m +42 -0
- package/ios/AppDelegates/EXAppDelegatesLoader.h +15 -0
- package/ios/AppDelegates/EXAppDelegatesLoader.m +29 -0
- package/ios/AppDelegates/EXLegacyAppDelegateWrapper.h +16 -0
- package/ios/{EXAppDelegateWrapper.m → AppDelegates/EXLegacyAppDelegateWrapper.m} +2 -2
- package/ios/AppDelegates/ExpoAppDelegate.swift +264 -0
- package/ios/AppDelegates/ExpoAppDelegateSubscriber.swift +24 -0
- package/ios/ExpoModulesCore.podspec +7 -2
- package/ios/JSI/ExpoModulesProxySpec.h +24 -0
- package/ios/JSI/ExpoModulesProxySpec.mm +135 -0
- package/ios/JSI/JSIConversions.h +42 -0
- package/ios/JSI/JSIConversions.mm +164 -0
- package/ios/JSI/JSIInstaller.h +19 -0
- package/ios/JSI/JSIInstaller.mm +22 -0
- package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +1 -6
- package/ios/NativeModulesProxy/EXNativeModulesProxy.h +6 -0
- package/ios/NativeModulesProxy/{EXNativeModulesProxy.m → EXNativeModulesProxy.mm} +45 -12
- package/ios/Services/EXReactNativeEventEmitter.h +6 -0
- package/ios/Services/EXReactNativeEventEmitter.m +15 -0
- package/ios/Swift/AppContext.swift +14 -1
- package/ios/Swift/Arguments/AnyArgument.swift +14 -0
- package/ios/Swift/Arguments/AnyArgumentType.swift +13 -0
- package/ios/Swift/Arguments/ArgumentType.swift +24 -0
- package/ios/Swift/Arguments/ConvertibleArgument.swift +15 -0
- package/ios/Swift/Arguments/Convertibles.swift +93 -0
- package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +42 -0
- package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +16 -0
- package/ios/Swift/Arguments/Types/EnumArgumentType.swift +105 -0
- package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +49 -0
- package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +15 -0
- package/ios/Swift/Arguments/Types/RawArgumentType.swift +25 -0
- package/ios/Swift/Conversions.swift +199 -7
- package/ios/Swift/EventListener.swift +37 -5
- package/ios/Swift/Functions/AnyFunction.swift +42 -0
- package/ios/Swift/{Methods/ConcreteMethod.swift → Functions/ConcreteFunction.swift} +32 -34
- package/ios/Swift/ModuleHolder.swift +75 -20
- package/ios/Swift/ModuleRegistry.swift +19 -8
- package/ios/Swift/Modules/AnyModule.swift +8 -8
- package/ios/Swift/Modules/Module.swift +7 -0
- package/ios/Swift/Modules/ModuleDefinition.swift +52 -8
- package/ios/Swift/Modules/ModuleDefinitionBuilder.swift +1 -1
- package/ios/Swift/Modules/ModuleDefinitionComponents.swift +140 -52
- package/ios/Swift/ModulesProvider.swift +9 -0
- package/ios/Swift/Promise.swift +1 -1
- package/ios/Swift/Records/Field.swift +1 -1
- package/ios/Swift/Records/Record.swift +8 -1
- package/ios/Swift/SwiftInteropBridge.swift +45 -16
- package/ios/Swift/Views/AnyViewProp.swift +2 -2
- package/ios/Swift/Views/ConcreteViewProp.swift +37 -10
- package/ios/Swift/Views/ViewModuleWrapper.swift +9 -4
- package/ios/Swift.h +9 -0
- package/ios/Tests/ArgumentTypeSpec.swift +145 -0
- package/ios/Tests/ConvertiblesSpec.swift +231 -0
- package/ios/Tests/{MethodSpec.swift → FunctionSpec.swift} +69 -54
- package/ios/Tests/FunctionWithConvertiblesSpec.swift +66 -0
- package/ios/Tests/Mocks/ModuleMocks.swift +21 -7
- package/ios/Tests/ModuleEventListenersSpec.swift +17 -16
- package/ios/Tests/ModuleRegistrySpec.swift +4 -7
- package/package.json +3 -3
- package/src/NativeModulesProxy.native.ts +22 -2
- package/src/NativeModulesProxy.types.ts +8 -0
- package/ios/EXAppDelegateWrapper.h +0 -13
- package/ios/Swift/Methods/AnyArgumentType.swift +0 -48
- package/ios/Swift/Methods/AnyMethod.swift +0 -31
- package/ios/Swift/Methods/AnyMethodArgument.swift +0 -13
|
@@ -1,30 +1,57 @@
|
|
|
1
|
+
// Copyright 2021-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
1
3
|
import UIKit
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
Specialized class for the view prop. Specifies the prop name and its setter.
|
|
5
7
|
*/
|
|
6
|
-
public class ConcreteViewProp<ViewType: UIView, PropType>: AnyViewProp {
|
|
8
|
+
public final class ConcreteViewProp<ViewType: UIView, PropType: AnyArgument>: AnyViewProp {
|
|
7
9
|
public typealias SetterType = (ViewType, PropType) -> Void
|
|
8
10
|
|
|
11
|
+
/**
|
|
12
|
+
Name of the view prop that JavaScript refers to.
|
|
13
|
+
*/
|
|
9
14
|
public let name: String
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
/**
|
|
17
|
+
An argument type wrapper for the prop's value type.
|
|
18
|
+
*/
|
|
19
|
+
private let propType: AnyArgumentType
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
Closure to call to set the actual property on the given view.
|
|
23
|
+
*/
|
|
24
|
+
private let setter: SetterType
|
|
12
25
|
|
|
13
|
-
init(
|
|
26
|
+
internal init(name: String, propType: AnyArgumentType, setter: @escaping SetterType) {
|
|
14
27
|
self.name = name
|
|
28
|
+
self.propType = propType
|
|
15
29
|
self.setter = setter
|
|
16
30
|
}
|
|
17
31
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
32
|
+
/**
|
|
33
|
+
Function that sets the underlying prop value for given view.
|
|
34
|
+
*/
|
|
35
|
+
public func set(value: Any, onView view: UIView) throws {
|
|
36
|
+
// Method's signature must be type-erased to conform to `AnyViewProp` protocol.
|
|
37
|
+
// Given view must be castable to the generic `ViewType` type.
|
|
22
38
|
guard let view = view as? ViewType else {
|
|
23
|
-
|
|
39
|
+
throw IncompatibleViewError(propName: name, viewType: ViewType.self)
|
|
24
40
|
}
|
|
25
|
-
guard let value = value as? PropType else {
|
|
26
|
-
|
|
41
|
+
guard let value = try propType.cast(value) as? PropType else {
|
|
42
|
+
throw Conversions.CastingError<PropType>(value: value)
|
|
27
43
|
}
|
|
28
44
|
setter(view, value)
|
|
29
45
|
}
|
|
30
46
|
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
An error that is thrown when the view passed to prop's setter doesn't match the type in setter's definition.
|
|
50
|
+
*/
|
|
51
|
+
internal struct IncompatibleViewError: CodedError {
|
|
52
|
+
let propName: String
|
|
53
|
+
let viewType: UIView.Type
|
|
54
|
+
var description: String {
|
|
55
|
+
"Tried to set prop `\(propName)` on the view that isn't `\(viewType)`"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -16,7 +16,7 @@ protocol DynamicModuleWrapperProtocol {
|
|
|
16
16
|
We're generating its subclasses in runtime as a workaround.
|
|
17
17
|
*/
|
|
18
18
|
@objc
|
|
19
|
-
public class ViewModuleWrapper: RCTViewManager, DynamicModuleWrapperProtocol {
|
|
19
|
+
public final class ViewModuleWrapper: RCTViewManager, DynamicModuleWrapperProtocol {
|
|
20
20
|
/**
|
|
21
21
|
A reference to the module holder that stores the module definition.
|
|
22
22
|
Enforced unwrapping is required since it can be set right after the object is initialized.
|
|
@@ -100,14 +100,19 @@ public class ViewModuleWrapper: RCTViewManager, DynamicModuleWrapperProtocol {
|
|
|
100
100
|
The setter for `proxiedProperties` prop. In Objective-C style, this function is generated by `RCT_CUSTOM_VIEW_PROPERTY` macro.
|
|
101
101
|
*/
|
|
102
102
|
@objc
|
|
103
|
-
public func set_proxiedProperties(_ json: Any
|
|
104
|
-
guard let json = json as? [String: Any
|
|
103
|
+
public func set_proxiedProperties(_ json: Any, forView view: UIView, withDefaultView defaultView: UIView) {
|
|
104
|
+
guard let json = json as? [String: Any],
|
|
105
105
|
let props = wrappedModuleHolder.definition.viewManager?.propsDict() else {
|
|
106
106
|
return
|
|
107
107
|
}
|
|
108
108
|
for (key, value) in json {
|
|
109
109
|
if let prop = props[key] {
|
|
110
|
-
|
|
110
|
+
let value = Conversions.fromNSObject(value)
|
|
111
|
+
|
|
112
|
+
// TODO: @tsapeta: Figure out better way to rethrow errors from here.
|
|
113
|
+
// Adding `throws` keyword to the function results in different
|
|
114
|
+
// method signature in Objective-C. Maybe just call `RCTLogError`?
|
|
115
|
+
try? prop.set(value: value, onView: view)
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
118
|
}
|
package/ios/Swift.h
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Copyright 2018-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
// When `use_frameworks!` is used, the generated Swift header is inside ExpoModulesCore module.
|
|
4
|
+
// Otherwise, it's available only locally with double-quoted imports.
|
|
5
|
+
#if __has_include(<ExpoModulesCore/ExpoModulesCore-Swift.h>)
|
|
6
|
+
#import <ExpoModulesCore/ExpoModulesCore-Swift.h>
|
|
7
|
+
#else
|
|
8
|
+
#import "ExpoModulesCore-Swift.h"
|
|
9
|
+
#endif
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Copyright 2021-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import Quick
|
|
4
|
+
import Nimble
|
|
5
|
+
|
|
6
|
+
@testable import ExpoModulesCore
|
|
7
|
+
|
|
8
|
+
class ArgumentTypeSpec: QuickSpec {
|
|
9
|
+
override func spec() {
|
|
10
|
+
|
|
11
|
+
it("casts primitives") {
|
|
12
|
+
let type = ArgumentType(Int.self)
|
|
13
|
+
let value = 123
|
|
14
|
+
let anyValue = value as Any
|
|
15
|
+
|
|
16
|
+
expect(try type.cast(anyValue)).to(be(value))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
it("casts optional types") {
|
|
20
|
+
let type = ArgumentType(Double?.self)
|
|
21
|
+
let value: Double? = nil
|
|
22
|
+
let anyValue = value as Any
|
|
23
|
+
let result = try type.cast(anyValue)
|
|
24
|
+
|
|
25
|
+
expect(result).to(beAKindOf(Double?.self))
|
|
26
|
+
|
|
27
|
+
// Since this `nil` is in fact of non-optional `Any` type, under the hood it's described as `Optional` enum.
|
|
28
|
+
// Simply checking `result == nil` does NOT work here, see `Optional.isNil` extension implementation.
|
|
29
|
+
expect(Optional.isNil(result)) == true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
it("throws null cast error") {
|
|
33
|
+
let type = ArgumentType(Double.self) // non-optional (!)
|
|
34
|
+
let value: Double? = nil
|
|
35
|
+
let anyValue = value as Any
|
|
36
|
+
|
|
37
|
+
expect { try type.cast(anyValue) }.to(throwError(errorType: Conversions.NullCastError<Double>.self))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
it("casts arrays") {
|
|
41
|
+
let type = ArgumentType([Double].self)
|
|
42
|
+
let value = 9.9
|
|
43
|
+
let anyValue = [value] as [Any]
|
|
44
|
+
let result = try type.cast(anyValue) as! [Any]
|
|
45
|
+
|
|
46
|
+
expect(result).to(beAKindOf([Double].self))
|
|
47
|
+
expect((result as! [Double]).first) == value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
it("casts convertibles") {
|
|
51
|
+
let type = ArgumentType(ConvertibleTestStruct.self)
|
|
52
|
+
let value = "expo is the best"
|
|
53
|
+
let result = try type.cast(value)
|
|
54
|
+
|
|
55
|
+
expect(result).to(beAKindOf(ConvertibleTestStruct.self))
|
|
56
|
+
expect((result as! ConvertibleTestStruct).value) == value
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
it("casts array of convertibles") {
|
|
60
|
+
let type = ArgumentType([ConvertibleTestStruct].self)
|
|
61
|
+
let value = ["expo is the best"]
|
|
62
|
+
let result = try type.cast(value)
|
|
63
|
+
|
|
64
|
+
expect(result).to(beAKindOf([ConvertibleTestStruct].self))
|
|
65
|
+
expect((result as! [ConvertibleTestStruct]).first!.value) == value.first
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
it("casts array of array of convertibles") {
|
|
69
|
+
let type = ArgumentType([[ConvertibleTestStruct]].self)
|
|
70
|
+
let value = [["expo is the best"]]
|
|
71
|
+
let result = try type.cast(value)
|
|
72
|
+
|
|
73
|
+
expect(result).to(beAKindOf([[ConvertibleTestStruct]].self))
|
|
74
|
+
expect((result as! [[ConvertibleTestStruct]]).first!.first!.value) == value.first!.first
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
describe("EnumArgumentType") {
|
|
78
|
+
it("casts from String") {
|
|
79
|
+
let type = ArgumentType(StringTestEnum.self)
|
|
80
|
+
let input = "expo"
|
|
81
|
+
let output = try type.cast(input)
|
|
82
|
+
|
|
83
|
+
expect(output).to(beAKindOf(StringTestEnum.self))
|
|
84
|
+
expect(output as? StringTestEnum) == StringTestEnum.expo
|
|
85
|
+
expect(output as? StringTestEnum) == StringTestEnum(rawValue: input)
|
|
86
|
+
expect((output as! StringTestEnum).rawValue) == input
|
|
87
|
+
expect((output as! EnumArgument).anyRawValue).to(beAKindOf(String.self))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
it("casts from Int") {
|
|
91
|
+
let type = ArgumentType(IntTestEnum.self)
|
|
92
|
+
let input: Int = -1
|
|
93
|
+
let output = try type.cast(input)
|
|
94
|
+
|
|
95
|
+
expect(output).to(beAKindOf(IntTestEnum.self))
|
|
96
|
+
expect(output as? IntTestEnum) == IntTestEnum.negative
|
|
97
|
+
expect(output as? IntTestEnum) == IntTestEnum(rawValue: input)
|
|
98
|
+
expect((output as! IntTestEnum).rawValue) == input
|
|
99
|
+
expect((output as! EnumArgument).anyRawValue).to(beAKindOf(Int.self))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
it("throws casting error") {
|
|
103
|
+
let type = ArgumentType(IntTestEnum.self)
|
|
104
|
+
|
|
105
|
+
// "841" is not a raw value of any `IntTestEnum` case
|
|
106
|
+
expect { try type.cast("string instead of int") }.to(throwError {
|
|
107
|
+
expect($0).to(beAKindOf(EnumCastingError.self))
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
it("throws no such value error") {
|
|
112
|
+
let type = ArgumentType(IntTestEnum.self)
|
|
113
|
+
|
|
114
|
+
// "841" is not a raw value of any `IntTestEnum` case
|
|
115
|
+
expect { try type.cast(841) }.to(throwError {
|
|
116
|
+
expect($0).to(beAKindOf(EnumNoSuchValueError.self))
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
it("gets a list of all raw values") {
|
|
121
|
+
expect(StringTestEnum.allRawValues as? [String]) == ["hello", "expo"]
|
|
122
|
+
expect(IntTestEnum.allRawValues as? [Int]) == [-1, 1]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
struct ConvertibleTestStruct: ConvertibleArgument {
|
|
129
|
+
let value: String
|
|
130
|
+
|
|
131
|
+
static func convert(from value: Any?) throws -> ConvertibleTestStruct {
|
|
132
|
+
guard let str = value as? String else { fatalError() }
|
|
133
|
+
return ConvertibleTestStruct(value: str)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
enum StringTestEnum: String, EnumArgument {
|
|
138
|
+
case hello
|
|
139
|
+
case expo
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
enum IntTestEnum: Int, EnumArgument {
|
|
143
|
+
case negative = -1
|
|
144
|
+
case positive = 1
|
|
145
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// Copyright 2018-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import CoreGraphics
|
|
4
|
+
import Quick
|
|
5
|
+
import Nimble
|
|
6
|
+
|
|
7
|
+
@testable import ExpoModulesCore
|
|
8
|
+
|
|
9
|
+
class ConvertiblesSpec: QuickSpec {
|
|
10
|
+
override func spec() {
|
|
11
|
+
describe("CGPoint") {
|
|
12
|
+
let x = -8.3
|
|
13
|
+
let y = 4.6
|
|
14
|
+
|
|
15
|
+
it("converts from array of doubles") {
|
|
16
|
+
let point = try CGPoint.convert(from: [x, y])
|
|
17
|
+
|
|
18
|
+
expect(point.x) == x
|
|
19
|
+
expect(point.y) == y
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
it("converts from dict") {
|
|
23
|
+
let point = try CGPoint.convert(from: ["x": x, "y": y])
|
|
24
|
+
|
|
25
|
+
expect(point.x) == x
|
|
26
|
+
expect(point.y) == y
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
it("throws when array size is unexpected") { // different than two
|
|
30
|
+
expect { try CGPoint.convert(from: []) }.to(throwError(errorType: Conversions.ConvertingError<CGPoint>.self))
|
|
31
|
+
expect { try CGPoint.convert(from: [x]) }.to(throwError(errorType: Conversions.ConvertingError<CGPoint>.self))
|
|
32
|
+
expect { try CGPoint.convert(from: [x, y, x]) }.to(throwError(errorType: Conversions.ConvertingError<CGPoint>.self))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
it("throws when dict is missing some keys") {
|
|
36
|
+
expect { try CGPoint.convert(from: ["test": x]) }.to(throwError {
|
|
37
|
+
expect($0).to(beAKindOf(Conversions.MissingKeysError<Double>.self))
|
|
38
|
+
expect(($0 as! CodedError).description) == Conversions.MissingKeysError<Double>(keys: ["x", "y"]).description
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
it("throws when dict has uncastable keys") {
|
|
43
|
+
expect { try CGPoint.convert(from: ["x": x, "y": "string"]) }.to(throwError {
|
|
44
|
+
expect($0).to(beAKindOf(Conversions.CastingValuesError<Double>.self))
|
|
45
|
+
expect(($0 as! CodedError).description) == Conversions.CastingValuesError<Double>(keys: ["y"]).description
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("CGSize") {
|
|
51
|
+
let width = 52.8
|
|
52
|
+
let height = 81.7
|
|
53
|
+
|
|
54
|
+
it("converts from array of doubles") {
|
|
55
|
+
let size = try CGSize.convert(from: [width, height])
|
|
56
|
+
|
|
57
|
+
expect(size.width) == width
|
|
58
|
+
expect(size.height) == height
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
it("converts from dict") {
|
|
62
|
+
let size = try CGSize.convert(from: ["width": width, "height": height])
|
|
63
|
+
|
|
64
|
+
expect(size.width) == width
|
|
65
|
+
expect(size.height) == height
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
it("throws when array size is unexpected") { // different than two
|
|
69
|
+
expect { try CGSize.convert(from: []) }.to(throwError(errorType: Conversions.ConvertingError<CGSize>.self))
|
|
70
|
+
expect { try CGSize.convert(from: [width]) }.to(throwError(errorType: Conversions.ConvertingError<CGSize>.self))
|
|
71
|
+
expect { try CGSize.convert(from: [width, height, width]) }.to(throwError(errorType: Conversions.ConvertingError<CGSize>.self))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
it("throws when dict is missing some keys") {
|
|
75
|
+
expect { try CGSize.convert(from: ["width": width]) }.to(throwError {
|
|
76
|
+
expect($0).to(beAKindOf(Conversions.MissingKeysError<Double>.self))
|
|
77
|
+
expect(($0 as! CodedError).description) == Conversions.MissingKeysError<Double>(keys: ["height"]).description
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
it("throws when dict has uncastable keys") {
|
|
82
|
+
expect { try CGSize.convert(from: ["width": "test", "height": height]) }.to(throwError {
|
|
83
|
+
expect($0).to(beAKindOf(Conversions.CastingValuesError<Double>.self))
|
|
84
|
+
expect(($0 as! CodedError).description) == Conversions.CastingValuesError<Double>(keys: ["width"]).description
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
describe("CGVector") {
|
|
90
|
+
let dx = 11.6
|
|
91
|
+
let dy = -4.0
|
|
92
|
+
|
|
93
|
+
it("converts from array of doubles") {
|
|
94
|
+
let vector = try CGVector.convert(from: [dx, dy])
|
|
95
|
+
|
|
96
|
+
expect(vector.dx) == dx
|
|
97
|
+
expect(vector.dy) == dy
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
it("converts from dict") {
|
|
101
|
+
let vector = try CGVector.convert(from: ["dx": dx, "dy": dy])
|
|
102
|
+
|
|
103
|
+
expect(vector.dx) == dx
|
|
104
|
+
expect(vector.dy) == dy
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
it("throws when array size is unexpected") { // different than two
|
|
108
|
+
expect { try CGVector.convert(from: []) }.to(throwError(errorType: Conversions.ConvertingError<CGVector>.self))
|
|
109
|
+
expect { try CGVector.convert(from: [dx]) }.to(throwError(errorType: Conversions.ConvertingError<CGVector>.self))
|
|
110
|
+
expect { try CGVector.convert(from: [dx, dy, dx]) }.to(throwError(errorType: Conversions.ConvertingError<CGVector>.self))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
it("throws when dict is missing some keys") {
|
|
114
|
+
expect { try CGVector.convert(from: ["dx": dx]) }.to(throwError {
|
|
115
|
+
expect($0).to(beAKindOf(Conversions.MissingKeysError<Double>.self))
|
|
116
|
+
expect(($0 as! CodedError).description) == Conversions.MissingKeysError<Double>(keys: ["dy"]).description
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
it("throws when dict has uncastable keys") {
|
|
121
|
+
expect { try CGVector.convert(from: ["dx": "dx", "dy": dy]) }.to(throwError {
|
|
122
|
+
expect($0).to(beAKindOf(Conversions.CastingValuesError<Double>.self))
|
|
123
|
+
expect(($0 as! CodedError).description) == Conversions.CastingValuesError<Double>(keys: ["dx"]).description
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
describe("CGRect") {
|
|
129
|
+
let x = -8.3
|
|
130
|
+
let y = 4.6
|
|
131
|
+
let width = 52.8
|
|
132
|
+
let height = 81.7
|
|
133
|
+
|
|
134
|
+
it("converts from array of doubles") {
|
|
135
|
+
let rect = try CGRect.convert(from: [x, y, width, height])
|
|
136
|
+
|
|
137
|
+
expect(rect.origin.x) == x
|
|
138
|
+
expect(rect.origin.y) == y
|
|
139
|
+
expect(rect.width) == width
|
|
140
|
+
expect(rect.height) == height
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
it("converts from dict") {
|
|
144
|
+
let rect = try CGRect.convert(from: ["x": x, "y": y, "width": width, "height": height])
|
|
145
|
+
|
|
146
|
+
expect(rect.origin.x) == x
|
|
147
|
+
expect(rect.origin.y) == y
|
|
148
|
+
expect(rect.width) == width
|
|
149
|
+
expect(rect.height) == height
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
it("throws when array size is unexpected") { // different than four
|
|
153
|
+
expect { try CGRect.convert(from: [x]) }.to(throwError(errorType: Conversions.ConvertingError<CGRect>.self))
|
|
154
|
+
expect { try CGRect.convert(from: [x, y]) }.to(throwError(errorType: Conversions.ConvertingError<CGRect>.self))
|
|
155
|
+
expect { try CGRect.convert(from: [x, y, width, height, y]) }.to(throwError(errorType: Conversions.ConvertingError<CGRect>.self))
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
it("throws when dict is missing some keys") {
|
|
159
|
+
expect { try CGRect.convert(from: ["x": x]) }.to(throwError {
|
|
160
|
+
expect($0).to(beAKindOf(Conversions.MissingKeysError<Double>.self))
|
|
161
|
+
expect(($0 as! CodedError).description) == Conversions.MissingKeysError<Double>(keys: ["y", "width", "height"]).description
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
it("throws when dict has uncastable keys") {
|
|
166
|
+
expect { try CGRect.convert(from: ["x": x, "y": nil, "width": width, "height": "\(height)"]) }.to(throwError {
|
|
167
|
+
expect($0).to(beAKindOf(Conversions.CastingValuesError<Double>.self))
|
|
168
|
+
expect(($0 as! CodedError).description) == Conversions.CastingValuesError<Double>(keys: ["y", "height"]).description
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
describe("UIColor/CGColor") {
|
|
174
|
+
func testColorComponents(_ color: CGColor, _ red: CGFloat, _ green: CGFloat, _ blue: CGFloat, _ alpha: CGFloat) {
|
|
175
|
+
expect(color.components?[0]) == red / 255.0
|
|
176
|
+
expect(color.components?[1]) == green / 255.0
|
|
177
|
+
expect(color.components?[2]) == blue / 255.0
|
|
178
|
+
expect(color.components?[3]) == alpha / 255.0
|
|
179
|
+
}
|
|
180
|
+
func testInvalidHexColor(_ hex: String) {
|
|
181
|
+
expect { try CGColor.convert(from: hex) }.to(throwError {
|
|
182
|
+
expect($0).to(beAKindOf(Conversions.InvalidHexColorError.self))
|
|
183
|
+
expect(($0 as! CodedError).description) == Conversions.InvalidHexColorError(hex: hex).description
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
it("converts from ARGB int") {
|
|
188
|
+
// NOTE: int representation has alpha channel at the beginning
|
|
189
|
+
let color = try CGColor.convert(from: 0x5147AC7F)
|
|
190
|
+
testColorComponents(color, 0x47, 0xAC, 0x7F, 0x51)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
it("converts from RGBA hex string") {
|
|
194
|
+
let color = try CGColor.convert(from: "47AC7F51")
|
|
195
|
+
testColorComponents(color, 0x47, 0xAC, 0x7F, 0x51)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
it("converts from #RGBA hex string") {
|
|
199
|
+
let color = try CGColor.convert(from: " #47AC7F51")
|
|
200
|
+
testColorComponents(color, 0x47, 0xAC, 0x7F, 0x51)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
it("converts from 3-character shorthand hex string") {
|
|
204
|
+
let color = try CGColor.convert(from: "C2B ")
|
|
205
|
+
testColorComponents(color, 0xCC, 0x22, 0xBB, 0xFF)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
it("converts from 4-character shorthand hex string") {
|
|
209
|
+
let color = try CGColor.convert(from: " #9EA5 ")
|
|
210
|
+
testColorComponents(color, 0x99, 0xEE, 0xAA, 0x55)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
it("throws when hex string is invalid") {
|
|
214
|
+
testInvalidHexColor("")
|
|
215
|
+
testInvalidHexColor("#21")
|
|
216
|
+
testInvalidHexColor("ABCDEFGH")
|
|
217
|
+
testInvalidHexColor("1122334455")
|
|
218
|
+
testInvalidHexColor("XYZ")
|
|
219
|
+
testInvalidHexColor("!@#$%")
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
it("throws when int overflows") {
|
|
223
|
+
let hex = 0xBBAA88FF2
|
|
224
|
+
expect { try CGColor.convert(from: hex) }.to(throwError {
|
|
225
|
+
expect($0).to(beAKindOf(Conversions.HexColorOverflowError.self))
|
|
226
|
+
expect(($0 as! CodedError).description) == Conversions.HexColorOverflowError(hex: UInt64(hex)).description
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|