Wat zijn Device APIs?

Device APIs geven je toegang tot hardware en functies van het apparaat, zoals de camera, GPS, contacten, en meer. Expo biedt veel ready-to-use packages voor deze features.

Populaire Device APIs:

  • Camera: Foto's maken en selecteren
  • Location: GPS locatie ophalen
  • Contacts: Toegang tot contactenlijst
  • Sharing: Content delen naar andere apps
  • Vibration: Apparaat laten trillen
  • Notifications: Push notificaties

Belangrijk: De meeste Device APIs vereisen permissies van de gebruiker!

Camera - Foto's Maken

Met expo-camera kun je de camera gebruiken om foto's te maken.

Installatie

npx expo install expo-camera

Basis Voorbeeld

import { useState, useRef } from 'react';
import { View, Pressable, Text, Image, StyleSheet } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';

export default function CameraExample() {
  const [permission, requestPermission] = useCameraPermissions();
  const [photo, setPhoto] = useState(null);
  const cameraRef = useRef(null);

  if (!permission) {
    return <View />;
  }

  if (!permission.granted) {
    return (
      <View style={styles.container}>
        <Text>Camera permissie nodig</Text>
        <Pressable style={styles.button} onPress={requestPermission}>
          <Text style={styles.buttonText}>Geef Permissie</Text>
        </Pressable>
      </View>
    );
  }

  const takePicture = async () => {
    if (cameraRef.current) {
      const photo = await cameraRef.current.takePictureAsync();
      setPhoto(photo.uri);
    }
  };

  if (photo) {
    return (
      <View style={styles.container}>
        <Image source={{ uri: photo }} style={styles.preview} />
        <Pressable style={styles.button} onPress={() => setPhoto(null)}>
          <Text style={styles.buttonText}>Nieuwe Foto</Text>
        </Pressable>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <CameraView style={styles.camera} ref={cameraRef}>
        <View style={styles.buttonContainer}>
          <Pressable style={styles.captureButton} onPress={takePicture}>
            <Text style={styles.buttonText}>📷</Text>
          </Pressable>
        </View>
      </CameraView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  camera: {
    flex: 1,
  },
  buttonContainer: {
    flex: 1,
    backgroundColor: 'transparent',
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'flex-end',
    marginBottom: 40,
  },
  captureButton: {
    width: 70,
    height: 70,
    borderRadius: 35,
    backgroundColor: 'white',
    justifyContent: 'center',
    alignItems: 'center',
  },
  preview: {
    flex: 1,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    margin: 20,
    borderRadius: 8,
    alignItems: 'center',
  },
  buttonText: {
    color: 'white',
    fontWeight: 'bold',
    fontSize: 16,
  },
});

Camera Features:

  • facing - Voor/achter camera ("front" of "back")
  • flash - Flits aan/uit
  • zoom - Zoom niveau
  • takePictureAsync() - Maak foto

Location - GPS Locatie

Met expo-location kun je de huidige locatie van de gebruiker ophalen.

Installatie

npx expo install expo-location

Huidige Locatie Ophalen

import { useState, useEffect } from 'react';
import { View, Text, Pressable, ActivityIndicator, StyleSheet } from 'react-native';
import * as Location from 'expo-location';

export default function LocationExample() {
  const [location, setLocation] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const getLocation = async () => {
    setLoading(true);
    setError(null);

    try {
      // Vraag permissie
      const { status } = await Location.requestForegroundPermissionsAsync();
      
      if (status !== 'granted') {
        setError('Locatie permissie geweigerd');
        setLoading(false);
        return;
      }

      // Haal locatie op
      const location = await Location.getCurrentPositionAsync({});
      setLocation(location);
    } catch (error) {
      setError('Kon locatie niet ophalen');
      console.error(error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <View style={styles.container}>
      <Pressable style={styles.button} onPress={getLocation}>
        <Text style={styles.buttonText}>Haal Locatie Op</Text>
      </Pressable>

      {loading && <ActivityIndicator size="large" color="#007AFF" />}
      
      {error && (
        <View style={styles.errorBox}>
          <Text style={styles.errorText}>{error}</Text>
        </View>
      )}

      {location && (
        <View style={styles.infoBox}>
          <Text style={styles.label}>Latitude:</Text>
          <Text style={styles.value}>{location.coords.latitude}</Text>
          
          <Text style={styles.label}>Longitude:</Text>
          <Text style={styles.value}>{location.coords.longitude}</Text>
          
          <Text style={styles.label}>Nauwkeurigheid:</Text>
          <Text style={styles.value}>{location.coords.accuracy}m</Text>
        </View>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    justifyContent: 'center',
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 8,
    alignItems: 'center',
    marginBottom: 20,
  },
  buttonText: {
    color: 'white',
    fontWeight: 'bold',
    fontSize: 16,
  },
  errorBox: {
    backgroundColor: '#ffebee',
    padding: 15,
    borderRadius: 8,
    marginTop: 10,
  },
  errorText: {
    color: '#c62828',
  },
  infoBox: {
    backgroundColor: '#f5f5f5',
    padding: 20,
    borderRadius: 8,
    marginTop: 20,
  },
  label: {
    fontSize: 14,
    fontWeight: 'bold',
    marginTop: 10,
  },
  value: {
    fontSize: 16,
    marginBottom: 5,
  },
});

Adres van Locatie (Reverse Geocoding)

const getAddress = async (latitude, longitude) => {
  try {
    const address = await Location.reverseGeocodeAsync({
      latitude,
      longitude,
    });
    
    if (address[0]) {
      console.log('Stad:', address[0].city);
      console.log('Land:', address[0].country);
      console.log('Straat:', address[0].street);
    }
  } catch (error) {
    console.error('Error:', error);
  }
};

Contacts - Contactenlijst

Met expo-contacts kun je toegang krijgen tot de contactenlijst van de gebruiker.

Installatie

npx expo install expo-contacts

Contacten Ophalen

import { useState } from 'react';
import { View, Pressable, Text, FlatList, StyleSheet } from 'react-native';
import * as Contacts from 'expo-contacts';

export default function ContactsExample() {
  const [contacts, setContacts] = useState([]);

  const loadContacts = async () => {
    try {
      const { status } = await Contacts.requestPermissionsAsync();
      
      if (status !== 'granted') {
        alert('Permissie geweigerd');
        return;
      }

      const { data } = await Contacts.getContactsAsync({
        fields: [Contacts.Fields.PhoneNumbers, Contacts.Fields.Emails],
      });

      if (data.length > 0) {
        setContacts(data);
      }
    } catch (error) {
      console.error('Error:', error);
    }
  };

  return (
    <View style={styles.container}>
      <Pressable style={styles.button} onPress={loadContacts}>
        <Text style={styles.buttonText}>Laad Contacten</Text>
      </Pressable>

      <FlatList
        data={contacts}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View style={styles.contactCard}>
            <Text style={styles.name}>{item.name}</Text>
            {item.phoneNumbers && (
              <Text>{item.phoneNumbers[0]?.number}</Text>
            )}
          </View>
        )}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 8,
    alignItems: 'center',
    marginBottom: 20,
  },
  buttonText: {
    color: 'white',
    fontWeight: 'bold',
  },
  contactCard: {
    backgroundColor: 'white',
    padding: 15,
    borderRadius: 8,
    marginBottom: 10,
    shadowColor: '#000',
    shadowOpacity: 0.1,
    shadowRadius: 3,
    elevation: 2,
  },
  name: {
    fontSize: 16,
    fontWeight: 'bold',
    marginBottom: 5,
  },
});

Sharing - Content Delen

Met de ingebouwde Share API kun je content delen naar andere apps.

Tekst Delen

import { View, Pressable, Text, Share, StyleSheet } from 'react-native';

export default function ShareExample() {
  const shareText = async () => {
    try {
      await Share.share({
        message: 'Check deze geweldige app uit!',
      });
    } catch (error) {
      console.error('Error:', error);
    }
  };

  return (
    <View style={styles.container}>
      <Pressable style={styles.button} onPress={shareText}>
        <Text style={styles.buttonText}>Deel Tekst</Text>
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 8,
    alignItems: 'center',
  },
  buttonText: {
    color: 'white',
    fontWeight: 'bold',
  },
});

