@imgly/camera-react-native 1.81.1 → 1.82.0-rc.1
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/IMGLYCameraModule.podspec +3 -0
- package/README.md +17 -0
- package/android/build.gradle +1 -1
- package/ios/IMGLYCameraModuleImplementation.swift +47 -0
- package/ios/Model/CameraResult.swift +2 -0
- package/ios/Package.swift +95 -0
- package/ios/{IMGLYCameraModule.mm → bridge/IMGLYCameraModule.mm} +8 -17
- package/ios/bridge/IMGLYCameraModuleSupport.m +23 -0
- package/ios/bridge/include/IMGLYCameraModuleSupport.h +46 -0
- package/package.json +1 -1
- /package/ios/{IMGLYCameraModule.h → bridge/include/IMGLYCameraModule.h} +0 -0
|
@@ -14,6 +14,9 @@ Pod::Spec.new do |s|
|
|
|
14
14
|
s.source = { path: './ios' }
|
|
15
15
|
|
|
16
16
|
s.source_files = 'ios/**/*.{h,m,mm,swift}'
|
|
17
|
+
# ios/Package.swift is the manifest for React Native's experimental Swift
|
|
18
|
+
# Package Manager mode, not a source file.
|
|
19
|
+
s.exclude_files = 'ios/Package.swift'
|
|
17
20
|
|
|
18
21
|
s.dependency 'IMGLYCamera', s.version.to_s
|
|
19
22
|
|
package/README.md
CHANGED
|
@@ -24,3 +24,20 @@ const result = await IMGLYCamera.openCamera({
|
|
|
24
24
|
## Changelog
|
|
25
25
|
|
|
26
26
|
To keep up-to-date with the latest changes, visit [CHANGELOG](https://img.ly/docs/cesdk/changelog/).
|
|
27
|
+
|
|
28
|
+
## Swift Package Manager (experimental)
|
|
29
|
+
|
|
30
|
+
React Native 0.87 introduced an experimental Swift Package Manager mode (`npx react-native spm`) — see the [React Native 0.87 announcement](https://reactnative.dev/blog/2026/08/11/react-native-0.87) and the [0.87.0 release notes](https://github.com/react/react-native/releases/tag/v0.87.0). This module dual-ships a SwiftPM manifest (`ios/Package.swift`) next to its podspec, so React Native's SPM autolinking resolves it out of the box. CocoaPods currently remains the default integration path; the React Native team does not recommend the SPM mode for production yet.
|
|
31
|
+
|
|
32
|
+
The notes below describe React Native **0.87.0**. That is the version this
|
|
33
|
+
support was built and verified against. We have not verified newer React Native
|
|
34
|
+
versions, so we do not know whether these workarounds still apply there. React
|
|
35
|
+
Native calls this mode experimental and can change its commands, flags, and
|
|
36
|
+
generated layout in any release. Check the release notes of the React Native
|
|
37
|
+
version you use.
|
|
38
|
+
|
|
39
|
+
Note: React Native 0.87 pins its generated autolinking package to iOS 15 while this module requires iOS 16. Until the floor is derived from the linked libraries upstream, raise the `platforms:` entries in `react-native/scripts/spm/generate-spm-autolinking.js` to `.iOS(.v16)` (for example with `patch-package`).
|
|
40
|
+
|
|
41
|
+
Note: The app target itself must also build with an iOS 16 deployment target. The React Native template scaffolds a lower one, which works until your app code imports this module directly — for example for native customization — and then fails with a minimum-deployment-target error. Raise the **iOS Deployment Target** of the app target in Xcode to 16.0.
|
|
42
|
+
|
|
43
|
+
Note: React Native 0.87.0 ships `hermes-compiler` for bytecode version 98 while its prebuilt SwiftPM Hermes runtime expects 99, so any build with an embedded JS bundle crashes at startup. Install `hermes-compiler@^260318099` until a React Native release aligns the two.
|
package/android/build.gradle
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
#if canImport(IMGLYCameraModuleObjC)
|
|
3
|
+
// Swift Package Manager builds the Objective-C++ bridge as a separate target.
|
|
4
|
+
// Under CocoaPods both languages share one module, so no import is needed.
|
|
5
|
+
import IMGLYCameraModuleObjC
|
|
6
|
+
#endif
|
|
7
|
+
|
|
8
|
+
/// The Swift implementation behind the Objective-C++ React Native bridge.
|
|
9
|
+
///
|
|
10
|
+
/// The bridge resolves this class at runtime by its stable Objective-C name
|
|
11
|
+
/// (see `IMGLYCameraModuleImplementationProvider`), so no static
|
|
12
|
+
/// Objective-C → Swift reference exists. This is what allows building the
|
|
13
|
+
/// module with Swift Package Manager, which cannot compile mixed
|
|
14
|
+
/// Swift/Objective-C++ targets (SE-0403).
|
|
15
|
+
@objc(IMGLYCameraModuleDefaultImplementation)
|
|
16
|
+
final class IMGLYCameraModuleDefaultImplementation: NSObject, IMGLYCameraModuleImplementation {
|
|
17
|
+
func openCamera(
|
|
18
|
+
withSettings settings: [AnyHashable: Any],
|
|
19
|
+
video: String?,
|
|
20
|
+
metadata: [AnyHashable: Any]?,
|
|
21
|
+
resolve: @escaping IMGLYCameraResolveBlock,
|
|
22
|
+
reject: @escaping IMGLYCameraRejectBlock,
|
|
23
|
+
) {
|
|
24
|
+
guard let settings = settings as? [String: Any],
|
|
25
|
+
let cameraSettings = CameraSettings.fromDictionary(settings) else {
|
|
26
|
+
reject("E_PARSING", "Unable to parse the argument(s): ", nil)
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
let metadata = metadata as? [String: Any]
|
|
30
|
+
|
|
31
|
+
DispatchQueue.main.async {
|
|
32
|
+
IMGLYCameraModuleSwiftAdapter.shared.openCamera(
|
|
33
|
+
settings: cameraSettings,
|
|
34
|
+
video: video,
|
|
35
|
+
metadata: metadata,
|
|
36
|
+
) { result, error in
|
|
37
|
+
if let result {
|
|
38
|
+
resolve(result.toDictionary())
|
|
39
|
+
} else if let error {
|
|
40
|
+
reject("E_RECORDING_FAILED", "The recording failed due to: ", error)
|
|
41
|
+
} else {
|
|
42
|
+
resolve(NSNull())
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// swift-tools-version: 6.1
|
|
2
|
+
// 6.1 is the parse floor for the trailing-comma syntax SwiftFormat enforces (SE-0439). The
|
|
3
|
+
// toolchain floor comes from the IMGLYUI-swift dependency (tools 6.3.1) — do not lower this.
|
|
4
|
+
|
|
5
|
+
// Hand-authored manifest for React Native's experimental Swift Package Manager
|
|
6
|
+
// mode (React Native >= 0.87, `npx react-native spm`). CocoaPods
|
|
7
|
+
// (IMGLYCameraModule.podspec) remains the supported default integration.
|
|
8
|
+
//
|
|
9
|
+
// React Native's SPM autolinking consumes this manifest through a symlink at
|
|
10
|
+
// <app>/ios/build/generated/autolinking/libs/CameraReactNative -> this
|
|
11
|
+
// directory, so the relative package references below resolve against that
|
|
12
|
+
// location. This is React Native's documented contract for libraries that ship
|
|
13
|
+
// their own manifest (react-native/scripts/spm/__doc__/spm-header-paths-contract.md);
|
|
14
|
+
// the package and product must be named after the npm package
|
|
15
|
+
// (@imgly/camera-react-native -> CameraReactNative).
|
|
16
|
+
//
|
|
17
|
+
// The Objective-C++ bridge and the Swift implementation are separate targets
|
|
18
|
+
// because SwiftPM cannot compile mixed-language targets (SE-0403). The bridge
|
|
19
|
+
// resolves the Swift implementation at runtime (IMGLYCameraModuleSupport.h),
|
|
20
|
+
// so no Objective-C -> Swift compile-time dependency exists.
|
|
21
|
+
|
|
22
|
+
import Foundation
|
|
23
|
+
import PackageDescription
|
|
24
|
+
|
|
25
|
+
// Single-source the version from package.json, like the podspec does, so
|
|
26
|
+
// release automation bumps the IMG.LY dependency pin together with the
|
|
27
|
+
// package version. React Native's autolinking references this manifest
|
|
28
|
+
// through a symlink, so resolve it before walking up to the package root.
|
|
29
|
+
let version: Version = {
|
|
30
|
+
let packageJSON = URL(fileURLWithPath: #filePath)
|
|
31
|
+
.resolvingSymlinksInPath()
|
|
32
|
+
.deletingLastPathComponent()
|
|
33
|
+
.deletingLastPathComponent()
|
|
34
|
+
.appendingPathComponent("package.json")
|
|
35
|
+
guard let data = try? Data(contentsOf: packageJSON),
|
|
36
|
+
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
37
|
+
let versionString = json["version"] as? String,
|
|
38
|
+
let version = Version(versionString) else {
|
|
39
|
+
fatalError("Unable to read the package version from \(packageJSON.path).")
|
|
40
|
+
}
|
|
41
|
+
return version
|
|
42
|
+
}()
|
|
43
|
+
|
|
44
|
+
let package = Package(
|
|
45
|
+
name: "CameraReactNative",
|
|
46
|
+
platforms: [
|
|
47
|
+
.iOS(.v16),
|
|
48
|
+
],
|
|
49
|
+
products: [
|
|
50
|
+
.library(name: "CameraReactNative", targets: ["IMGLYCameraModule", "IMGLYCameraModuleObjC"]),
|
|
51
|
+
],
|
|
52
|
+
dependencies: [
|
|
53
|
+
.package(name: "ReactNative", path: "../../../../xcframeworks"),
|
|
54
|
+
.package(name: "React-GeneratedCode", path: "../../../ios"),
|
|
55
|
+
// Pinned exactly: the module and IMGLYUI are versioned in lockstep, matching the podspec.
|
|
56
|
+
.package(url: "https://github.com/imgly/IMGLYUI-swift.git", exact: version),
|
|
57
|
+
],
|
|
58
|
+
targets: [
|
|
59
|
+
.target(
|
|
60
|
+
name: "IMGLYCameraModuleObjC",
|
|
61
|
+
dependencies: [
|
|
62
|
+
.product(name: "ReactHeaders", package: "ReactNative"),
|
|
63
|
+
.product(name: "ReactNativeHeaders", package: "ReactNative"),
|
|
64
|
+
.product(name: "ReactNativeDependenciesHeaders", package: "ReactNative"),
|
|
65
|
+
.product(name: "ReactAppHeaders", package: "React-GeneratedCode"),
|
|
66
|
+
],
|
|
67
|
+
path: "bridge",
|
|
68
|
+
publicHeadersPath: "include",
|
|
69
|
+
cxxSettings: [
|
|
70
|
+
// React Native's SwiftPM autolinking defines this for no target, while
|
|
71
|
+
// CocoaPods defines it for every one. Without it this module would
|
|
72
|
+
// compile its legacy bridge branch and run behind the TurboModule
|
|
73
|
+
// interop layer, so the integration path would decide the dispatch
|
|
74
|
+
// mechanism. The codegen'd spec headers the branch needs come from the
|
|
75
|
+
// ReactAppHeaders product above.
|
|
76
|
+
.define("RCT_NEW_ARCH_ENABLED", to: "1"),
|
|
77
|
+
.define("DEBUG", .when(configuration: .debug)),
|
|
78
|
+
.define("NDEBUG", .when(configuration: .release)),
|
|
79
|
+
],
|
|
80
|
+
linkerSettings: [.linkedFramework("Foundation")],
|
|
81
|
+
),
|
|
82
|
+
.target(
|
|
83
|
+
name: "IMGLYCameraModule",
|
|
84
|
+
dependencies: [
|
|
85
|
+
"IMGLYCameraModuleObjC",
|
|
86
|
+
.product(name: "IMGLYCamera", package: "IMGLYUI-swift"),
|
|
87
|
+
],
|
|
88
|
+
path: ".",
|
|
89
|
+
exclude: ["bridge"],
|
|
90
|
+
),
|
|
91
|
+
],
|
|
92
|
+
// The module sources are written for the Swift 5 language mode.
|
|
93
|
+
swiftLanguageModes: [.v5],
|
|
94
|
+
cxxLanguageStandard: .cxx20,
|
|
95
|
+
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#import "IMGLYCameraModule.h"
|
|
2
|
-
#import "
|
|
2
|
+
#import "IMGLYCameraModuleSupport.h"
|
|
3
3
|
|
|
4
4
|
@implementation IMGLYCameraModule
|
|
5
5
|
|
|
@@ -52,21 +52,12 @@ RCT_EXPORT_METHOD(openCamera : (nonnull NSDictionary*)settings video : (nullable
|
|
|
52
52
|
metadata:(NSDictionary*)metadata
|
|
53
53
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
54
54
|
reject:(RCTPromiseRejectBlock)reject {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (result != nil) {
|
|
63
|
-
resolve([result toDictionary]);
|
|
64
|
-
} else if (error != nil) {
|
|
65
|
-
reject(@"E_RECORDING_FAILED", @"The recording failed due to: ", error);
|
|
66
|
-
} else {
|
|
67
|
-
resolve([NSNull null]);
|
|
68
|
-
}
|
|
69
|
-
}];
|
|
70
|
-
});
|
|
55
|
+
id<IMGLYCameraModuleImplementation> implementation = IMGLYCameraModuleImplementationProvider.implementation;
|
|
56
|
+
if (implementation == nil) {
|
|
57
|
+
reject(@"E_IMPLEMENTATION_MISSING",
|
|
58
|
+
@"The Swift implementation of the IMGLYCamera module was not linked into the app.", nil);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
[implementation openCameraWithSettings:settings video:video metadata:metadata resolve:resolve reject:reject];
|
|
71
62
|
}
|
|
72
63
|
@end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#import "IMGLYCameraModuleSupport.h"
|
|
2
|
+
|
|
3
|
+
@implementation IMGLYCameraModuleImplementationProvider
|
|
4
|
+
|
|
5
|
+
+ (nullable id<IMGLYCameraModuleImplementation>)implementation {
|
|
6
|
+
static id<IMGLYCameraModuleImplementation> implementation;
|
|
7
|
+
static dispatch_once_t onceToken;
|
|
8
|
+
dispatch_once(&onceToken, ^{
|
|
9
|
+
// The Swift class is looked up by its stable Objective-C runtime name
|
|
10
|
+
// (`IMGLYCameraModuleImplementation.swift`) because this target must not
|
|
11
|
+
// reference Swift at compile time. React Native apps link with `-ObjC` on
|
|
12
|
+
// both the CocoaPods and the Swift Package Manager path, which keeps the
|
|
13
|
+
// class alive in Release and Archive builds despite the missing static
|
|
14
|
+
// reference.
|
|
15
|
+
Class implementationClass = NSClassFromString(@"IMGLYCameraModuleDefaultImplementation");
|
|
16
|
+
if ([implementationClass conformsToProtocol:@protocol(IMGLYCameraModuleImplementation)]) {
|
|
17
|
+
implementation = [[implementationClass alloc] init];
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return implementation;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
/// Resolves a fulfilled result. Mirrors React Native's `RCTPromiseResolveBlock`
|
|
6
|
+
/// without importing React, so the Swift implementation stays React-free.
|
|
7
|
+
typedef void (^IMGLYCameraResolveBlock)(id _Nullable result);
|
|
8
|
+
|
|
9
|
+
/// Rejects with an error. Mirrors React Native's `RCTPromiseRejectBlock`.
|
|
10
|
+
typedef void (^IMGLYCameraRejectBlock)(NSString* code, NSString* message, NSError* _Nullable error);
|
|
11
|
+
|
|
12
|
+
/// The seam between the Objective-C++ React Native bridge and the Swift
|
|
13
|
+
/// implementation of the camera module.
|
|
14
|
+
///
|
|
15
|
+
/// The bridge resolves the implementation at runtime through
|
|
16
|
+
/// `IMGLYCameraModuleImplementationProvider` instead of importing the generated
|
|
17
|
+
/// `-Swift.h` header. Swift Package Manager cannot compile mixed
|
|
18
|
+
/// Swift/Objective-C++ targets (SE-0403) and never emits that header, so the
|
|
19
|
+
/// Objective-C target must not depend on Swift at compile time.
|
|
20
|
+
@protocol IMGLYCameraModuleImplementation <NSObject>
|
|
21
|
+
|
|
22
|
+
/// Opens the camera.
|
|
23
|
+
/// - Parameters:
|
|
24
|
+
/// - settings: The raw camera settings as received from JavaScript.
|
|
25
|
+
/// - video: The source of the video to react to as received from JavaScript.
|
|
26
|
+
/// - metadata: Any custom metadata as received from JavaScript.
|
|
27
|
+
/// - resolve: The block to invoke with the recording result, or `NSNull` on cancellation.
|
|
28
|
+
/// - reject: The block to invoke when parsing or the recording fails.
|
|
29
|
+
- (void)openCameraWithSettings:(NSDictionary*)settings
|
|
30
|
+
video:(nullable NSString*)video
|
|
31
|
+
metadata:(nullable NSDictionary*)metadata
|
|
32
|
+
resolve:(IMGLYCameraResolveBlock)resolve
|
|
33
|
+
reject:(IMGLYCameraRejectBlock)reject;
|
|
34
|
+
|
|
35
|
+
@end
|
|
36
|
+
|
|
37
|
+
/// Resolves the Swift implementation of the camera module at runtime.
|
|
38
|
+
@interface IMGLYCameraModuleImplementationProvider : NSObject
|
|
39
|
+
|
|
40
|
+
/// The resolved implementation, or `nil` when the Swift part of this module
|
|
41
|
+
/// was not linked into the app.
|
|
42
|
+
+ (nullable id<IMGLYCameraModuleImplementation>)implementation;
|
|
43
|
+
|
|
44
|
+
@end
|
|
45
|
+
|
|
46
|
+
NS_ASSUME_NONNULL_END
|
package/package.json
CHANGED
|
File without changes
|