本文へ移動
cccskills
無料GitHub で公開

rn-platform-specific

iOS / Android platform-specific code in React Native: Platform.OS, Platform.select, .ios.tsx / .android.tsx file extensions, native modules, permissions, safe area handling, status bar. Use this skill to: - Branch code at runtime via Platform.OS / Platform.select. - Use file extensions for whole-component swaps. - Link and use native modules (Expo SDK or autolinked bare). - Handle permissions across platforms. - Configure status bar and safe area correctly. Do NOT use this skill for: - General project structure (see rn-conventions). - Navigation (see rn-navigation). - Storage (see rn-state-and-storage). - Testing (see rn-testing).

インストール方法を見る

含まれるファイル(1)

  • SKILL.md9.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Platform-Specific Patterns

iOS and Android have real differences. RN abstracts most, but sometimes you need to branch.

Platform.OS

import { Platform } from 'react-native';

console.log(Platform.OS);                  // 'ios' | 'android' | 'web' | 'windows' | 'macos'

if (Platform.OS === 'ios') {
  // iOS-only logic
}

Platform.OS is set at runtime by RN. Use for small branches:

const elevation = Platform.OS === 'android' ? { elevation: 4 } : { shadowOpacity: 0.1 };

Don't fork entire components for 5 lines of difference — use Platform.select or inline conditionals.

Platform.select

Declarative platform branching:

const styles = StyleSheet.create({
  card: {
    padding: 16,
    backgroundColor: '#fff',
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: { width: 0, height: 2 },
        shadowOpacity: 0.1,
        shadowRadius: 4,
      },
      android: {
        elevation: 4,
      },
      default: {},   // web, windows, macos
    }),
  },
});

Each key returns a value; Platform.select picks the matching one.

Platform.Version

Numeric on Android (API level: 28, 30, 33, 34), string on iOS (e.g. '17.0'):

if (Platform.OS === 'android' && Platform.Version >= 33) {
  // Android 13+ specific behavior
}

if (Platform.OS === 'ios' && parseInt(Platform.Version as string) >= 17) {
  // iOS 17+ specific
}

File extensions

For whole-component swaps, Metro bundler picks files based on extension:

src/
├── components/
│   ├── DatePicker.tsx              # default for all platforms
│   ├── DatePicker.ios.tsx          # iOS-only override
│   ├── DatePicker.android.tsx      # Android-only override
│   ├── DatePicker.native.tsx       # native (iOS + Android, NOT web)
│   └── DatePicker.web.tsx          # web (RN Web)
// import is the same — Metro resolves to the right file
import { DatePicker } from './components/DatePicker';

Use file extensions when:

  • The two platforms need genuinely different markup.
  • Native modules differ per platform.
  • Otherwise, prefer Platform.select or inline conditionals — easier to read in one file.

Native modules

Expo SDK (managed)

Pre-installed, no native linking needed. Just install the JS package:

pnpm add expo-camera expo-location expo-notifications
import * as Camera from 'expo-camera';
const [permission, requestPermission] = Camera.useCameraPermissions();

For custom native code in managed workflow, you need to migrate to dev-client or eject to bare.

Bare RN (autolinking)

RN 0.60+ has autolinking — installing a package via npm/yarn/pnpm wires it up automatically:

pnpm add react-native-camera
cd ios && pod install                          # iOS only — install CocoaPods deps

After install:

  • Run npm run ios / npm run android to rebuild with new native module.
  • Restart Metro (npm start --reset-cache if cached).

Some packages need additional native config (manifest entries, Info.plist keys). Check package README.

Custom native modules

Bare: write Objective-C/Swift (iOS) and Java/Kotlin (Android) modules. Beyond the scope of this skill.

Expo: write a config plugin that injects native code via the prebuild step. See expo-build-properties and withDangerousMod examples.

Permissions

Expo

Each feature-specific package handles its own permission flow:

import * as Camera from 'expo-camera';
const [permission, requestPermission] = Camera.useCameraPermissions();
if (!permission?.granted) {
  await requestPermission();
}

import * as Location from 'expo-location';
const { status } = await Location.requestForegroundPermissionsAsync();

Declare permission descriptions in app.json:

{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSCameraUsageDescription": "Allow $(PRODUCT_NAME) to access your camera",
        "NSLocationWhenInUseUsageDescription": "Allow location access for map features"
      }
    },
    "android": {
      "permissions": ["CAMERA", "ACCESS_FINE_LOCATION"]
    }
  }
}

