@appattest/react-native 0.1.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/LICENSE +21 -0
- package/README.md +150 -0
- package/ios/AppAttestModule.mm +52 -0
- package/ios/AppAttestModule.swift +173 -0
- package/ios/AppAttestReactNative.podspec +34 -0
- package/lib/commonjs/NativeAppAttest.js +20 -0
- package/lib/commonjs/NativeAppAttest.js.map +1 -0
- package/lib/commonjs/index.js +245 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/module/NativeAppAttest.js +17 -0
- package/lib/module/NativeAppAttest.js.map +1 -0
- package/lib/module/index.js +237 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/NativeAppAttest.d.ts +54 -0
- package/lib/typescript/NativeAppAttest.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +115 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/package.json +76 -0
- package/src/NativeAppAttest.ts +70 -0
- package/src/index.ts +282 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bault LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# @appattest/react-native
|
|
2
|
+
|
|
3
|
+
React Native bridge for [AppAttest](https://www.appattest.dev) — App-Attest-gated
|
|
4
|
+
secret delivery for iOS.
|
|
5
|
+
|
|
6
|
+
> **Status: pre-release.** Ships in lockstep with the Swift SDK at
|
|
7
|
+
> `v0.1.0`. Not yet published to npm.
|
|
8
|
+
> The JS/TS API shape is stable.
|
|
9
|
+
|
|
10
|
+
## Platform support
|
|
11
|
+
|
|
12
|
+
- **iOS 17+** — full support. Uses Apple's `DCAppAttestService` via the
|
|
13
|
+
native AppAttest Swift SDK.
|
|
14
|
+
- **Android / other** — not supported. Apple's App Attest is iOS-only and
|
|
15
|
+
there is no equivalent on other platforms; the native module is not
|
|
16
|
+
registered outside iOS.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
> Not yet on npm — publication is upcoming. Until then the bridge ships as
|
|
21
|
+
> source in `bridges/react-native` of the SDK repository. Once published:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @appattest/react-native
|
|
25
|
+
cd ios && pod install
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The iOS pod depends on `AppAttestObjC` (a companion pod from the same
|
|
29
|
+
monorepo). That's wired automatically through the pod's `s.dependency`.
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { AppAttest, useSecret } from '@appattest/react-native';
|
|
35
|
+
|
|
36
|
+
// Once, at app launch:
|
|
37
|
+
AppAttest.start();
|
|
38
|
+
|
|
39
|
+
// In any component — re-renders when secrets land:
|
|
40
|
+
function Chat() {
|
|
41
|
+
const openaiKey = useSecret('OPENAI_API_KEY');
|
|
42
|
+
if (!openaiKey) return <Splash />;
|
|
43
|
+
return <ChatView apiKey={openaiKey} />;
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Outside components:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
await AppAttest.waitForReady();
|
|
51
|
+
const key = await AppAttest.getSecret('OPENAI_API_KEY'); // string | null
|
|
52
|
+
const all = await AppAttest.getAllSecrets(); // Record<string, string>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`start()` is fire-and-forget: the first launch attests the device once
|
|
56
|
+
(persists across launches), then syncs secrets; later launches hydrate
|
|
57
|
+
from the Keychain and re-sync in the background. Foreground re-entry
|
|
58
|
+
re-syncs automatically — your app does no lifecycle wiring.
|
|
59
|
+
|
|
60
|
+
## State
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { AppAttest, useAppAttestState } from '@appattest/react-native';
|
|
64
|
+
|
|
65
|
+
const state = useAppAttestState(); // { name, error? }, re-renders on change
|
|
66
|
+
|
|
67
|
+
// or imperatively:
|
|
68
|
+
const s = await AppAttest.getState();
|
|
69
|
+
const unsubscribe = AppAttest.addStateListener((s) => console.log(s.name));
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`state.name` is one of `'initializing' | 'attesting' | 'syncing' | 'ready' |
|
|
73
|
+
'subscription_required' | 'credits_required' | 'unavailable'`. The
|
|
74
|
+
non-`ready` terminal states carry `state.error`.
|
|
75
|
+
|
|
76
|
+
**End-user-facing apps:** show a generic "temporarily unavailable" notice
|
|
77
|
+
for the non-`ready` terminal states. **Developer / staff builds:** log the
|
|
78
|
+
full error (including `actionUrl`) so the developer knows whether to
|
|
79
|
+
subscribe, top up, or investigate.
|
|
80
|
+
|
|
81
|
+
## Refresh & recovery
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
await AppAttest.retry(); // re-run the sync (no re-attestation)
|
|
85
|
+
await AppAttest.invalidateBundle(); // drop the cached bundle, force a fresh sync
|
|
86
|
+
await AppAttest.reset(); // full wipe; next start() re-attests
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`retry()` recovers from transient failures. `invalidateBundle()` forces
|
|
90
|
+
fresh secret bytes when you don't want to wait for the next rotation
|
|
91
|
+
pickup. `reset()` is the nuclear option, for sign-out / data-clearing flows.
|
|
92
|
+
|
|
93
|
+
## Debug mode (simulator, tests, CI)
|
|
94
|
+
|
|
95
|
+
The simulator can't produce a real App Attest attestation. Use local stubs:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
if (__DEV__) {
|
|
99
|
+
await AppAttest.setDebugMode('local', {
|
|
100
|
+
OPENAI_API_KEY: 'sk-test-stub',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
AppAttest.start();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Pass `null` to return to real attestation. The native debug surface is
|
|
107
|
+
`#if DEBUG`-gated — physically absent from Release builds, which always
|
|
108
|
+
run real attestation; calling it there rejects with
|
|
109
|
+
`debug_mode_release_blocked`.
|
|
110
|
+
|
|
111
|
+
Dev builds on **real devices** don't need debug mode — they attest for
|
|
112
|
+
real and read the sandbox bucket (below).
|
|
113
|
+
|
|
114
|
+
## Buckets (sandbox vs production)
|
|
115
|
+
|
|
116
|
+
There is no environment configuration. Apple's AAGUID in each attestation
|
|
117
|
+
determines the bucket server-side: dev / TestFlight builds read the
|
|
118
|
+
**sandbox** secrets column; App Store builds read **production**. Same
|
|
119
|
+
code in both, no flags.
|
|
120
|
+
|
|
121
|
+
## Error handling
|
|
122
|
+
|
|
123
|
+
All methods reject with `AppAttestError` (`code`, plus `subscribeUrl` /
|
|
124
|
+
`topupUrl` / `actionUrl` on the billing cases):
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { AppAttest, AppAttestError, ErrorCode } from '@appattest/react-native';
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
await AppAttest.waitForReady();
|
|
131
|
+
} catch (e) {
|
|
132
|
+
if (e instanceof AppAttestError && e.code === ErrorCode.SubscriptionRequired) {
|
|
133
|
+
console.log('project needs a subscription:', e.actionUrl);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
| Code | Meaning |
|
|
139
|
+
|------|---------|
|
|
140
|
+
| `subscription_required` | Project subscription not active (`subscribeUrl`). |
|
|
141
|
+
| `credits_required` | Allowance exhausted and balance empty (`topupUrl`). |
|
|
142
|
+
| `attestation_rejected` | Apple or AppAttest rejected this install — terminal until reinstall. |
|
|
143
|
+
| `service_unavailable` | Temporary service condition; retryable (the SDK backs off automatically). |
|
|
144
|
+
| `network` | Device-side transport failure; retryable. |
|
|
145
|
+
| `debug_mode_release_blocked` | `setDebugMode` called in a Release build. |
|
|
146
|
+
| `invalid_argument` | Malformed call input. |
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT © 2026 Bault LLC. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// AppAttestModule.mm — React Native module registration macros.
|
|
2
|
+
//
|
|
3
|
+
// Exposes the Swift class `AppAttestModule` to RN as `RNAppAttest`
|
|
4
|
+
// (matches the JS-side TurboModuleRegistry.getEnforcing<Spec>('RNAppAttest')).
|
|
5
|
+
// Each RCT_EXTERN_METHOD declares a method the RN runtime may invoke and
|
|
6
|
+
// its argument shape. Keep in lockstep with ../src/NativeAppAttest.ts.
|
|
7
|
+
|
|
8
|
+
#import <React/RCTBridgeModule.h>
|
|
9
|
+
#import <React/RCTEventEmitter.h>
|
|
10
|
+
|
|
11
|
+
#if __has_include(<React/RCTBridgeModule.h>)
|
|
12
|
+
@interface RCT_EXTERN_REMAP_MODULE(RNAppAttest, AppAttestModule, RCTEventEmitter)
|
|
13
|
+
|
|
14
|
+
RCT_EXTERN_METHOD(start:(RCTPromiseResolveBlock)resolve
|
|
15
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
16
|
+
|
|
17
|
+
RCT_EXTERN_METHOD(waitForReady:(RCTPromiseResolveBlock)resolve
|
|
18
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
19
|
+
|
|
20
|
+
RCT_EXTERN_METHOD(retry:(RCTPromiseResolveBlock)resolve
|
|
21
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
22
|
+
|
|
23
|
+
RCT_EXTERN_METHOD(reset:(RCTPromiseResolveBlock)resolve
|
|
24
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
25
|
+
|
|
26
|
+
RCT_EXTERN_METHOD(invalidateBundle:(RCTPromiseResolveBlock)resolve
|
|
27
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
28
|
+
|
|
29
|
+
RCT_EXTERN_METHOD(getSecret:(NSString *)name
|
|
30
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
31
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
32
|
+
|
|
33
|
+
RCT_EXTERN_METHOD(getAllSecrets:(RCTPromiseResolveBlock)resolve
|
|
34
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
35
|
+
|
|
36
|
+
RCT_EXTERN_METHOD(getState:(RCTPromiseResolveBlock)resolve
|
|
37
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
38
|
+
|
|
39
|
+
RCT_EXTERN_METHOD(setDebugMode:(nullable NSString *)name
|
|
40
|
+
stubs:(nullable NSDictionary *)stubs
|
|
41
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
42
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
43
|
+
|
|
44
|
+
// setApiBaseUrl is not exposed — base URL is hardcoded.
|
|
45
|
+
|
|
46
|
+
+ (BOOL)requiresMainQueueSetup
|
|
47
|
+
{
|
|
48
|
+
return NO;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@end
|
|
52
|
+
#endif
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// AppAttestModule.swift — iOS native module for @appattest/react-native.
|
|
2
|
+
//
|
|
3
|
+
// Adapter between React Native's module system and the AppAttest SDK's
|
|
4
|
+
// @objc facade (AppAttestObjC). Every method matches the TurboModule spec
|
|
5
|
+
// in ../src/NativeAppAttest.ts.
|
|
6
|
+
//
|
|
7
|
+
// Errors: rejection bodies carry { code, message,
|
|
8
|
+
// subscribeUrl? | topupUrl? }. The 402 envelope key matches the code:
|
|
9
|
+
// `subscription_required` → subscribeUrl; `credits_required` → topupUrl.
|
|
10
|
+
//
|
|
11
|
+
// State events: emitted on `stateChanged` whenever the SDK's state
|
|
12
|
+
// transitions. JS hooks subscribe via the `addStateListener` wrapper.
|
|
13
|
+
|
|
14
|
+
import Foundation
|
|
15
|
+
import AppAttestObjC
|
|
16
|
+
import React
|
|
17
|
+
|
|
18
|
+
@objc(AppAttestModule)
|
|
19
|
+
final class AppAttestModule: RCTEventEmitter {
|
|
20
|
+
|
|
21
|
+
private let client = AppAttestObjCClient.shared
|
|
22
|
+
private var stateToken: AppAttestObservationToken?
|
|
23
|
+
private var hasJSListeners = false
|
|
24
|
+
|
|
25
|
+
override static func requiresMainQueueSetup() -> Bool { false }
|
|
26
|
+
|
|
27
|
+
// MARK: - Event emitter
|
|
28
|
+
|
|
29
|
+
override func supportedEvents() -> [String]! { ["stateChanged"] }
|
|
30
|
+
|
|
31
|
+
override func startObserving() {
|
|
32
|
+
hasJSListeners = true
|
|
33
|
+
stateToken?.invalidate()
|
|
34
|
+
stateToken = client.addStateObserver { [weak self] state in
|
|
35
|
+
guard let self else { return }
|
|
36
|
+
self.sendEvent(withName: "stateChanged", body: Self.encodeState(state))
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// Encode an `AppAttestState` for the JS bridge — flattens the nested
|
|
41
|
+
/// NSError userInfo into a JSON-safe dict. Forwards every 402 URL key
|
|
42
|
+
/// (`subscribeUrl`, `topupUrl`); JS picks whichever matches its current
|
|
43
|
+
/// state.
|
|
44
|
+
private static func encodeState(_ state: AppAttestState) -> [String: Any] {
|
|
45
|
+
var body: [String: Any] = ["name": state.name]
|
|
46
|
+
if let err = state.error {
|
|
47
|
+
var errBody: [String: Any] = [
|
|
48
|
+
"code": (err.userInfo["code"] as? String) ?? "internal_error",
|
|
49
|
+
"message": (err.userInfo["message"] as? String) ?? err.localizedDescription
|
|
50
|
+
]
|
|
51
|
+
if let url = err.userInfo["subscribeUrl"] as? String { errBody["subscribeUrl"] = url }
|
|
52
|
+
if let url = err.userInfo["topupUrl"] as? String { errBody["topupUrl"] = url }
|
|
53
|
+
body["error"] = errBody
|
|
54
|
+
}
|
|
55
|
+
return body
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override func stopObserving() {
|
|
59
|
+
hasJSListeners = false
|
|
60
|
+
stateToken?.invalidate()
|
|
61
|
+
stateToken = nil
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// MARK: - Lifecycle
|
|
65
|
+
|
|
66
|
+
@objc func start(
|
|
67
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
68
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
69
|
+
) {
|
|
70
|
+
client.start()
|
|
71
|
+
resolve(NSNull())
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@objc func waitForReady(
|
|
75
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
76
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
77
|
+
) {
|
|
78
|
+
client.waitForReady { error in
|
|
79
|
+
Self.complete((), error: error, resolve: resolve, reject: reject)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@objc func retry(
|
|
84
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
85
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
86
|
+
) {
|
|
87
|
+
client.retry()
|
|
88
|
+
resolve(NSNull())
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@objc func reset(
|
|
92
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
93
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
94
|
+
) {
|
|
95
|
+
client.reset { error in
|
|
96
|
+
Self.complete((), error: error, resolve: resolve, reject: reject)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@objc func invalidateBundle(
|
|
101
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
102
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
103
|
+
) {
|
|
104
|
+
client.invalidateBundle()
|
|
105
|
+
resolve(NSNull())
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// MARK: - Reads
|
|
109
|
+
|
|
110
|
+
@objc func getSecret(
|
|
111
|
+
_ name: NSString,
|
|
112
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
113
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
114
|
+
) {
|
|
115
|
+
DispatchQueue.main.async {
|
|
116
|
+
let value = self.client.secret(forKey: name as String)
|
|
117
|
+
resolve(value as Any? ?? NSNull())
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@objc func getAllSecrets(
|
|
122
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
123
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
124
|
+
) {
|
|
125
|
+
DispatchQueue.main.async {
|
|
126
|
+
resolve(self.client.allSecrets())
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@objc func getState(
|
|
131
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
132
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
133
|
+
) {
|
|
134
|
+
DispatchQueue.main.async {
|
|
135
|
+
resolve(Self.encodeState(self.client.currentState()))
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// MARK: - Configuration
|
|
140
|
+
|
|
141
|
+
@objc func setDebugMode(
|
|
142
|
+
_ name: NSString?,
|
|
143
|
+
stubs: NSDictionary?,
|
|
144
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
145
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
146
|
+
) {
|
|
147
|
+
let dict = (stubs as? [String: String]) ?? [:]
|
|
148
|
+
client.setDebugMode(name as String?, stubs: dict.isEmpty ? nil : dict) { error in
|
|
149
|
+
Self.complete((), error: error, resolve: resolve, reject: reject)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// setApiBaseUrl is not exposed — the base URL is hardcoded in the
|
|
154
|
+
// Swift SDK (https://edge.appattest.dev). No way to point at any
|
|
155
|
+
// other URL from a published binary; that's the security model.
|
|
156
|
+
|
|
157
|
+
// MARK: - Promise plumbing
|
|
158
|
+
|
|
159
|
+
private static func complete<T>(
|
|
160
|
+
_ value: T,
|
|
161
|
+
error: NSError?,
|
|
162
|
+
resolve: RCTPromiseResolveBlock,
|
|
163
|
+
reject: RCTPromiseRejectBlock
|
|
164
|
+
) {
|
|
165
|
+
if let error {
|
|
166
|
+
let code = (error.userInfo["code"] as? String) ?? "internal_error"
|
|
167
|
+
let message = (error.userInfo["message"] as? String) ?? error.localizedDescription
|
|
168
|
+
reject(code, message, error)
|
|
169
|
+
} else {
|
|
170
|
+
resolve(value)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'AppAttestReactNative'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.homepage = 'https://github.com/AppAttest/appAttest-sdk'
|
|
10
|
+
s.license = 'MIT'
|
|
11
|
+
s.author = { 'AppAttest' => 'support@appattest.dev' }
|
|
12
|
+
s.source = {
|
|
13
|
+
:git => 'https://github.com/AppAttest/appAttest-sdk.git',
|
|
14
|
+
:tag => "v#{s.version}"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
s.platforms = { :ios => '17.0' }
|
|
18
|
+
s.swift_versions = ['5.9']
|
|
19
|
+
|
|
20
|
+
s.source_files = '**/*.{swift,m,mm,h}'
|
|
21
|
+
|
|
22
|
+
# Depend on the Swift SDK's Objective-C facade. The facade transitively
|
|
23
|
+
# pulls in `AppAttest` (the pure Swift module).
|
|
24
|
+
s.dependency 'AppAttestObjC', "= #{s.version}"
|
|
25
|
+
|
|
26
|
+
# React Native core — this is the standard pattern from @react-native/
|
|
27
|
+
# official modules. `install_modules_dependencies` is exposed by RN's
|
|
28
|
+
# podspec helpers and ensures new-arch / TurboModule deps are wired.
|
|
29
|
+
if respond_to?(:install_modules_dependencies, true)
|
|
30
|
+
install_modules_dependencies(s)
|
|
31
|
+
else
|
|
32
|
+
s.dependency 'React-Core'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
/**
|
|
9
|
+
* TurboModule spec for the AppAttest iOS native module.
|
|
10
|
+
*
|
|
11
|
+
* Codegen reads this file. The shape is restricted to what TurboModule
|
|
12
|
+
* codegen can serialize (primitives, Object, arrays of primitives,
|
|
13
|
+
* Promises, null).
|
|
14
|
+
*
|
|
15
|
+
* Errors: rejection objects carry `{ code, message,
|
|
16
|
+
* subscribeUrl?, topupUrl? }`. The TS wrapper in `index.ts` translates
|
|
17
|
+
* these into `AppAttestError`.
|
|
18
|
+
*/
|
|
19
|
+
var _default = exports.default = _reactNative.TurboModuleRegistry.getEnforcing('RNAppAttest');
|
|
20
|
+
//# sourceMappingURL=NativeAppAttest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeAppAttest.ts"],"mappings":";;;;;;AAYA,IAAAA,YAAA,GAAAC,OAAA;AAZA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAVA,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAqEeC,gCAAmB,CAACC,YAAY,CAAO,aAAa,CAAC","ignoreList":[]}
|