swip 1.0.0+6
swip: ^1.0.0+6 copied to clipboard
Synheart Wellness Impact Protocol - Flutter SDK
SWIP Flutter SDK #
Synheart Wellness Impact Protocol - Flutter SDK for measuring how apps affect user well-being in real-time.
Overview #
The SWIP SDK enables apps to understand how users feel during digital interactions — privately, locally, and in real time. It combines:
- synheart_wear – Reads heart rate (HR), heart rate variability (HRV), and motion from wearables
- synheart-emotion – Runs lightweight on-device models that infer emotional states
- swip-core – Fuses biosignal features and emotion probabilities into a single SWIP Score (0–100)
Installation #
Add the dependency to your pubspec.yaml:
dependencies:
swip: ^1.0.0
# SWIP dependencies (required)
synheart_wear: ^0.1.2
synheart_emotion: ^0.2.0
swip_core: ^0.1.0
Then run:
flutter pub get
Note: The SWIP SDK depends on synheart_wear, synheart_emotion, and swip_core packages. These will be automatically installed when you add swip to your dependencies.
Platform-Specific Setup #
iOS #
The SWIP SDK requires HealthKit permissions to access heart rate and heart rate variability data from connected wearables. Add the following keys to your ios/Runner/Info.plist file:
<key>NSHealthShareUsageDescription</key>
<string>This app needs access to your health data to provide wellness insights and track your biometric metrics.</string>
<key>NSHealthUpdateUsageDescription</key>
<string>This app needs access to update your health data for comprehensive wellness tracking.</string>
Note: Customize the description strings to match your app's purpose. These strings are shown to users when requesting HealthKit permissions.
Android #
For Android, the SDK uses the Health Services API. Ensure your app has the necessary permissions declared in android/app/src/main/AndroidManifest.xml. The required permissions are typically handled by the synheart_wear package.
Quick Start #
import 'package:swip/swip.dart';
// Initialize the SDK
final sdk = SwipSdkManager(
config: SwipSdkConfig(
enableLogging: true,
),
);
await sdk.initialize();
// Listen to SWIP scores
sdk.scoreStream.listen((result) {
print('SWIP Score: ${result.swipScore}');
print('Emotion: ${result.dominantEmotion}');
print('Confidence: ${result.confidence}');
});
// Start a session when your app goes to foreground
final sessionId = await sdk.startSession(
appId: 'com.example.myapp',
);
// ... your app logic ...
// Stop the session when app goes to background
final results = await sdk.stopSession();
print('Average SWIP Score: ${results.getSummary()['average_swip_score']}');
// Dispose when done
sdk.dispose();
Components #
SwipSdkManager #
Main entry point for the SDK that orchestrates all components.
final sdk = SwipSdkManager(
config: SwipSdkConfig(
swipConfig: SwipConfig(
smoothingLambda: 0.9, // Exponential smoothing factor
enableSmoothing: true,
enableArtifactDetection: true,
),
emotionConfig: EmotionConfig.defaultConfig,
enableLogging: true,
),
);
SwipScoreResult #
Contains the computed SWIP score and metadata:
class SwipScoreResult {
final double swipScore; // 0-100 score
final double physSubscore; // Physiological contribution
final double emoSubscore; // Emotion contribution
final double confidence; // Confidence level
final String dominantEmotion; // Top emotion
final Map<String, double> emotionProbabilities; // All emotions
final DateTime timestamp;
final String modelId;
final Map<String, double> reasons; // Explainable factors
final bool artifactFlag;
}
Score Interpretation #
| Score Range | State | Meaning |
|---|---|---|
| 80-100 | Positive | Relaxed / Engaged - app supports wellness |
| 60-79 | Neutral | Emotionally stable |
| 40-59 | Mild Stress | Cognitive or emotional fatigue |
| <40 | Negative | Stress / emotional load detected |
Architecture #
┌─────────────────────┐
│ Your App │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ SwipSdkManager │ ← Orchestrates everything
└──────────┬──────────┘
│
┌──────┴──────┬────────────┐
▼ ▼ ▼
┌──────────┐ ┌───────────┐ ┌──────────┐
│synheart_ │ │synheart_ │ │swip_core │
│wear │ │emotion │ │ │
└────┬─────┘ └────┬──────┘ └────┬─────┘
│ │ │
▼ ▼ ▼
HR/HRV Emotion SWIP Score
Motion Probabilities (0-100)
Data Flow #
- Wearable Sensor →
synheart_wearreads HR, HRV, motion data - Feature Extraction → Sliding window aggregates data (~1 Hz)
- Emotion Inference →
synheart-emotioncomputes emotion probabilities - SWIP Computation →
swip-corefuses physiological and emotion data - Stream Output → Your app receives SWIP scores and emotion updates
Privacy & Consent #
SWIP follows a privacy-first design with three consent levels:
Consent Levels #
| Level | Name | Description |
|---|---|---|
| 0 | onDevice |
Default - All processing local, no network calls, raw biosignals never leave device |
| 1 | localExport |
User can manually export data, no automatic uploads |
| 2 | dashboardShare |
Aggregated metrics can be uploaded (no raw biosignals) |
Privacy Guarantees #
- Local-first: All computation defaults to on-device processing
- No Raw Data Transmission: Raw HR/RR intervals never uploaded automatically
- Explicit Consent Required: Network operations gated by consent level
- Data Purge API: Complete data deletion with
purgeAllData() - 30-Day Retention: Raw biosignals auto-deleted after 30 days
- Encryption: Sensitive data encrypted via device Keychain/Keystore
- Anonymization: Hashed device IDs, per-session UUIDs
- TLS 1.3: Required for any cloud transmission
Usage Example #
import 'package:swip/swip.dart';
// Initialize consent manager
final consentManager = ConsentManager();
// Request dashboard sharing (shows UI to user)
final approved = await consentManager.requestConsent(
requested: ConsentLevel.dashboardShare,
context: ConsentContext(appId: 'com.example.app'),
);
if (approved) {
await consentManager.grantConsent(ConsentLevel.dashboardShare);
// Now network operations are allowed
await sdk.uploadDailyAggregate();
}
// Check consent before sensitive operations
if (consentManager.canPerformAction(ConsentLevel.dashboardShare)) {
// Upload aggregates
}
// Purge all user data (GDPR compliance)
await consentManager.purgeAllData();
Data Storage #
Local SQLite database with schema:
sessions- Session trackingscores- SWIP scoressamples_raw- Raw biosignals (30-day retention)daily_agg- Daily aggregatesmonthly_agg- Monthly summariesconsent_history- Audit trail
See SwipStorageSchema for complete schema.
SWIP Core Implementation #
The swip-core package implements the RFC specification:
Physiological Subscore #
S_phys = w_HR * S_HR + w_HRV * S_HRV + w_M * S_M
where:
- w_HR = 0.45 (heart rate weight)
- w_HRV = 0.35 (heart rate variability weight)
- w_M = 0.20 (motion weight)
Emotion Subscore #
S_emo = Σ(p_i * u_i)
where emotion utilities are:
- Amused: 0.95
- Calm: 0.85
- Focused: 0.80
- Neutral: 0.70
- Stressed: 0.15
Fusion Formula #
SWIP = β * S_emo + (1-β) * S_phys
where β = min(0.6, C)
Finally: SWIP_100 = 100 * SWIP
Session Lifecycle #
- App Opened / Foreground → Start reading biosignals
- During Session → Continuous emotion inference and SWIP score updates (~1 Hz)
- App Minimized / Background → Stop sampling, save session summary
- App Closed → Finalize session, write daily aggregates
Example Usage #
import 'package:swip/swip.dart';
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
late SwipSdkManager _sdk;
String? _currentSessionId;
double? _currentScore;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_initSdk();
}
Future<void> _initSdk() async {
_sdk = SwipSdkManager(
config: SwipSdkConfig(enableLogging: true),
);
await _sdk.initialize();
// Listen to score updates
_sdk.scoreStream.listen((result) {
setState(() {
_currentScore = result.swipScore;
});
if (result.swipScore < 40) {
// Alert user about stress
_showStressAlert();
}
});
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_startSession();
} else if (state == AppLifecycleState.paused) {
_stopSession();
}
}
Future<void> _startSession() async {
if (_currentSessionId != null) return;
_currentSessionId = await _sdk.startSession(
appId: 'com.example.myapp',
);
}
Future<void> _stopSession() async {
if (_currentSessionId == null) return;
final results = await _sdk.stopSession();
print('Session summary: ${results.getSummary()}');
_currentSessionId = null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Current SWIP Score: ${_currentScore?.toStringAsFixed(1) ?? 'N/A'}'),
_currentScore != null
? _buildScoreIndicator(_currentScore!)
: CircularProgressIndicator(),
],
),
),
);
}
Widget _buildScoreIndicator(double score) {
Color color;
if (score >= 80) color = Colors.green;
else if (score >= 60) color = Colors.yellow;
else if (score >= 40) color = Colors.orange;
else color = Colors.red;
return Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: color.withOpacity(0.3),
shape: BoxShape.circle,
),
child: Center(
child: Text(
score.toStringAsFixed(0),
style: TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
),
),
);
}
void _showStressAlert() {
// Show user-friendly stress alert
}
@override
void dispose() {
_sdk.dispose();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
Requirements #
- Flutter SDK >=3.10.0
- Dart SDK >=3.0.0 <4.0.0
- iOS 13+ or Android API 24+
- Compatible wearable device (Apple Watch, Fitbit, Garmin, etc.)
- Health permissions granted (see Platform-Specific Setup above)
License #
Apache-2.0
Documentation #
Author #
Israel Goytom - Synheart AI