crossvault_ios 1.0.0 copy "crossvault_ios: ^1.0.0" to clipboard
crossvault_ios: ^1.0.0 copied to clipboard

PlatformiOS
unlisted

iOS implementation of crossvault plugin using Keychain with iCloud sync support. Provides hardware-backed security via Secure Enclave.

crossvault_ios #

The iOS implementation of crossvault.

Features #

  • ✅ Secure storage using iOS Keychain Services
  • ✅ Support for Keychain Access Groups (sharing between apps)
  • ✅ iCloud Keychain synchronization support
  • ✅ Pure Swift implementation
  • ✅ Comprehensive error handling

Usage #

This package is endorsed, which means you can simply use crossvault normally. This package will be automatically included in your app when you do, so you do not need to add it to your pubspec.yaml.

However, if you import this package to use any of its APIs directly, you should add it to your pubspec.yaml as usual.

Usage Modes #

Crossvault iOS supports two modes of operation:

1. Private Mode (Default) - No Access Group #

Data is stored privately for your app only. No additional setup required.

// No configuration needed
final crossvault = Crossvault();
await crossvault.setValue('api_token', 'secret_value');

// Or explicitly without access group
await crossvault.setValue(
  'api_token',
  'secret_value',
  options: IOSOptions(),  // No accessGroup specified
);

Use this when:

  • You don't need to share data between apps
  • You want the simplest setup
  • You're building a single app

2. Shared Mode - With Access Group #

Data can be shared between apps with the same Team ID, or between your app and its extensions.

await Crossvault.init(
  options: IOSOptions(
    accessGroup: 'io.alexmelnyk.crossvault.shared',
  ),
);

Use this when:

  • You need to share data between multiple apps
  • You have app extensions (widgets, share extensions, etc.)
  • You want to sync data between your apps

Keychain Access Groups Setup #

Note: This setup is only required if you want to use Shared Mode with access groups. For private storage, skip this section.

To use Keychain Access Groups for sharing data between apps (or between your app and extensions), you need to configure entitlements.

Step 1: Enable Keychain Sharing in Xcode #

  1. Open your iOS project in Xcode: ios/Runner.xcworkspace
  2. Select your target (Runner)
  3. Go to Signing & Capabilities
  4. Click + Capability
  5. Add Keychain Sharing
  6. Add your access group identifier (e.g., $(AppIdentifierPrefix)io.alexmelnyk.crossvault.shared)

Step 2: Configure Entitlements #

Xcode will automatically create ios/Runner/Runner.entitlements. It should look like this:

<?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>
    <key>keychain-access-groups</key>
    <array>
        <string>$(AppIdentifierPrefix)io.alexmelnyk.crossvault.shared</string>
    </array>
</dict>
</plist>

Step 3: Use Access Groups in Your Code #

Initialize Crossvault once at app startup with your global configuration:

import 'package:crossvault/crossvault.dart';

// In your main() or app initialization
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Initialize with global iOS options
  await Crossvault.init(
    options: IOSOptions(
      accessGroup: 'io.alexmelnyk.crossvault.shared',
      synchronizable: true,
      accessibility: IOSAccessibility.afterFirstUnlock,
    ),
  );
  
  runApp(MyApp());
}

// Now use Crossvault anywhere without specifying options
final crossvault = Crossvault();

// All operations use global configuration
await crossvault.setValue('api_token', 'secret_value');
final value = await crossvault.getValue('api_token');

Option 2: Per-Method Configuration

Override global configuration for specific operations:

import 'package:crossvault/crossvault.dart';

final crossvault = Crossvault();

// Use different options for specific call
await crossvault.setValue(
  'temp_token',
  'temp_value',
  options: IOSOptions(
    synchronizable: false,  // Don't sync this one
    accessibility: IOSAccessibility.whenUnlocked,
  ),
);

// Or use completely different access group
await crossvault.setValue(
  'shared_token',
  'shared_value',
  options: IOSOptions(
    accessGroup: 'io.alexmelnyk.another.group',
  ),
);

Option 3: No Global Configuration

Use options on every call:

import 'package:crossvault/crossvault.dart';

final crossvault = Crossvault();

// Specify options for each operation
await crossvault.setValue(
  'api_token',
  'secret_value',
  options: IOSOptions(
    accessGroup: 'io.alexmelnyk.crossvault.shared',
    synchronizable: true,
    accessibility: IOSAccessibility.whenUnlocked,
  ),
);

final value = await crossvault.getValue(
  'api_token',
  options: IOSOptions(
    accessGroup: 'io.alexmelnyk.crossvault.shared',
  ),
);

Comparison: Private vs Shared Mode #

Feature Private Mode (No Access Group) Shared Mode (With Access Group)
Setup Required ❌ None ✅ Xcode entitlements configuration
Data Sharing ❌ App-only ✅ Between apps with same Team ID
App Extensions ❌ Cannot share ✅ Can share with extensions
iCloud Sync ✅ Optional ✅ Optional
Security 🔒 Highest (app-isolated) 🔒 High (team-isolated)
Use Case Single app Multiple apps or extensions