Bare

Use react-native-permissions for unified API:

import { check, request, RESULTS, PERMISSIONS } from 'react-native-permissions';

const status = await check(PERMISSIONS.IOS.CAMERA);
if (status !== RESULTS.GRANTED) {
  await request(PERMISSIONS.IOS.CAMERA);
}

Configure Info.plist (iOS) and AndroidManifest.xml (Android) with the right keys / permissions.

Safe area

The notch (iPhone X+) and rounded corners (iPad/iPhone) eat into screen real estate. iOS has the home indicator; Android may have on-screen nav bar or gesture area.

react-native-safe-area-context (preferred)

// Wrap app once at root
import { SafeAreaProvider } from 'react-native-safe-area-context';

export default function App() {
  return (
    <SafeAreaProvider>
      <RootNavigator />
    </SafeAreaProvider>
  );
}

// Use SafeAreaView in screens
import { SafeAreaView } from 'react-native-safe-area-context';

<SafeAreaView style={{ flex: 1 }} edges={['top', 'bottom']}>
  {/* screen content */}
</SafeAreaView>

// Or insets for fine control
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const insets = useSafeAreaInsets();
<View style={{ paddingTop: insets.top, paddingBottom: insets.bottom }}>...</View>

The RN-built-in SafeAreaView from react-native is deprecated — don't use it.

edges prop: which edges to apply safe area padding. Often ['top'] for screens with bottom tab nav (tab nav handles bottom safe area).

Keyboard handling

import { KeyboardAvoidingView, Platform } from 'react-native';

<KeyboardAvoidingView
  behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
  style={{ flex: 1 }}
  keyboardVerticalOffset={64}                  // header height
>
  {/* form */}
</KeyboardAvoidingView>

For more control, use react-native-keyboard-controller:

import { KeyboardProvider } from 'react-native-keyboard-controller';
// Wrap app, provides better keyboard event APIs and animations.

Status bar

Expo

import { StatusBar } from 'expo-status-bar';
<StatusBar style="auto" />

style: 'auto' (matches color scheme), 'light', 'dark', 'inverted'.

Bare

import { StatusBar } from 'react-native';
<StatusBar barStyle="dark-content" backgroundColor="#fff" translucent={false} />

backgroundColor is Android-only; iOS uses the underlying view's color.

Pixel ratio and dimensions

import { Dimensions, useWindowDimensions, PixelRatio } from 'react-native';

// One-time read (doesn't update on rotation)
const { width, height } = Dimensions.get('window');

// Reactive — updates on rotation/resize
const { width, height } = useWindowDimensions();

// For native conversions (rare)
const px = PixelRatio.getPixelSizeForLayoutSize(50);

Use useWindowDimensions for layouts that adapt to orientation.

Common platform pitfalls

IssuePlatformsFix
Shadows differiOS uses shadowColor/Offset/Opacity; Android uses elevationPlatform.select
Status bar overlaps contentiOS by default doesn't; Android translucent doesSet translucent={false} or wrap in SafeAreaView
Back buttonAndroid has hardware back; iOS doesn'tBackHandler (Android) for custom logic; React Navigation handles automatically
Date/time pickeriOS shows wheel; Android shows native dialog@react-native-community/datetimepicker handles both
Keyboard appearanceiOS animates over content; Android may resize layoutKeyboardAvoidingView with behavior per platform
Linking external appsiOS has stricter URL scheme rulesLinking.canOpenURL before openURL; declare allowed schemes in LSApplicationQueriesSchemes (iOS)
Push notificationsDifferent APNs (iOS) vs FCM (Android) tokensUse expo-notifications or react-native-firebase for unified API
Notch / Dynamic Island (iOS)Only iOSSafeAreaView handles automatically

