@classic-homes/native
v0.1.2
Published
Native device APIs for iOS and Android using Capacitor
Readme
@classic-homes/native
Native device APIs for iOS and Android mobile apps. Uses Capacitor for native functionality with web fallbacks.
Installation
npm install @classic-homes/nativePeer Dependencies
Install the Capacitor plugins you need:
# Core (required)
npm install @capacitor/core
# Optional plugins
npm install @capacitor/camera
npm install @capacitor/geolocation
npm install @capacitor/preferences
npm install @capacitor/haptics
npm install @capacitor/device
npm install @capacitor/network
npm install @capacitor/push-notifications
npm install @capacitor/app
npm install @capgo/capacitor-native-biometricQuick Start
Initialize
import { initNative, getPlatformInfo } from '@classic-homes/native';
// Initialize at app startup
const platform = initNative({
debug: import.meta.env.DEV,
geolocationDefaults: {
enableHighAccuracy: true,
timeout: 10000,
},
});
console.log(platform.isNative); // true on iOS/Android
console.log(platform.platform); // 'ios', 'android', or 'web'React Usage
import { useGeolocation, useCamera, NativeGuard } from '@classic-homes/native/react';
function LocationTracker() {
const { position, isLoading, getCurrentPosition, startWatching, stopWatching } = useGeolocation();
return (
<NativeGuard native fallback={<p>Native features not available</p>}>
<div>
{isLoading && <p>Getting location...</p>}
{position && (
<p>
Lat: {position.coords.latitude}, Lng: {position.coords.longitude}
(accuracy: {position.coords.accuracy}m)
</p>
)}
<button onClick={() => getCurrentPosition()}>Get Location</button>
<button onClick={() => startWatching()}>Start Watching</button>
</div>
</NativeGuard>
);
}
function PhotoCapture() {
const { photo, takePhoto, pickFromGallery } = useCamera({ quality: 90 });
return (
<div>
{photo && <img src={photo.webPath || photo.dataUrl} alt="Captured" />}
<button onClick={() => takePhoto()}>Take Photo</button>
<button onClick={() => pickFromGallery()}>Choose from Gallery</button>
</div>
);
}Svelte Usage
<script lang="ts">
import { useGeolocation, useCamera, usePlatformCheck } from '@classic-homes/native/svelte';
const geo = useGeolocation({ enableHighAccuracy: true });
const camera = useCamera({ quality: 90 });
const platform = usePlatformCheck();
</script>
{#if platform.isNative}
<div>
{#if geo.isLoading}
<p>Getting location...</p>
{:else if geo.position}
<p>Lat: {geo.position.coords.latitude}</p>
{/if}
<button onclick={() => geo.getCurrentPosition()}>Get Location</button>
<button onclick={() => geo.startWatching()}>Start Watching</button>
</div>
{:else}
<p>Native features not available</p>
{/if}
{#if camera.photo}
<img src={camera.photo.webPath || camera.photo.dataUrl} alt="Captured" />
{/if}
<button onclick={() => camera.takePhoto()}>Take Photo</button>Available Features
| Feature | Hook/Composable | iOS | Android | Web Fallback |
| ------------------ | ---------------------- | ---------------- | -------------- | -------------- |
| Camera | useCamera | Native | Native | File input |
| Geolocation | useGeolocation | GPS | GPS | Navigator API |
| Secure Storage | useSecureStorage | Keychain | EncryptedPrefs | localStorage |
| Biometrics | useBiometrics | Face ID/Touch ID | Fingerprint | WebAuthn |
| Haptics | useHaptics | Native | Native | Vibration API |
| Device Info | useDevice | Native | Native | UA parsing |
| Network Status | useNetwork | Native | Native | Navigator API |
| Push Notifications | usePushNotifications | APNs | FCM | Web Push |
| App Lifecycle | useAppLifecycle | Native | Native | Visibility API |
API Reference
Platform Detection
import {
getPlatformInfo,
isNative,
isWeb,
isIOS,
isAndroid,
isMobile,
} from '@classic-homes/native';
const info = getPlatformInfo();
// { platform: 'ios', shellType: 'capacitor', isNative: true, isMobile: true }
if (isNative()) {
// Running in Capacitor
}Geolocation
// React
const {
position, // Current position
isLoading, // Loading state
isWatching, // Whether watching
error, // Error state
getCurrentPosition,
startWatching,
stopWatching,
checkPermissions,
requestPermissions,
} = useGeolocation({
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0,
});
// Get once
const pos = await getCurrentPosition();
// Watch continuously
await startWatching();
// ... later
await stopWatching();Camera
const {
photo, // Last captured photo
isLoading,
error,
takePhoto,
pickFromGallery,
checkPermissions,
requestPermissions,
} = useCamera({
quality: 90,
allowEditing: false,
resultType: 'dataUrl', // 'base64' | 'uri' | 'dataUrl'
});
const photo = await takePhoto();
console.log(photo.dataUrl); // data:image/jpeg;base64,...Biometrics
const {
isAvailable, // Whether biometrics available
availability, // Detailed info
isLoading,
error,
checkAvailability,
authenticate,
} = useBiometrics();
if (isAvailable) {
const result = await authenticate({
reason: 'Confirm your identity',
allowDeviceCredential: true,
});
if (result.verified) {
// Authentication successful
}
}Secure Storage
const { isLoading, error, get, set, remove, clear, keys } = useSecureStorage();
await set('auth_token', 'your-token');
const token = await get('auth_token');
await remove('auth_token');Haptics
const { isAvailable, impact, notification, vibrate } = useHaptics();
await impact('medium'); // 'light' | 'medium' | 'heavy'
await notification('success'); // 'success' | 'warning' | 'error'
await vibrate(300); // duration in msNetwork
const {
status, // Full status object
isConnected, // Boolean
connectionType, // 'wifi' | 'cellular' | 'none' | 'unknown'
refresh,
} = useNetwork();App Lifecycle
const {
isActive, // Whether app is active
launchUrl, // URL that launched the app
exitApp, // Android only
minimizeApp, // Android only
} = useAppLifecycle({
onStateChange: (state) => {
if (!state.isActive) {
// App went to background
}
},
onUrlOpen: (event) => {
// Handle deep link
console.log('Opened:', event.url);
},
});Push Notifications
const {
token,
isRegistered,
permissionState,
register,
unregister,
requestPermissions,
createChannel,
} = usePushNotifications({
onNotificationReceived: (notification) => {
console.log('Received:', notification);
},
onNotificationAction: (action) => {
console.log('User tapped:', action);
},
});
await requestPermissions();
await register();
console.log('Token:', token?.value);Conditional Rendering
React
import { NativeGuard, usePlatformCheck } from '@classic-homes/native/react';
// Component-based
<NativeGuard native>
<MobileFeatures />
</NativeGuard>
<NativeGuard platform="ios">
<ApplePayButton />
</NativeGuard>
<NativeGuard feature="camera">
<PhotoButton />
</NativeGuard>
// Hook-based
const { isNative, isIOS, isFeatureAvailable } = usePlatformCheck();
if (isNative && await isFeatureAvailable('camera')) {
// Show camera button
}Svelte
<script lang="ts">
import { usePlatformCheck } from '@classic-homes/native/svelte';
const platform = usePlatformCheck();
// Check feature availability (async)
let cameraAvailable = $state(false);
$effect(() => {
platform.isFeatureAvailable('camera').then((available) => {
cameraAvailable = available;
});
});
</script>
{#if platform.isNative}
<MobileFeatures />
{/if}
{#if cameraAvailable}
<PhotoButton />
{/if}Error Handling
import {
isPermissionDeniedError,
isUserCancelledError,
isFeatureUnavailableError,
} from '@classic-homes/native';
try {
await getCurrentPosition();
} catch (error) {
if (isPermissionDeniedError(error)) {
// Show settings prompt
console.log('Please enable location in settings');
} else if (isUserCancelledError(error)) {
// User cancelled, no action needed
} else if (isFeatureUnavailableError(error)) {
// Feature not available on this device
}
}TypeScript Support
This package includes TypeScript declarations for:
@classic-homes/native- Main entry@classic-homes/native/core- Core utilities@classic-homes/native/react- React hooks and components
Svelte Note: The @classic-homes/native/svelte subpath does not include TypeScript declarations due to Svelte 5 runes ($state, $derived) not being supported by TypeScript's declaration emitter. The Svelte composables work correctly at runtime and include JSDoc comments for IDE support.
License
MIT