Code Examples #

Private Mode (Simple)

// No setup needed
final crossvault = Crossvault();
await crossvault.setValue('token', 'value');

Shared Mode (Requires entitlements)

// Setup once
await Crossvault.init(
  options: IOSOptions(
    accessGroup: 'io.alexmelnyk.crossvault.shared',
  ),
);

// Use anywhere
final crossvault = Crossvault();
await crossvault.setValue('token', 'value');  // Shared with other apps

Mixed Mode (Both in same app)

// Global config for shared data
await Crossvault.init(
  options: IOSOptions(
    accessGroup: 'io.alexmelnyk.crossvault.shared',
  ),
);

final crossvault = Crossvault();

// Shared data (uses global config)
await crossvault.setValue('shared_token', 'value');

// Private data (override to remove access group)
await crossvault.setValue(
  'private_token',
  'value',
  options: IOSOptions(),  // No accessGroup = private
);

Important Notes #

  1. Team ID Prefix: The $(AppIdentifierPrefix) is automatically replaced with your Team ID by Xcode.

  2. Same Team ID: All apps sharing keychain data must be signed with the same Team ID.

  3. Access Group Format:

    • With prefix: $(AppIdentifierPrefix)com.yourcompany.shared
    • Full format: TEAM_ID.com.yourcompany.shared
  4. Without Access Group: If you don't specify an access group, data is stored privately for your app only. This is the default and most secure option.

  5. iCloud Sync: Available in both modes via synchronizable: true option.

Security Features #

Data Protection #

  • Uses kSecAttrAccessibleAfterFirstUnlock by default
  • Data is encrypted by iOS Keychain
  • Survives app reinstalls (unless explicitly deleted)
  • Protected by device passcode/biometrics

Access Control #

You can customize access control by modifying the kSecAttrAccessible attribute in the Swift code:

  • kSecAttrAccessibleWhenUnlocked - Most secure, only when device is unlocked
  • kSecAttrAccessibleAfterFirstUnlock - Default, balanced security
  • kSecAttrAccessibleAlways - Least secure, always accessible
  • kSecAttrAccessibleWhenUnlockedThisDeviceOnly - No iCloud sync
  • kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly - No iCloud sync

Error Handling #

The plugin throws PlatformException with the following error codes:

  • INVALID_ARGUMENT - Missing or invalid parameters
  • KEYCHAIN_ERROR - Keychain operation failed (includes OSStatus details)

Example:

try {
  await crossvault.setValue('key', 'value');
} on PlatformException catch (e) {
  if (e.code == 'KEYCHAIN_ERROR') {
    print('Keychain error: ${e.message}');
  }
}

FAQ #

Do I need to configure entitlements? #

No, only if you want to use Shared Mode (access groups). For private storage, no configuration is needed.

Can I use both private and shared storage in the same app? #

Yes! Use global config for one mode, and override with options parameter for the other:

// Global: shared mode
await Crossvault.init(
  options: IOSOptions(accessGroup: 'shared.group'),
);

// Shared (uses global)
await crossvault.setValue('shared_key', 'value');

// Private (override)
await crossvault.setValue(
  'private_key',
  'value',
  options: IOSOptions(),  // No accessGroup = private
);

What happens if I specify an access group without configuring entitlements? #

The keychain operation will fail with an error. You must configure entitlements in Xcode if you want to use access groups.

Is private mode more secure than shared mode? #

Yes, slightly. Private mode isolates data to your app only, while shared mode allows access from other apps with the same Team ID and access group. Both are encrypted by iOS Keychain.

Can I share data between my app and widget extension? #

Yes, use Shared Mode with the same access group in both targets.

Troubleshooting #

"Keychain operation failed" errors #

  1. Check that your app is properly signed
  2. Verify entitlements are correctly configured (if using access groups)
  3. Ensure access group names match exactly
  4. Check that all apps use the same Team ID

Access Group not working #

  1. Verify the access group is listed in entitlements
  2. Check that $(AppIdentifierPrefix) is used in entitlements
  3. Ensure both apps have the same access group configured
  4. Rebuild the app after changing entitlements

I don't want to use access groups, do I need to do anything? #

No! Just use Crossvault without specifying accessGroup in IOSOptions. Data will be stored privately for your app.

0
likes
130
points
20
downloads

Publisher

unverified uploader

Weekly Downloads

iOS implementation of crossvault plugin using Keychain with iCloud sync support. Provides hardware-backed security via Secure Enclave.

Homepage
Repository (GitHub)
View/report issues

Topics

#ios #keychain #security #icloud #secure-enclave

Documentation

API reference

License

unknown (license)

Dependencies

crossvault_platform_interface, flutter

More

Packages that depend on crossvault_ios

Packages that implement crossvault_ios