URL Delen

const shareUrl = async () => {
  try {
    await Share.share({
      message: 'Bekijk deze website!',
      url: 'https://example.com',
    });
  } catch (error) {
    console.error('Error:', error);
  }
};

Met Titel

const shareWithTitle = async () => {
  try {
    await Share.share(
      {
        message: 'Geweldige content!',
        url: 'https://example.com',
        title: 'Mijn Favoriete Website',
      },
      {
        dialogTitle: 'Deel met vrienden',
      }
    );
  } catch (error) {
    console.error('Error:', error);
  }
};

Vibration - Apparaat Laten Trillen

Met de Vibration API kun je het apparaat laten trillen.

Basis Vibratie

import { View, Pressable, Text, Vibration, StyleSheet } from 'react-native';

export default function VibrationExample() {
  const vibrateOnce = () => {
    Vibration.vibrate();
  };

  const vibrateDuration = () => {
    Vibration.vibrate(1000); // 1 seconde
  };

  const vibratePattern = () => {
    // [wacht, trillen, wacht, trillen]
    Vibration.vibrate([0, 500, 200, 500]);
  };

  const vibrateCancel = () => {
    Vibration.cancel();
  };

  return (
    <View style={styles.container}>
      <Pressable style={styles.button} onPress={vibrateOnce}>
        <Text style={styles.buttonText}>Kort Trillen</Text>
      </Pressable>

      <Pressable style={styles.button} onPress={vibrateDuration}>
        <Text style={styles.buttonText}>1 Seconde</Text>
      </Pressable>

      <Pressable style={styles.button} onPress={vibratePattern}>
        <Text style={styles.buttonText}>Patroon</Text>
      </Pressable>

      <Pressable style={styles.cancelButton} onPress={vibrateCancel}>
        <Text style={styles.buttonText}>Stop</Text>
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 8,
    alignItems: 'center',
    marginBottom: 10,
  },
  cancelButton: {
    backgroundColor: '#ff3b30',
    padding: 15,
    borderRadius: 8,
    alignItems: 'center',
    marginTop: 10,
  },
  buttonText: {
    color: 'white',
    fontWeight: 'bold',
  },
});

