> ## Documentation Index
> Fetch the complete documentation index at: https://docs.contactsmanager.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Offline Functionality

> Understanding offline capabilities with the ContactsManager SDK

# Offline Functionality

The ContactsManager SDK stores contact data locally on your device, enabling basic offline functionality. This guide explains how contacts are managed in offline scenarios and how synchronization works.

## Offline-First Approach

The SDK stores all contact data locally on the device using SwiftData, which means:

1. All contact data is accessible offline through the local database
2. Contacts can be viewed and queried even without an internet connection
3. Changes made while offline are synchronized when connectivity is restored

## Working with Contacts Offline

### Fetching Contacts

You can fetch contacts from the local database at any time, regardless of connection status:

<CodeGroup>
  ```swift Swift theme={null}
  do {
      // Fetch contacts from local storage
      let contacts = try await ContactsService.shared.fetchContacts()
      print("Retrieved \(contacts.count) contacts from local storage")
  } catch {
      print("Error fetching contacts: \(error.localizedDescription)")
  }
  ```

  ```jsx React Native theme={null}
  import { fetchContacts } from '@contactsmanager/rn';

  try {
    // Fetch contacts from local storage
    const contacts = await fetchContacts();
    console.log(`Retrieved ${contacts.length} contacts from local storage`);
  } catch (error) {
    console.error('Error fetching contacts:', error);
  }
  ```

  ```kotlin Kotlin theme={null}
  // Coming Soon
  ```

  ```objectivec Objective-C theme={null}
  // Coming Soon
  ```
</CodeGroup>

You can also fetch contacts with specific field types:

<CodeGroup>
  ```swift Swift theme={null}
  do {
      // Fetch only contacts with phone numbers
      let contactsWithPhones = try await ContactsService.shared.fetchContacts(
          fieldType: .phone
      )
      print("Found \(contactsWithPhones.count) contacts with phone numbers")
  } catch {
      print("Error fetching contacts: \(error.localizedDescription)")
  }
  ```

  ```jsx React Native theme={null}
  import { fetchContacts, ContactFieldType } from '@contactsmanager/rn';

  try {
    // Fetch only contacts with phone numbers
    const contactsWithPhones = await fetchContacts(ContactFieldType.Phone);
    console.log(`Found ${contactsWithPhones.length} contacts with phone numbers`);
  } catch (error) {
    console.error('Error fetching contacts:', error);
  }
  ```

  ```kotlin Kotlin theme={null}
  // Coming Soon
  ```

  ```objectivec Objective-C theme={null}
  // Coming Soon
  ```
</CodeGroup>

### Fetching a Single Contact

To retrieve a specific contact:

<CodeGroup>
  ```swift Swift theme={null}
  do {
      // Fetch a contact from local storage
      if let contact = try await ContactsService.shared.fetchContact(withId: contactId) {
          print("Found contact: \(contact.displayName ?? "Unknown")")
      } else {
          print("Contact not found")
      }
  } catch {
      print("Error fetching contact: \(error.localizedDescription)")
  }
  ```

  ```jsx React Native theme={null}
  import { fetchContact } from '@contactsmanager/rn';

  try {
    // Fetch a contact from local storage
    const contact = await fetchContact(contactId);
    
    if (contact) {
      console.log(`Found contact: ${contact.displayName || 'Unknown'}`);
    } else {
      console.log('Contact not found');
    }
  } catch (error) {
    console.error('Error fetching contact:', error);
  }
  ```

  ```kotlin Kotlin theme={null}
  // Coming Soon
  ```

  ```objectivec Objective-C theme={null}
  // Coming Soon
  ```
</CodeGroup>

## Synchronization Management

### Manual Synchronization

The SDK automatically attempts to synchronize contacts when the app becomes active, when the contact store changes, or when contacts access is granted. You can also trigger manual synchronization:

