@symbiote-native/splash-screen 0.1.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/LICENSE +21 -0
- package/README.md +145 -0
- package/build-ngc/angular/index.d.ts +3 -0
- package/build-ngc/angular/index.js +6 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/angular/services/hide-animation.service.d.ts +9 -0
- package/build-ngc/angular/services/hide-animation.service.js +32 -0
- package/build-ngc/angular/services/hide-animation.service.js.map +1 -0
- package/build-ngc/core/compute-hide-animation-styles.d.ts +3 -0
- package/build-ngc/core/compute-hide-animation-styles.js +78 -0
- package/build-ngc/core/compute-hide-animation-styles.js.map +1 -0
- package/build-ngc/core/hide-animation-controller.d.ts +10 -0
- package/build-ngc/core/hide-animation-controller.js +51 -0
- package/build-ngc/core/hide-animation-controller.js.map +1 -0
- package/build-ngc/core/hide.d.ts +1 -0
- package/build-ngc/core/hide.js +5 -0
- package/build-ngc/core/hide.js.map +1 -0
- package/build-ngc/core/index.d.ts +5 -0
- package/build-ngc/core/index.js +5 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +2 -0
- package/build-ngc/core/native-module.js +13 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/types.d.ts +51 -0
- package/build-ngc/core/types.js +2 -0
- package/build-ngc/core/types.js.map +1 -0
- package/cli.cjs +23 -0
- package/package.json +118 -0
- package/react-native.config.cjs +39 -0
- package/src/angular/index.ts +6 -0
- package/src/angular/services/hide-animation.service.test.ts +205 -0
- package/src/angular/services/hide-animation.service.ts +35 -0
- package/src/core/compute-hide-animation-styles.ts +111 -0
- package/src/core/hide-animation-controller.ts +70 -0
- package/src/core/hide.ts +4 -0
- package/src/core/index.ts +13 -0
- package/src/core/native-module.ts +19 -0
- package/src/core/types.ts +62 -0
- package/src/react/hooks/use-hide-animation.test.tsx +114 -0
- package/src/react/hooks/use-hide-animation.ts +40 -0
- package/src/react/index.ts +9 -0
- package/src/vue/composables/use-hide-animation.test.ts +199 -0
- package/src/vue/composables/use-hide-animation.ts +32 -0
- package/src/vue/index.ts +9 -0
- package/symbiote-splash-screen.podspec +64 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// React lifecycle wiring over the framework-agnostic HideAnimationController + style
|
|
2
|
+
// computation (core/) — mirrors the lifecycle-bucket naming convention of
|
|
3
|
+
// adapters/react/src/hooks. Faithful port of react-native-bootsplash's own useHideAnimation:
|
|
4
|
+
// the controller is constructed lazily ONCE (a useRef factory, never reconstructed across
|
|
5
|
+
// re-renders), native constants are read once via useState's lazy initializer (they never
|
|
6
|
+
// change), and an effect with NO dependency array re-syncs the controller's config after
|
|
7
|
+
// EVERY render — intentional, since `ready` flipping true only gets picked up this way.
|
|
8
|
+
//
|
|
9
|
+
// No useMemo around the style computation: upstream lists ~15 individual primitive fields
|
|
10
|
+
// (manifest.logo.width, backgroundColor, ...) as its memo's deps specifically so Object.is
|
|
11
|
+
// compares by VALUE, not by the `config`/`manifest` object's reference — callers construct
|
|
12
|
+
// that object fresh on every render (the normal way to call this hook), so keying a memo off
|
|
13
|
+
// the whole object would recompute every render anyway while looking like it memoizes.
|
|
14
|
+
// Reproducing the fine-grained list isn't worth it for a splash screen shown for a couple of
|
|
15
|
+
// renders at boot; computing plainly is honest about the actual cost and simpler to read.
|
|
16
|
+
import { useEffect, useRef, useState } from 'react';
|
|
17
|
+
import {
|
|
18
|
+
computeHideAnimationStyles,
|
|
19
|
+
getHideAnimationConstants,
|
|
20
|
+
HideAnimationController,
|
|
21
|
+
type IHideAnimationConfig,
|
|
22
|
+
type IHideAnimationConstants,
|
|
23
|
+
type IHideAnimationResult,
|
|
24
|
+
} from '../../core';
|
|
25
|
+
|
|
26
|
+
export function useHideAnimation(config: IHideAnimationConfig): IHideAnimationResult {
|
|
27
|
+
const controllerRef = useRef<HideAnimationController | null>(null);
|
|
28
|
+
if (controllerRef.current == null) {
|
|
29
|
+
controllerRef.current = new HideAnimationController(config);
|
|
30
|
+
}
|
|
31
|
+
const controller = controllerRef.current;
|
|
32
|
+
|
|
33
|
+
const [constants] = useState<IHideAnimationConstants>(() => getHideAnimationConstants());
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
controller.updateConfig(config);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return computeHideAnimationStyles(config, constants, controller);
|
|
40
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// @symbiote-native/splash-screen/react: the React entry over the framework-agnostic core.
|
|
2
|
+
// hide/isVisible carry zero lifecycle and are re-exported verbatim; useHideAnimation wraps
|
|
3
|
+
// HideAnimationController + computeHideAnimationStyles with React's own lifecycle
|
|
4
|
+
// (hooks/use-hide-animation.ts) — mirrors the lifecycle-bucket naming convention of
|
|
5
|
+
// adapters/react/src/hooks (never `composables`, that's Vue's term).
|
|
6
|
+
|
|
7
|
+
export { hide, isVisible } from '../core';
|
|
8
|
+
export { useHideAnimation } from './hooks/use-hide-animation';
|
|
9
|
+
export type { IHideAnimationConfig, IHideAnimationResult, IManifest, IHideConfig } from '../core';
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// Co-located Vue-driven test (ADR 0025) for useHideAnimation. react-native-bootsplash's
|
|
2
|
+
// TurboModule is faked (both its public JS API and the RNBootSplash native constants
|
|
3
|
+
// module HideAnimationController/computeHideAnimationStyles reach through), so no real
|
|
4
|
+
// native call fires. Proves the composable wires the framework-agnostic core's readiness
|
|
5
|
+
// gate + style computation onto Vue's own reactivity: a getter-driven `ready` ref reactively
|
|
6
|
+
// re-runs the watchEffect, hide() fires exactly once, and the returned computed reflects the
|
|
7
|
+
// core's container/logo/brand shapes faithfully — including the "no logo" skip case.
|
|
8
|
+
|
|
9
|
+
import { effectScope, nextTick, ref, type EffectScope, type Ref } from '@vue/runtime-core';
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
11
|
+
import { hide } from 'react-native-bootsplash';
|
|
12
|
+
import type { IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
13
|
+
import { useHideAnimation } from './use-hide-animation';
|
|
14
|
+
import type { IHideAnimationConfig, IManifest } from '../../core';
|
|
15
|
+
|
|
16
|
+
vi.mock('react-native-bootsplash', () => ({
|
|
17
|
+
hide: vi.fn(() => Promise.resolve()),
|
|
18
|
+
isVisible: vi.fn(() => true),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
const FAKE_NATIVE_MODULE = {
|
|
22
|
+
getConstants: vi.fn(() => ({ darkModeEnabled: false })),
|
|
23
|
+
hide: vi.fn(() => Promise.resolve()),
|
|
24
|
+
isVisible: () => true,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const registeredNativeModules: Record<string, unknown> = { RNBootSplash: FAKE_NATIVE_MODULE };
|
|
28
|
+
|
|
29
|
+
function isPresent<T>(value: unknown): value is T {
|
|
30
|
+
return value !== null && value !== undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
globalThis.__turboModuleProxy = <T>(name: string): T | null => {
|
|
35
|
+
const module = registeredNativeModules[name];
|
|
36
|
+
return isPresent<T>(module) ? module : null;
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
globalThis.__turboModuleProxy = undefined;
|
|
42
|
+
vi.clearAllMocks();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const MANIFEST: IManifest = {
|
|
46
|
+
background: '#ffffff',
|
|
47
|
+
logo: { width: 100, height: 100 },
|
|
48
|
+
brand: { bottom: 40, width: 80, height: 20 },
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// container.style is IStyleProp<IViewStyle> (union with the falsy/array style-composition
|
|
52
|
+
// shapes), but computeHideAnimationStyles always hands back a plain object here — narrow it
|
|
53
|
+
// once so the assertions can read .backgroundColor without an `as` cast.
|
|
54
|
+
function isPlainViewStyle(style: IStyleProp<IViewStyle>): style is IViewStyle {
|
|
55
|
+
return typeof style === 'object' && style !== null && !Array.isArray(style);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function asViewStyle(style: IStyleProp<IViewStyle>): IViewStyle {
|
|
59
|
+
if (!isPlainViewStyle(style)) {
|
|
60
|
+
throw new Error('expected a plain view-style object');
|
|
61
|
+
}
|
|
62
|
+
return style;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function runInScope<T>(fn: () => T): { value: T; stop: () => void } {
|
|
66
|
+
const scope: EffectScope = effectScope();
|
|
67
|
+
const value = scope.run(fn);
|
|
68
|
+
if (value === undefined) {
|
|
69
|
+
throw new Error('effectScope.run() returned undefined');
|
|
70
|
+
}
|
|
71
|
+
return { value, stop: () => scope.stop() };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe('useHideAnimation (Vue)', () => {
|
|
75
|
+
it('fires hide exactly once, only after layout + logo load + ready all become true', async () => {
|
|
76
|
+
const readyRef: Ref<boolean> = ref(false);
|
|
77
|
+
const animate = vi.fn();
|
|
78
|
+
const getConfig = (): IHideAnimationConfig => ({
|
|
79
|
+
manifest: MANIFEST,
|
|
80
|
+
logo: 1,
|
|
81
|
+
animate,
|
|
82
|
+
ready: readyRef.value,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const { value: result, stop } = runInScope(() => useHideAnimation(getConfig));
|
|
86
|
+
|
|
87
|
+
expect(hide).not.toHaveBeenCalled();
|
|
88
|
+
|
|
89
|
+
result.value.container.onLayout();
|
|
90
|
+
expect(hide, 'layout alone must not hide').not.toHaveBeenCalled();
|
|
91
|
+
|
|
92
|
+
expect(typeof result.value.logo.onLoadEnd).toBe('function');
|
|
93
|
+
result.value.logo.onLoadEnd?.();
|
|
94
|
+
expect(hide, 'layout + logo alone must not hide (ready still false)').not.toHaveBeenCalled();
|
|
95
|
+
|
|
96
|
+
readyRef.value = true;
|
|
97
|
+
await nextTick();
|
|
98
|
+
expect(hide).toHaveBeenCalledTimes(1);
|
|
99
|
+
expect(hide).toHaveBeenCalledWith({ fade: false });
|
|
100
|
+
|
|
101
|
+
stop();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('does not fire hide again once it already fired', async () => {
|
|
105
|
+
const readyRef: Ref<boolean> = ref(true);
|
|
106
|
+
const getConfig = (): IHideAnimationConfig => ({
|
|
107
|
+
manifest: MANIFEST,
|
|
108
|
+
logo: 1,
|
|
109
|
+
animate: vi.fn(),
|
|
110
|
+
ready: readyRef.value,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const { value: result, stop } = runInScope(() => useHideAnimation(getConfig));
|
|
114
|
+
|
|
115
|
+
result.value.container.onLayout();
|
|
116
|
+
result.value.logo.onLoadEnd?.();
|
|
117
|
+
await nextTick();
|
|
118
|
+
expect(hide).toHaveBeenCalledTimes(1);
|
|
119
|
+
|
|
120
|
+
// Toggling ready off and back on must not re-run the already-fired animation.
|
|
121
|
+
readyRef.value = false;
|
|
122
|
+
await nextTick();
|
|
123
|
+
readyRef.value = true;
|
|
124
|
+
await nextTick();
|
|
125
|
+
expect(hide).toHaveBeenCalledTimes(1);
|
|
126
|
+
|
|
127
|
+
stop();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('returns the container/logo/brand shapes for a config with both logo and brand', () => {
|
|
131
|
+
const getConfig = (): IHideAnimationConfig => ({
|
|
132
|
+
manifest: MANIFEST,
|
|
133
|
+
logo: 1,
|
|
134
|
+
brand: 2,
|
|
135
|
+
animate: vi.fn(),
|
|
136
|
+
ready: true,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const { value: result, stop } = runInScope(() => useHideAnimation(getConfig));
|
|
140
|
+
const { container, logo, brand } = result.value;
|
|
141
|
+
|
|
142
|
+
expect(asViewStyle(container.style).backgroundColor).toBe('#ffffff');
|
|
143
|
+
expect(typeof container.onLayout).toBe('function');
|
|
144
|
+
|
|
145
|
+
expect(logo.source).toBe(1);
|
|
146
|
+
expect(logo.style).toEqual({ width: 100, height: 100 });
|
|
147
|
+
expect(typeof logo.onLoadEnd).toBe('function');
|
|
148
|
+
|
|
149
|
+
expect(brand.source).toBe(2);
|
|
150
|
+
expect(brand.style).toEqual({
|
|
151
|
+
position: 'absolute',
|
|
152
|
+
bottom: 40,
|
|
153
|
+
width: 80,
|
|
154
|
+
height: 20,
|
|
155
|
+
});
|
|
156
|
+
expect(typeof brand.onLoadEnd).toBe('function');
|
|
157
|
+
|
|
158
|
+
stop();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('skips the logo (source -1, no onLoadEnd) when config.logo is omitted', () => {
|
|
162
|
+
const getConfig = (): IHideAnimationConfig => ({
|
|
163
|
+
manifest: MANIFEST,
|
|
164
|
+
animate: vi.fn(),
|
|
165
|
+
ready: true,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const { value: result, stop } = runInScope(() => useHideAnimation(getConfig));
|
|
169
|
+
const { logo } = result.value;
|
|
170
|
+
|
|
171
|
+
expect(logo.source).toBe(-1);
|
|
172
|
+
expect('onLoadEnd' in logo).toBe(false);
|
|
173
|
+
|
|
174
|
+
stop();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('reads the native constants exactly once, not on every recompute', () => {
|
|
178
|
+
const readyRef: Ref<boolean> = ref(false);
|
|
179
|
+
const getConfig = (): IHideAnimationConfig => ({
|
|
180
|
+
manifest: MANIFEST,
|
|
181
|
+
logo: 1,
|
|
182
|
+
animate: vi.fn(),
|
|
183
|
+
ready: readyRef.value,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const { value: result, stop } = runInScope(() => useHideAnimation(getConfig));
|
|
187
|
+
|
|
188
|
+
// Force several recomputes of the returned computed.
|
|
189
|
+
void result.value;
|
|
190
|
+
readyRef.value = true;
|
|
191
|
+
void result.value;
|
|
192
|
+
readyRef.value = false;
|
|
193
|
+
void result.value;
|
|
194
|
+
|
|
195
|
+
expect(FAKE_NATIVE_MODULE.getConstants).toHaveBeenCalledTimes(1);
|
|
196
|
+
|
|
197
|
+
stop();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Vue lifecycle wiring over the framework-agnostic HideAnimationController + style
|
|
2
|
+
// computation (core/). A Vue composable's setup body runs ONCE (unlike a React hook,
|
|
3
|
+
// which re-runs every render), so this takes a config GETTER, not a plain value — Vue's
|
|
4
|
+
// reactivity tracks whatever reactive refs the getter reads internally and re-runs the
|
|
5
|
+
// watchEffect/computed below on their change, mirroring how a React consumer would pass a
|
|
6
|
+
// fresh config object on every render.
|
|
7
|
+
//
|
|
8
|
+
// The controller is a plain local, not a Vue ref: nothing needs Vue to react to the
|
|
9
|
+
// controller reference itself (only its methods are called), so wrapping it would only
|
|
10
|
+
// add an unnecessary reactive Proxy — same identity discipline as
|
|
11
|
+
// `use-color-scheme.ts`'s subscription handle.
|
|
12
|
+
import { computed, watchEffect, type ComputedRef } from '@vue/runtime-core';
|
|
13
|
+
import {
|
|
14
|
+
computeHideAnimationStyles,
|
|
15
|
+
getHideAnimationConstants,
|
|
16
|
+
HideAnimationController,
|
|
17
|
+
type IHideAnimationConfig,
|
|
18
|
+
type IHideAnimationResult,
|
|
19
|
+
} from '../../core';
|
|
20
|
+
|
|
21
|
+
export function useHideAnimation(
|
|
22
|
+
getConfig: () => IHideAnimationConfig,
|
|
23
|
+
): ComputedRef<IHideAnimationResult> {
|
|
24
|
+
const controller = new HideAnimationController(getConfig());
|
|
25
|
+
const constants = getHideAnimationConstants();
|
|
26
|
+
|
|
27
|
+
watchEffect(() => {
|
|
28
|
+
controller.updateConfig(getConfig());
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return computed(() => computeHideAnimationStyles(getConfig(), constants, controller));
|
|
32
|
+
}
|
package/src/vue/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// @symbiote-native/splash-screen/vue: the Vue entry over the framework-agnostic core.
|
|
2
|
+
// hide/isVisible carry zero lifecycle and are re-exported verbatim; useHideAnimation
|
|
3
|
+
// wraps HideAnimationController + computeHideAnimationStyles with Vue's own reactivity
|
|
4
|
+
// (composables/use-hide-animation.ts) — mirrors the lifecycle-bucket naming convention
|
|
5
|
+
// of adapters/vue/src/composables (never `hooks/`, that's React's term).
|
|
6
|
+
|
|
7
|
+
export { hide, isVisible } from '../core';
|
|
8
|
+
export { useHideAnimation } from './composables/use-hide-animation';
|
|
9
|
+
export type { IHideAnimationConfig, IHideAnimationResult, IManifest, IHideConfig } from '../core';
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
5
|
+
|
|
6
|
+
native_splash_screen_package_json = `node --print "require.resolve('react-native-bootsplash/package.json', { paths: [process.argv[1]] })" "#{__dir__}"`.strip
|
|
7
|
+
native_splash_screen_root = File.dirname(native_splash_screen_package_json)
|
|
8
|
+
|
|
9
|
+
# CocoaPods' file glob never crosses a symlink (Sandbox::PathList#read_file_system walks the
|
|
10
|
+
# pod dir with ONE recursive Dir.glob, and Ruby's `**` never descends into a symlinked
|
|
11
|
+
# subdirectory it meets mid-walk). Under pnpm, react-native-bootsplash always sits behind a
|
|
12
|
+
# .pnpm-store symlink, so pointing source_files at it in place silently produces zero matched
|
|
13
|
+
# files -> CocoaPods downgrades the pod to an empty PBXAggregateTarget -> a runtime
|
|
14
|
+
# NSClassFromString crash on first launch, not a build error. Fix: vendor (copy) the native iOS
|
|
15
|
+
# sources into a gitignored folder next to this podspec on every `pod install`, and point
|
|
16
|
+
# source_files at that copy (a purely-downward relative pattern).
|
|
17
|
+
vendored_dir = File.join(__dir__, '.rn-bootsplash')
|
|
18
|
+
|
|
19
|
+
FileUtils.rm_rf(vendored_dir)
|
|
20
|
+
FileUtils.mkdir_p(vendored_dir)
|
|
21
|
+
FileUtils.cp_r(File.join(native_splash_screen_root, 'ios'), File.join(vendored_dir, 'ios'))
|
|
22
|
+
|
|
23
|
+
Pod::Spec.new do |s|
|
|
24
|
+
s.name = 'symbiote-splash-screen'
|
|
25
|
+
s.version = package['version']
|
|
26
|
+
s.summary = 'Symbiote wrapper for the react-native-bootsplash native splash-screen module.'
|
|
27
|
+
s.license = package['license'] || 'MIT'
|
|
28
|
+
s.authors = package['author'] || 'symbiote'
|
|
29
|
+
s.homepage = package['homepage'] || 'https://github.com/symbiote/symbiote'
|
|
30
|
+
s.platforms = { :ios => '13.4' }
|
|
31
|
+
s.source = { :git => 'https://github.com/symbiote/symbiote.git', :tag => "v#{s.version}" }
|
|
32
|
+
|
|
33
|
+
# react-native-bootsplash's own docs have callers `import RNBootSplash` directly in
|
|
34
|
+
# AppDelegate.swift — that import name is the Clang MODULE name, which CocoaPods derives
|
|
35
|
+
# from the pod's `s.name` unless told otherwise. Since this proxy pod is deliberately
|
|
36
|
+
# named after this package (`symbiote-splash-screen`, one-dependency-proxy pattern), the
|
|
37
|
+
# module would default to `symbiote_splash_screen` and break that import. Pin it back to
|
|
38
|
+
# the name upstream's native init instructions expect.
|
|
39
|
+
s.module_name = 'RNBootSplash'
|
|
40
|
+
|
|
41
|
+
s.source_files = '.rn-bootsplash/ios/**/*.{h,m,mm}'
|
|
42
|
+
|
|
43
|
+
if defined?(install_modules_dependencies)
|
|
44
|
+
install_modules_dependencies(s)
|
|
45
|
+
else
|
|
46
|
+
s.dependency 'React-Core'
|
|
47
|
+
s.compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32 -DRCT_NEW_ARCH_ENABLED=1'
|
|
48
|
+
s.pod_target_xcconfig = {
|
|
49
|
+
'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/boost"',
|
|
50
|
+
'OTHER_CPLUSPLUSFLAGS' => '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1',
|
|
51
|
+
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17'
|
|
52
|
+
}
|
|
53
|
+
s.dependency 'ReactCommon/turbomodule/core'
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# `module_name` alone only picks the NAME of the Clang module — it does not make CocoaPods
|
|
57
|
+
# generate one. For a static-library pod (no use_frameworks!), a module map is only emitted
|
|
58
|
+
# when DEFINES_MODULE is YES, and install_modules_dependencies(s) does not set it for us.
|
|
59
|
+
# Without this, `import RNBootSplash` in AppDelegate.swift fails with "no such module".
|
|
60
|
+
# `pod_target_xcconfig` has a DSL writer but no plain reader, so read the raw value back
|
|
61
|
+
# through `attributes_hash` (what install_modules_dependencies(s) already wrote into)
|
|
62
|
+
# instead of `s.pod_target_xcconfig`, which raises NoMethodError.
|
|
63
|
+
s.pod_target_xcconfig = (s.attributes_hash['pod_target_xcconfig'] || {}).merge('DEFINES_MODULE' => 'YES')
|
|
64
|
+
end
|