28 KiB
Nilai Clock Client - Project Documentation
Table of Contents
- Project Overview
- Technology Stack
- Current Status
- Architecture Overview
- iOS Development Requirements
- API Documentation
- Critical Implementation Details
- Migration Guide for iOS
- Development Setup
- Deployment Considerations
Project Overview
The Nilai Clock Client is a worker time-tracking application that has been successfully converted from a Vue.js web application to a native Android app using Capacitor. The application enables workers to clock in/out using QR code scanning with location verification and provides comprehensive time tracking features.
Key Features
- QR Code-based Clock In/Out: Workers scan QR codes to record attendance
- Background Location Tracking: Continuous location monitoring while clocked in
- Geofence Validation: Server-side location verification against predefined boundaries
- Anti-Spoofing Security: Detection of GPS spoofing applications
- Device Management: UUID-based device registration and validation
- Offline Capability: Local storage with sync when connection is restored
- Multi-language Support: i18n implementation with English and other languages
Target Users
- Workers: Primary users who clock in/out and view their attendance history
- Managers: Administrative users who manage workers, QR codes, and view reports (web-only currently)
Technology Stack
Frontend
- Vue 3: Progressive JavaScript framework with Composition API
- Capacitor 7.4.0: Native runtime for web apps
- Vite: Build tool and development server
- Tailwind CSS: Utility-first CSS framework
- Vue Router: Client-side routing
- Vue i18n: Internationalization
- PrimeVue: UI component library
Native Plugins
- @capacitor-community/background-geolocation: Background location tracking
- @capacitor/geolocation: Foreground location services
- @capacitor/device: Device information access
- @capacitor/preferences: Secure local storage
- @capacitor/local-notifications: Push notifications
- @capacitor-community/safe-area: Safe area handling
- html5-qrcode: QR code scanning functionality
Backend
- Node.js with Express: REST API server
- MySQL 5.x: Database with MyISAM engine
- JWT: Authentication tokens
- bcrypt: Password hashing
- CORS: Cross-origin resource sharing
Android-Specific
- Custom AppSecurity Plugin: Anti-spoofing detection
- Network Security Config: Development certificate handling
- Native Window Management: Status bar and navigation bar styling
Current Status
✅ Fully Implemented (Android)
- Worker authentication with device UUID validation
- QR code scanning for clock in/out
- Background location tracking with foreground notifications
- Geofence validation (server-side)
- Anti-spoofing security checks
- Local data persistence
- Worker dashboard and history views
- Multi-language support
- Safe area handling for modern Android devices
❌ Not Supported
- iOS Platform: No iOS implementation exists
- Web Platform: Limited functionality, native features disabled
- Manager Dashboard: Android app is worker-only (managers use web interface)
🔄 Platform-Specific Limitations
- Background location requires Android-specific permissions and services
- Anti-spoofing detection uses Android package manager APIs
- Device UUID generation relies on Android device identifiers
- Network security configuration is Android-specific
Architecture Overview
Service Layer Architecture
The application uses a service-oriented architecture with the following key services:
- NativeServicesManager: Central coordinator for all native services
- AuthService: Authentication and token management
- BackgroundLocationService: Location tracking and geofence validation
- AntiSpoofingService: Security checks and GPS spoofing detection
- DeviceUuidService: Device identification and registration
Data Flow
User Login → Device Validation → Service Initialization → Background Services Start
↓
QR Scan → Location Check → Geofence Validation → Clock Event Recording
↓
Background Location Updates → Server Sync → Security Monitoring
Database Schema (Key Tables)
- workers: User accounts with device_uuid field
- clock_records: Time tracking events with location data
- qr_codes: QR code definitions and status
- location_updates: Simplified location tracking (user_id, longitude, latitude, created_at)
- security_alerts: Security violation logging
- security_checks: Periodic security assessments
iOS Development Requirements
1. Capacitor Plugin Compatibility
✅ iOS-Compatible Plugins (No Changes Required)
@capacitor/core: Full iOS support@capacitor/app: iOS lifecycle management@capacitor/device: iOS device information@capacitor/geolocation: iOS location services@capacitor/preferences: iOS Keychain integration@capacitor/local-notifications: iOS notification system@capacitor/network: iOS network monitoringhtml5-qrcode: Web-based, works on iOS WebView
⚠️ Requires iOS-Specific Implementation
@capacitor-community/background-geolocation: Needs iOS background modes@capacitor-community/safe-area: iOS safe area handling- Custom
AppSecurityplugin: Requires complete iOS rewrite
2. iOS Background Location Requirements
Info.plist Configurations
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs location access to track work attendance and ensure workers are at authorized locations.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access to verify your work location when clocking in/out.</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<string>background-processing</string>
</array>
iOS Background Modes
- Location Updates: Required for continuous location tracking
- Background App Refresh: Needed for periodic location updates
- Silent Push Notifications: For server-triggered location checks
3. iOS-Specific Security Implementation
Required iOS AppSecurity Plugin Features
// iOS equivalent of Android AppSecurity.java
@objc(AppSecurity)
public class AppSecurity: CAPPlugin {
@objc func getInstalledApps(_ call: CAPPluginCall) {
// iOS has limited access to installed apps
// Alternative: Check for specific URL schemes
call.resolve(["packages": []])
}
@objc func checkJailbreakStatus(_ call: CAPPluginCall) {
// iOS-specific jailbreak detection
let isJailbroken = detectJailbreak()
call.resolve(["isJailbroken": isJailbroken])
}
}
4. iOS Permission Management
Location Permissions
- Request "Always" location permission for background tracking
- Handle permission degradation (Always → When In Use)
- Implement location permission recovery flows
Camera Permissions
NSCameraUsageDescriptionfor QR code scanning- Handle camera permission denial gracefully
5. iOS-Specific UI Considerations
Safe Area Handling
- Use
@capacitor-community/safe-areafor iOS notch/Dynamic Island - Implement iOS-specific status bar styling
- Handle iOS keyboard behavior differences
Navigation Patterns
- iOS-style navigation with proper back button behavior
- Swipe gestures for navigation (if desired)
- iOS-specific loading indicators and animations
API Documentation
Base Configuration
- Production API:
https://myapp.ouji.com/nilai_clock_api/ - Authentication: JWT Bearer tokens
- Content-Type:
application/json - Custom Headers:
ngrok-skip-browser-warning: true
Authentication Endpoints
POST /api/auth/login
Login with username, password, and device UUID.
Request:
{
"username": "worker123",
"password": "password",
"deviceUuid": "android-device-uuid-here"
}
Response:
{
"token": "jwt-token-here",
"fullName": "Worker Name"
}
POST /api/auth/refresh
Refresh expired JWT token.
Headers: Authorization: Bearer <token>
Response:
{
"token": "new-jwt-token-here"
}
Worker Endpoints
POST /api/clock
Record clock in/out event with location verification.
Request:
{
"userId": 123,
"eventType": "clock_in",
"qrCodeValue": "qr-code-uuid",
"latitude": -6.2088,
"longitude": 106.8456,
"notes": "Optional notes"
}
Response:
{
"message": "Clock in successful",
"timestamp": "2024-01-15T08:00:00Z"
}
GET /api/worker/clock-history/:userId
Retrieve worker's clock history.
Response:
[
{
"id": 1,
"event_type": "clock_in",
"timestamp": "2024-01-15T08:00:00Z",
"qrCodeUsedName": "Main Entrance"
}
]
GET /api/workers/:id
Get worker details.
Response:
{
"full_name": "Worker Name"
}
Location & Security Endpoints
POST /api/location/update
Send location update to server.
Request:
{
"userId": 123,
"longitude": 106.8456,
"latitude": -6.2088,
"timestamp": "2024-01-15T08:00:00Z",
"accuracy": 5.0,
"source": "background"
}
POST /api/security/check - DEPRECATED
DEPRECATED: Client-side security computation removed in favor of server-side security validation.
Anti-spoofing functionality continues through:
- Server-side blacklist checking via
/api/security/app-blacklist - Automatic geofence violation logging
- Device UUID validation during login
GET /api/security/app-blacklist
Get list of blacklisted applications.
Response:
[
"com.lexa.fakegps",
"com.incorporateapps.fakegps.fre"
]
Device Management Endpoints
POST /api/device/register
Register device for user.
Request:
{
"userId": 123,
"deviceUuid": "device-uuid-here"
}
POST /api/device/validate
Validate device for user.
Request:
{
"userId": 123,
"deviceUuid": "device-uuid-here"
}
Response:
{
"valid": true,
"message": "Device validated successfully"
}
Error Handling
All endpoints return consistent error responses:
{
"message": "Error description",
"code": "ERROR_CODE",
"details": {}
}
Common HTTP status codes:
200: Success400: Bad Request (validation errors)401: Unauthorized (invalid/expired token)403: Forbidden (insufficient permissions)404: Not Found500: Internal Server Error
Critical Implementation Details
Background Location Service Architecture
Android Implementation
The current Android implementation uses @capacitor-community/background-geolocation with:
- Tracking Triggers: Clock-in, app resume while clocked in, 30-minute intervals
- Foreground Notification: Required for Android background location
- Distance Filter: 50 meters minimum movement
- Server-side Geofence: Validation performed on backend using Turf.js
iOS Requirements for Background Location
// iOS-specific configuration needed
const iosConfig = {
requestPermissions: true,
stale: false,
distanceFilter: 50,
backgroundMessage: "Tracking work location",
backgroundTitle: "Nilai Clock",
// iOS-specific options
pausesLocationUpdatesAutomatically: false,
allowsBackgroundLocationUpdates: true,
showsBackgroundLocationIndicator: true
}
iOS Background Modes Setup
<!-- Required in Info.plist -->
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<string>background-processing</string>
</array>
Camera/QR Scanning Implementation
Current Implementation (Platform Agnostic)
Uses html5-qrcode library which works on both Android and iOS:
// Works on both platforms
const html5QrCode = new Html5Qrcode('qr-reader')
const config = {
fps: 10,
qrbox: { width: 250, height: 250 }
}
html5QrCode.start(
{ facingMode: 'environment' },
config,
onScanSuccess,
onScanFailure
)
iOS Considerations
- Camera Permissions: Requires
NSCameraUsageDescriptionin Info.plist - WebView Camera Access: Ensure WKWebView has camera permissions
- Performance: iOS WebView camera performance may differ from Android
Local Storage and Data Persistence
Current Implementation
Uses Capacitor Preferences for secure storage:
// Cross-platform secure storage
import { Preferences } from '@capacitor/preferences'
// Store data
await Preferences.set({
key: 'auth_token',
value: token
})
// Retrieve data
const { value } = await Preferences.get({ key: 'auth_token' })
iOS-Specific Considerations
- Keychain Integration: Preferences automatically uses iOS Keychain
- App Backup: Keychain data survives app reinstalls
- Biometric Protection: Can be enhanced with Touch ID/Face ID
Device UUID Generation
Android Implementation
// Android-specific device identification
const deviceInfo = await Device.getInfo()
const deviceId = await Device.getId()
const baseString = [
deviceInfo.platform,
deviceInfo.model,
deviceInfo.manufacturer,
deviceId.identifier,
deviceInfo.osVersion
].join('-')
iOS Implementation Requirements
// iOS equivalent (limited device info available)
const deviceInfo = await Device.getInfo()
const deviceId = await Device.getId()
// iOS provides limited device identification
const baseString = [
deviceInfo.platform, // "ios"
deviceInfo.model, // "iPhone" (generic)
deviceInfo.manufacturer, // "Apple"
deviceId.identifier, // iOS identifier (may change)
deviceInfo.osVersion // "16.1"
].join('-')
iOS Limitations:
- Device model is generic ("iPhone", "iPad")
- Device identifier may change on iOS updates
- Less unique device fingerprinting available
Anti-Spoofing Security Implementation
Android Implementation
Custom plugin checks installed applications:
// Android AppSecurity.java
@PluginMethod
public void getInstalledApps(PluginCall call) {
PackageManager pm = getContext().getPackageManager();
List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
// Check against blacklist
}
iOS Implementation Requirements
// iOS AppSecurity.swift - Limited capabilities
@objc func getInstalledApps(_ call: CAPPluginCall) {
// iOS doesn't allow listing installed apps
// Alternative: Check for specific URL schemes
let schemes = ["fakegps://", "locationspoof://"]
var detectedApps: [String] = []
for scheme in schemes {
if let url = URL(string: scheme) {
if UIApplication.shared.canOpenURL(url) {
detectedApps.append(scheme)
}
}
}
call.resolve(["detectedSchemes": detectedApps])
}
@objc func checkJailbreakStatus(_ call: CAPPluginCall) {
let isJailbroken = detectJailbreak()
call.resolve(["isJailbroken": isJailbroken])
}
private func detectJailbreak() -> Bool {
// Check for jailbreak indicators
let jailbreakPaths = [
"/Applications/Cydia.app",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/bin/bash",
"/usr/sbin/sshd",
"/etc/apt"
]
for path in jailbreakPaths {
if FileManager.default.fileExists(atPath: path) {
return true
}
}
return false
}
Push Notification Setup
Android Configuration (Current)
// capacitor.config.json
"LocalNotifications": {
"smallIcon": "ic_stat_location_on",
"iconColor": "#0066CC",
"sound": null
}
iOS Configuration Requirements
// capacitor.config.json - iOS additions
"LocalNotifications": {
"smallIcon": "ic_stat_location_on",
"iconColor": "#0066CC",
"sound": null,
// iOS-specific
"requestPermissions": true,
"allowBadge": true,
"allowSound": true,
"allowAlert": true
}
iOS Info.plist additions:
<key>UIUserNotificationSettings</key>
<dict>
<key>UIUserNotificationTypeBadge</key>
<true/>
<key>UIUserNotificationTypeSound</key>
<true/>
<key>UIUserNotificationTypeAlert</key>
<true/>
</dict>
Migration Guide for iOS
Phase 1: Environment Setup
1. Install iOS Development Tools
# Install Xcode from App Store
# Install iOS Simulator
# Install Capacitor iOS platform
npm install @capacitor/ios
npx cap add ios
2. Configure iOS Project
# Copy Android configuration to iOS
npx cap copy ios
npx cap sync ios
# Open in Xcode
npx cap open ios
3. Update Capacitor Configuration
// capacitor.config.json - Add iOS section
{
"appId": "com.ouji.factory.myapp",
"appName": "nilai-clock",
"webDir": "dist",
"android": {
"useLegacyBridge": true
},
"ios": {
"scheme": "NilaiClock",
"contentInset": "automatic"
}
}
Phase 2: iOS-Specific Plugin Implementation
1. Create iOS AppSecurity Plugin
# Create iOS plugin directory
mkdir -p ios/App/App/Plugins
# Create AppSecurity.swift
touch ios/App/App/Plugins/AppSecurity.swift
2. Implement AppSecurity.swift
import Foundation
import Capacitor
@objc(AppSecurity)
public class AppSecurity: CAPPlugin {
@objc func getInstalledApps(_ call: CAPPluginCall) {
// iOS implementation for app detection
call.resolve(["packages": []])
}
@objc func checkJailbreakStatus(_ call: CAPPluginCall) {
let isJailbroken = detectJailbreak()
call.resolve(["isJailbroken": isJailbroken])
}
private func detectJailbreak() -> Bool {
// Jailbreak detection logic
return false
}
}
3. Register Plugin in iOS
// ios/App/App/AppDelegate.swift
import Capacitor
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Register custom plugin
CAPBridge.registerPlugin(AppSecurity.self)
return true
}
}
Phase 3: iOS Permissions and Info.plist
1. Configure Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Location Permissions -->
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs location access to track work attendance and ensure workers are at authorized locations.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access to verify your work location when clocking in/out.</string>
<!-- Camera Permission -->
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes for clocking in/out.</string>
<!-- Background Modes -->
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<string>background-processing</string>
</array>
<!-- App Transport Security -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>
Phase 4: Platform-Specific Code Adaptations
1. Update Device UUID Service
// src/services/deviceUuidService.js - iOS adaptations
async generateDeviceUuid() {
try {
let baseString = ''
if (this.isNative) {
const deviceInfo = await Device.getInfo()
const deviceId = await Device.getId()
if (deviceInfo.platform === 'ios') {
// iOS-specific UUID generation
baseString = [
'ios',
deviceInfo.osVersion,
deviceId.identifier,
// Add iOS-specific identifiers
await this.getIOSSpecificId()
].join('-')
} else {
// Android implementation (existing)
baseString = [
deviceInfo.platform,
deviceInfo.model,
deviceInfo.manufacturer,
deviceId.identifier,
deviceInfo.osVersion
].join('-')
}
}
// Generate UUID from base string
return await this.hashString(baseString)
} catch (error) {
console.error('Failed to generate device UUID:', error)
return this.generateFallbackUuid()
}
}
async getIOSSpecificId() {
// iOS-specific device identification
try {
const { value } = await Preferences.get({ key: 'ios_device_id' })
if (value) return value
// Generate and store iOS-specific ID
const iosId = this.generateRandomId()
await Preferences.set({ key: 'ios_device_id', value: iosId })
return iosId
} catch (error) {
return 'ios-fallback'
}
}
2. Update Background Location Service
// src/services/backgroundLocationService.js - iOS adaptations
async initialize() {
if (!this.isNative) return false
try {
const deviceInfo = await Device.getInfo()
if (deviceInfo.platform === 'ios') {
// iOS-specific initialization
await this.initializeIOS()
} else {
// Android initialization (existing)
await this.initializeAndroid()
}
this.isInitialized = true
return true
} catch (error) {
console.error('Failed to initialize background location:', error)
return false
}
}
async initializeIOS() {
const messages = this.getNotificationMessages()
this.watcherId = await BackgroundGeolocation.addWatcher(
{
requestPermissions: true,
stale: false,
distanceFilter: 50,
backgroundMessage: messages.message,
backgroundTitle: messages.title,
// iOS-specific options
pausesLocationUpdatesAutomatically: false,
allowsBackgroundLocationUpdates: true,
showsBackgroundLocationIndicator: true
},
(location, error) => {
if (error) {
console.error('iOS background location error:', error)
return
}
if (location) {
this.handleLocationUpdate(location)
}
}
)
}
Phase 5: Testing and Validation
1. iOS Simulator Testing
# Build for iOS simulator
npm run build
npx cap copy ios
npx cap sync ios
# Open in Xcode and run on simulator
npx cap open ios
2. iOS Device Testing
- Configure Apple Developer account
- Set up provisioning profiles
- Test on physical iOS devices
- Validate background location functionality
- Test QR code scanning with camera
3. iOS-Specific Testing Checklist
- Location permissions (When In Use → Always)
- Background location tracking
- Camera permissions for QR scanning
- Local notifications
- Secure storage (Keychain)
- App lifecycle management
- Network connectivity handling
- Safe area handling (notch/Dynamic Island)
Phase 6: iOS Deployment Preparation
1. App Store Configuration
- Configure App Store Connect
- Set up app metadata and screenshots
- Prepare privacy policy for location usage
- Configure in-app purchase (if applicable)
2. iOS Build Configuration
# Production build for iOS
npm run build:production
npx cap copy ios
npx cap sync ios
# Archive in Xcode for App Store submission
3. iOS-Specific Considerations
- Privacy Manifest: Required for iOS 17+ apps
- Location Usage: Justify always-on location access
- Background Processing: Document background location needs
- Security: Implement additional iOS security measures
Development Setup
Prerequisites
- Node.js 18+ and npm
- Android Studio (for Android development)
- Xcode 14+ (for iOS development)
- MySQL 5.x database server
Environment Setup
1. Clone and Install Dependencies
git clone <repository-url>
cd Nilai_Clock_Client
npm install
2. Environment Configuration
Create environment files:
.env.local-http- Local HTTP development.env.local-https- Local HTTPS development.env.production-server- Production server
Example .env.production-server:
VITE_API_BASE_URL=https://myapp.ouji.com/nilai_clock_api
3. Database Setup
# Import database schema
mysql -u username -p database_name < 0709.sql
4. Development Server
# Start development server
npm run dev
# Start backend server
npm run backend
# Start both (recommended)
npm run dev:all
Build Commands
Android Development
# Build for local testing
npm run build:local-http
npm run build:local-https
# Build for production
npm run build:production
# Open Android Studio
npm run open:android
iOS Development (Future)
# Add iOS platform
npx cap add ios
# Build and sync
npm run build:production
npx cap copy ios
npx cap sync ios
# Open Xcode
npx cap open ios
Development Workflow
1. Code Changes
# Make changes to src/ files
# Build and sync to native platforms
npm run build:android
2. Native Development
# Android: Open Android Studio
npm run open:android
# iOS: Open Xcode (future)
npx cap open ios
3. Testing
- Test on Android emulator/device
- Test background location functionality
- Verify QR code scanning
- Test offline/online scenarios
Deployment Considerations
Android Deployment
1. Production Build
# Switch to production environment
npm run switch:production
# Build for production
npm run build:android
2. Android App Bundle
- Generate signed AAB in Android Studio
- Upload to Google Play Console
- Configure app permissions and descriptions
3. Android Permissions
Required permissions in android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
iOS Deployment (Future)
1. App Store Requirements
- Valid Apple Developer account
- App Store Connect configuration
- Privacy policy for location usage
- App review guidelines compliance
2. iOS Permissions Justification
- Location Always: Required for work attendance tracking
- Camera: Required for QR code scanning
- Background Processing: Required for location updates
3. iOS Security Considerations
- Implement additional jailbreak detection
- Use iOS Keychain for secure storage
- Follow iOS security best practices
Backend Deployment
1. Production Server
- Deploy Node.js backend to production server
- Configure MySQL database
- Set up SSL certificates
- Configure environment variables
2. API Security
- Implement rate limiting
- Use HTTPS in production
- Secure JWT secret keys
- Configure CORS properly
3. Database Optimization
- Optimize location_updates table for performance
- Implement database indexing
- Set up regular backups
- Monitor database performance
Monitoring and Maintenance
1. Application Monitoring
- Track location update frequency
- Monitor API response times
- Log security violations
- Track user engagement metrics
2. Performance Optimization
- Optimize background location battery usage
- Minimize API calls
- Implement efficient data caching
- Monitor app size and performance
3. Security Monitoring
- Monitor for GPS spoofing attempts
- Track device registration patterns
- Log authentication failures
- Monitor for suspicious activity
Conclusion
The Nilai Clock Client represents a successful conversion from web to native Android application with comprehensive worker time-tracking capabilities. The iOS implementation requires significant platform-specific development, particularly for background location services and security features.
Key success factors for iOS development:
- Background Location: Proper iOS background modes and permissions
- Security Implementation: iOS-specific anti-spoofing measures
- Native Integration: Platform-specific UI and UX patterns
- Testing: Comprehensive testing on iOS devices and simulators
- App Store Compliance: Meeting Apple's strict review guidelines
The modular architecture and service-oriented design make the iOS migration feasible, with most business logic remaining platform-agnostic while native services require iOS-specific implementations.