<CodeGroup>
  ```swift Swift theme={null}
  do {
      // Force a sync with the server
      let syncedCount = try await ContactsService.shared.syncContacts()
      print("Synchronized \(syncedCount) contacts successfully")
  } catch {
      print("Synchronization error: \(error.localizedDescription)")
  }
  ```

  ```jsx React Native theme={null}
  import { syncContacts } from '@contactsmanager/rn';

  try {
    // Force a sync with the server
    const syncedCount = await syncContacts();
    console.log(`Synchronized ${syncedCount} contacts successfully`);
  } catch (error) {
    console.error('Synchronization error:', error);
  }
  ```

  ```kotlin Kotlin theme={null}
  // Coming Soon
  ```

  ```objectivec Objective-C theme={null}
  // Coming Soon
  ```
</CodeGroup>

### Background Synchronization

You can enable background synchronization by calling the following method during app initialization (typically in your AppDelegate or App):

<CodeGroup>
  ```swift Swift theme={null}
  // Enable background synchronization capabilities
  ContactsService.shared.enableBackgroundSync()
  ```

  ```jsx React Native theme={null}
  import { enableBackgroundSync } from '@contactsmanager/rn';

  // In your app initialization (e.g., App.js or index.js)
  enableBackgroundSync();
  ```

  ```kotlin Kotlin theme={null}
  // Coming Soon
  ```

  ```objectivec Objective-C theme={null}
  // Coming Soon
  ```
</CodeGroup>

This registers a background task with the system that will periodically synchronize contacts even when your app is in the background.

## Social Features in Offline Mode

Social features rely more heavily on server connectivity, but some operations are queued for later synchronization when offline.

### Following Contacts

To follow a contact (will be synchronized when online):

<CodeGroup>
  ```swift Swift theme={null}
  do {
      let result = try await ContactsService.shared.socialService.followContact(
          followedId: contactId,
          contactId: contactId
      )
      
      if let success = result.success, success {
          print("Follow request processed")
      } else if let alreadyFollowing = result.alreadyFollowing, alreadyFollowing {
          print("Already following this contact")
      }
  } catch {
      print("Error processing follow request: \(error.localizedDescription)")
  }
  ```

  ```jsx React Native theme={null}
  import { followContact } from '@contactsmanager/rn';

  try {
    const result = await followContact(contactId, contactId);
    
    if (result.success) {
      console.log('Follow request processed');
    } else if (result.alreadyFollowing) {
      console.log('Already following this contact');
    }
  } catch (error) {
    console.error('Error processing follow request:', error);
  }
  ```

  ```kotlin Kotlin theme={null}
  // Coming Soon
  ```

  ```objectivec Objective-C theme={null}
  // Coming Soon
  ```
</CodeGroup>

### Offline Limitations for Social Features

While contact data is fully accessible offline, social features have some limitations:

1. **New user discovery**: Finding new users requires connectivity
2. **Activity feeds**: New remote events can't be fetched while offline
3. **Follow status**: New follow relationships from other users won't be visible until synchronization

## Implementing an Offline-Aware UI

To provide a good user experience, your UI should be aware of potential synchronization states. Here's a simplified example:

<CodeGroup>
  ```swift Swift theme={null}
  struct ContactListView: View {
      @State private var contacts: [Contact] = []
      @State private var isLoading = false
      
      var body: some View {
          NavigationView {
              VStack {
                  if isLoading {
                      ProgressView("Loading contacts...")
                  } else {
                      List(contacts, id: \.id) { contact in
                          ContactRow(contact: contact)
                      }
                      .listStyle(PlainListStyle())
                  }
              }
              .navigationTitle("Contacts")
              .onAppear {
                  loadContacts()
              }
              .refreshable {
                  await syncContacts()
              }
          }
      }
      
      private func loadContacts() {
          isLoading = true
          Task {
              do {
                  let fetchedContacts = try await ContactsService.shared.fetchContacts()
                  
                  await MainActor.run {
                      self.contacts = fetchedContacts
                      self.isLoading = false
                  }
              } catch {
                  print("Error loading contacts: \(error.localizedDescription)")
                  await MainActor.run {
                      self.isLoading = false
                  }
              }
          }
      }
      
      private func syncContacts() async {
          do {
              try await ContactsService.shared.syncContacts()
              // Reload contacts after sync
              loadContacts()
          } catch {
              print("Error syncing contacts: \(error.localizedDescription)")
          }
      }
  }
  ```

  ```jsx React Native theme={null}
  import React, { useState, useEffect } from 'react';
  import {
    View,
    Text,
    FlatList,
    ActivityIndicator,
    StyleSheet,
    RefreshControl
  } from 'react-native';
  import { fetchContacts, syncContacts } from '@contactsmanager/rn';

  const ContactListScreen = () => {
    const [contacts, setContacts] = useState([]);
    const [isLoading, setIsLoading] = useState(false);
    const [isRefreshing, setIsRefreshing] = useState(false);
    
    useEffect(() => {
      loadContacts();
    }, []);
    
    const loadContacts = async () => {
      setIsLoading(true);
      
      try {
        const fetchedContacts = await fetchContacts();
        setContacts(fetchedContacts);
      } catch (error) {
        console.error('Error loading contacts:', error);
      } finally {
        setIsLoading(false);
      }
    };
    
    const handleRefresh = async () => {
      setIsRefreshing(true);
      
      try {
        await syncContacts();
        // Reload contacts after sync
        await loadContacts();
      } catch (error) {
        console.error('Error syncing contacts:', error);
      } finally {
        setIsRefreshing(false);
      }
    };
    
    const renderContactRow = ({ item }) => (
      <View style={styles.contactRow}>
        <Text style={styles.contactName}>{item.displayName || 'Unknown'}</Text>
        {item.phoneNumbers && item.phoneNumbers.length > 0 && (
          <Text style={styles.contactDetail}>{item.phoneNumbers[0].value}</Text>
        )}
      </View>
    );
    
    return (
      <View style={styles.container}>
        {isLoading ? (
          <View style={styles.loadingContainer}>
            <ActivityIndicator size="large" />
            <Text style={styles.loadingText}>Loading contacts...</Text>
          </View>
        ) : (
          <FlatList
            data={contacts}
            renderItem={renderContactRow}
            keyExtractor={(item) => item.id}
            refreshControl={
              <RefreshControl
                refreshing={isRefreshing}
                onRefresh={handleRefresh}
              />
            }
            ListEmptyComponent={
              <View style={styles.emptyContainer}>
                <Text style={styles.emptyText}>No contacts found</Text>
              </View>
            }
          />
        )}
      </View>
    );
  };

  const styles = StyleSheet.create({
    container: {
      flex: 1,
      backgroundColor: '#fff',
    },
    loadingContainer: {
      flex: 1,
      justifyContent: 'center',
      alignItems: 'center',
    },
    loadingText: {
      marginTop: 10,
      fontSize: 16,
      color: '#666',
    },
    contactRow: {
      padding: 15,
      borderBottomWidth: 1,
      borderBottomColor: '#f0f0f0',
    },
    contactName: {
      fontSize: 16,
      fontWeight: '600',
    },
    contactDetail: {
      fontSize: 14,
      color: '#666',
      marginTop: 4,
    },
    emptyContainer: {
      flex: 1,
      justifyContent: 'center',
      alignItems: 'center',
      padding: 50,
    },
    emptyText: {
      fontSize: 16,
      color: '#666',
    },
  });

  export default ContactListScreen;
  ```

  ```kotlin Kotlin theme={null}
  // Coming Soon
  ```

  ```objectivec Objective-C theme={null}
  // Coming Soon
  ```
</CodeGroup>

## Troubleshooting Offline Issues

### Common Issues

1. **Synchronization not completing**
   * Check for connectivity issues
   * Ensure the SDK is properly initialized
   * Verify that authentication tokens are valid

2. **Unexpected data loss**
   * Ensure the app isn't forcefully terminated during sync
   * Verify that device storage isn't critically low
   * Check for permission changes while offline

3. **Background sync issues**
   * Verify that you've called `enableBackgroundSync()` during app initialization
   * Check that your app has the proper background modes enabled in capabilities
   * Ensure you have the "Background Processing" entitlement for your app

### Logging and Debugging

The ContactsManager SDK uses the OSLog system for logging important events. To view these logs, use the Console app on macOS with your device connected, and filter for the subsystem "com.contactsmanager".