Anti-patterns

  • ❌ Forking entire screens for Platform.OS === 'ios' when 90% of code is shared — use inline conditionals or .ios.tsx/.android.tsx only for genuinely different markup.
  • ❌ Ignoring safe areas — content clipped by notch/home indicator.
  • ❌ Calling NativeModules.X directly without Platform check — module may not exist on the other platform = crash.
  • ❌ Using web-only positioning (position: 'fixed').
  • ❌ Hardcoding pixel values without considering pixel ratio (use density-independent units; RN's "px" already handles this).
  • ❌ Forgetting to declare permission usage strings in app.json / Info.plist — App Store / Play Store rejection.
  • ❌ Assuming Android back button works without handling — BackHandler.addEventListener('hardwareBackPress', ...).
  • ❌ Mixing the deprecated SafeAreaView from react-native with react-native-safe-area-context.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Angular 18-21 project structure, standalone components vs NgModule, control flow (@if/@for/@switch + *ngIf/*ngFor legacy), decorators, dependency injection (inject() function), lifecycle hooks, pipes, Angular Universal SSR pointer. Use this skill to: - Detect project style (standalone vs NgModule) and apply matching patterns. - Pick correct decorators and DI approach. - Use modern control flow (@if/@for/@switch) in Angular 17+ projects. - Apply `inject()` function over constructor injection where appropriate. - Wire bootstrap correctly (bootstrapApplication for standalone, AppModule for legacy). Do NOT use this skill for: - State management (see angular-state-and-rx). - Routing (see angular-routing). - Forms (see angular-forms). - Testing (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Angular forms: Reactive Forms (preferred — typed FormGroup/FormControl since Angular 14, FormBuilder, custom + async validators, FormArray, multi-step) and Template-driven (`[(ngModel)]` + FormsModule). Validation strategies, server error mapping, accessibility. Use this skill to: - Build Reactive Forms with typed FormGroup/FormControl. - Use FormBuilder для concise syntax. - Implement custom synchronous and async validators. - Wire FormArray for dynamic field lists. - Map server errors back to form fields. - Pick Reactive vs Template-driven (prefer Reactive). Do NOT use this skill for: - General conventions (see angular-conventions). - State management beyond forms (see angular-state-and-rx). - Routing (see angular-routing). - Testing forms (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Angular Router (built-in `@angular/router`) — route configuration for standalone and NgModule projects, functional guards (Angular 14.1+), lazy loading, route resolvers, typed params via signals/observables, programmatic navigation, route data and meta. Use this skill to: - Configure routes (standalone-style or NgModule-style). - Use functional guards (canActivate as function, preferred over class-based in 17+). - Lazy-load components or feature modules. - Implement auth guards via route meta + functional guards. - Read params/queries via `inject(ActivatedRoute)` + signals or RxJS. Do NOT use this skill for: - General conventions (see angular-conventions). - State management (see angular-state-and-rx). - Forms (see angular-forms). - Testing routes (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

State management for Angular 18-21: signals (signal/computed/effect), services-as-state, NgRx Store + Effects + Selectors, NgRx Component Store, NgRx Signals (newer signal-based store). RxJS essentials — operators, async pipe, takeUntilDestroyed, signal/observable interop. Use this skill to: - Pick the right state tool (signals / services / NgRx variant / vue-query equivalent). - Use signals correctly (signal/computed/effect — when each). - Build a Pinia-style service-as-state singleton. - Set up NgRx Store + Effects + Selectors. - Use RxJS without leaking subscriptions (async pipe, takeUntilDestroyed, Subject patterns). - Bridge signals ↔ observables via toSignal / toObservable. Do NOT use this skill for: - General Angular conventions (see angular-conventions). - Routing state (see angular-routing). - Form state (see angular-forms). - Testing state (see angular-testing).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Testing Angular 18-21: TestBed, component harnesses (@angular/cdk/testing), Karma+Jasmine (default historical) vs Jest (jest-preset-angular, modern), Angular Testing Library (RTL-style). HttpClient mocking via HttpTestingController. NgRx Effects testing. Cypress / Playwright e2e. Use this skill to: - Detect runner (Karma+Jasmine vs Jest) and configure correctly. - Write component tests with TestBed. - Use component harnesses for Material / custom UI components. - Mock HttpClient via provideHttpClientTesting + HttpTestingController. - Test signal-based inputs with componentRef.setInput(). - Test NgRx Effects with provideMockActions. Do NOT use this skill for: - General Angular conventions (see angular-conventions). - Routing patterns broadly (see angular-routing — covers testing routes briefly). - Form patterns broadly (see angular-forms).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

Shared conventions for every SDLC development-phase architect agent: hard rules, code quality bar, workflow steps (superpowers invocation, spec reading, codebase exploration, verification), and the report/compact-summary contract. Architects load this skill first, then apply their stack-specific instructions on top.

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

AratKruglik のスキルをすべて見る

このスキルの問題を報告する