Vibratie Patronen:

Array van milliseconden: [wacht, trillen, wacht, trillen, ...]

Vibration.vibrate([0, 100, 50, 100]); // 2x kort
Vibration.vibrate([0, 500, 200, 500]); // 2x lang
Vibration.vibrate([0, 100, 100, 100, 100, 100]); // SOS

Permissies Afhandelen

De meeste Device APIs hebben permissies nodig. Hier zijn best practices voor permissie handling.

Permissie Status Checken

import { useEffect, useState } from 'react';
import * as Location from 'expo-location';

export default function App() {
  const [hasPermission, setHasPermission] = useState(null);

  useEffect(() => {
    checkPermission();
  }, []);

  const checkPermission = async () => {
    const { status } = await Location.getForegroundPermissionsAsync();
    setHasPermission(status === 'granted');
  };

  const requestPermission = async () => {
    const { status } = await Location.requestForegroundPermissionsAsync();
    setHasPermission(status === 'granted');
  };

  if (hasPermission === null) {
    return <Text>Aan het laden...</Text>;
  }

  if (hasPermission === false) {
    return (
      <View>
        <Text>Locatie permissie nodig</Text>
        <Pressable onPress={requestPermission}>
          <Text>Geef Permissie</Text>
        </Pressable>
      </View>
    );
  }

  return <Text>Permissie verleend!</Text>;
}

Overzicht Permissie Functies

API Permissie Functie
Camera Camera.requestCameraPermissionsAsync()
Location Location.requestForegroundPermissionsAsync()
Contacts Contacts.requestPermissionsAsync()
Media Library MediaLibrary.requestPermissionsAsync()

Best Practices:

  • Vraag permissies alleen als je ze nodig hebt
  • Leg uit waarom je de permissie nodig hebt
  • Geef een fallback als permissie geweigerd wordt
  • Check permissie status voordat je de API gebruikt

Samenvatting

Camera

expo-camera voor foto's

Location

GPS locatie ophalen

Contacts

Contactenlijst toegang

Share

Content delen

Je hebt nu geleerd:

  • Camera gebruiken om foto's te maken
  • GPS locatie ophalen met expo-location
  • Toegang tot contactenlijst
  • Content delen met Share API
  • Apparaat laten trillen met Vibration
  • Permissies correct afhandelen