Receber por email
← Voltar para o catálogo

rn-native-features

Native iOS features in Expo React Native apps. Use when implementing camera, push notifications, haptics, permissions, device sensors, or other native APIs in Expo.

367estrelas
Atualizado há 4 meses

Ver no GitHub ↗

Como adicionar

/plugin marketplace add aiskillstore/marketplace

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/rn-native-features/svg)](https://www.skillteca.com.br/skills/rn-native-features)

Skills relacionadas

Ver todas de Segurança →

Alerta por categoria

Receba novas skills de Segurança toda segunda

1 email curto, só com as skills novas de Segurança. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Native Features (Expo)

Permissions Pattern

Always request permissions before using native features:

import * as Camera from 'expo-camera';

async function requestCameraPermission() {
  const { status } = await Camera.requestCameraPermissionsAsync();
  
  if (status !== 'granted') {
    // Guide user to settings if denied
    Alert.alert(
      'Camera Access Required',
      'Please enable camera access in Settings to take photos.',
      [
        { text: 'Cancel', style: 'cancel' },
        { text: 'Open Settings', onPress: () => Linking.openSettings() },
      ]
    );
    return false;
  }
  return true;
}

Check permission status first

import * as Camera from 'expo-camera';

function usePermission() {
  const [permission, requestPermission] = Camera.useCameraPermissions();
  
  // permission.granted - boolean
  // permission.canAskAgain - false if user selected "Don't ask again"
  // permission.status - 'granted' | 'denied' | 'undetermined'
  
  return { permission, requestPermission };
}

Camera

Basic Camera with Photo Capture

import { CameraView, useCameraPermissions } from 'expo-camera';
import { useRef, useState } from 'react';

export function CameraScreen() {
  const [permission, requestPermission] = useCameraPermissions();
  const [facing, setFacing] = useState<'front' | 'back'>('back');
  const cameraRef = useRef<CameraView>(null);

  if (!permission) return <View />;
  
  if (!permission.granted) {
    return (
      <View>
        <Text>Camera access is required</Text>
        <Button title="Grant Permission" onPress={requestPermission} />
      </View>
    );
  }

  const takePicture = async () => {
    if (cameraRef.current) {
      const photo = await cameraRef.current.takePictureAsync({
        quality: 0.8,
        base64: false,
        exif: false,
      });
      console.log('Photo URI:', photo.uri);
      // photo.uri is a local file path
    }
  };

  return (
    <View style={{ flex: 1 }}>
      <CameraView 
        ref={cameraRef}
        style={{ flex: 1 }} 
        facing={facing}
      >
        <View style={styles.controls}>
          <Button title="Flip" onPress={() => setFacing(f => f === 'back' ? 'front' : 'back')} />
          <Button title="Take Photo" onPress={takePicture} />
        </View>
      </CameraView>
    </View>
  );
}

Image Picker (Gallery + Camera)

Often simpler than full camera UI:

import * as ImagePicker from 'expo-image-picker';

async function pickImage() {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.Images,
    allowsEditing: true,
    aspect: [1, 1],
    quality: 0.8,
  });

  if (!result.canceled) {
    return result.assets[0].uri;
  }
}

async function takePhoto() {
  const permission = await ImagePicker.requestCameraPermissionsAsync();
  if (!permission.granted) return;
  
  const result = await ImagePicker.launchCameraAsync({
    allowsEditing: true,
    aspect: [1, 1],
    quality: 0.8,
  });

  if (!result.canceled) {
    return result.assets[0].uri;
  }
}

Push Notifications

Setup with expo-notifications

import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
import { Platform } from 'react-native';

// Configure how notifications appear when app is foregrounded
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: true,
  }),
});

async function registerForPushNotifications() {
  if (!Device.isDevice) {
    console.log('Push notifications require a physical device');
    return null;
  }

  // Check existing permission
  const { status: existingStatus } = await Notifications.getPermissionsAsync();
  let finalStatus = existingStatus;

  // Request if not determined
  if (existingStatus !== 'granted') {
    const { status } = await Notifications.requestPermissionsAsync();
    finalStatus = status;
  }

  if (finalStatus !== 'granted') {
    console.log('Push notification permission denied');
    return null;
  }

  // Get Expo push token
  const token = await Notifications.getExpoPushTokenAsync({
    projectId: 'your-expo-project-id', // From app.json
  });

  return token.data; // "ExponentPushToken[xxxx]"
}

Handle incoming notifications

import { useEffect, useRef } from 'react';
import * as Notifications from 'expo-notifications';

export function useNotificationHandler() {
  const notificationListener = useRef<Notifications.Subscription>();
  const responseListener = useRef<Notifications.Subscription>();

  useEffect(() => {
    // Notification received while app is foregrounded
    notificationListener.current = Notifications.addNotificationReceivedListener(
      (notification) => {
        console.log('Received:', notification.request.content);
      }
    );

    // User tapped on notification
    responseListener.current = Notifications.addNotificationResponseReceivedListener(
      (response) => {
        const data = response.notification.request.content.data;
        // Navigate based on notification data
        if (data.screen) {
          router.push(data.screen);
        }
      }
    );

    return () => {
      notificationListener.current?.remove();
      responseListener.current?.remove();
    };
  }, []);
}

Send test notification locally

async function sendTestNotification() {
  await Notifications.scheduleNotificationAsync({
    content: {
      title: "Match Reminder",
      body: "Your match starts in 30 minutes!",
      data: { screen: '/match/123' },
    },
    trigger: { seconds: 5 },
  });
}

Send from backend (Expo Push API)

# FastAPI example
import httpx

async def send_push_notification(
    expo_push_token: str, 
    title: str, 
    body: str, 
    data: dict = None
):
    message = {
        "to": expo_push_token,
        "title": title,
        "body": body,
        "data": data or {},
    }
    
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://exp.host/--/api/v2/push/send",
            json=message,
            headers={"Content-Type": "application/json"},
        )
        return response.json()

Haptic Feedback

import * as Haptics from 'expo-haptics';

// Light tap - for UI interactions
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);

// Medium - for confirmations
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);

// Heavy - for significant actions
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);

// Success/Error/Warning - semantic feedback
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);

// Selection - for picker/scroll
Haptics.selectionAsync();

Good haptic patterns

// Button press
<Pressable 
  onPress={() => {
    Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
    handlePress();
  }}
/>

// Form submission success
await submitForm();
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);

// Error state
if (error) {
  Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
}

// Picker/Scroll selection
<Picker 
  onValueChange={(value) => {
    Haptics.selectionAsync();
    setValue(value);
  }}
/>

Location

import * as Location from 'expo-location';

async function getCurrentLocation() {
  const { status } = await Location.requestForegroundPermissionsAsync();
  if (status !== 'granted') {
    return null;
  }

  const location = await Location.getCurrentPositionAsync({
    accuracy: Location.Accuracy.Balanced,
  });
  
  return {
    latitude: location.coords.latitude,
    longitude: location.coords.longitude,
  };
}

// Watch location ch
Compartilhar X LinkedIn