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

# Triggers Provider

> React to cohort changes and user identity updates in real-time

`TriggerProvider` enables reactive updates when users enter or exit cohorts, allowing your app to respond to segment changes in real-time.

<CardGroup cols={3}>
  <Card title="Cohort Triggers" href="#cohort-membership-triggers" icon="users" />

  <Card title="Query Triggers" href="#query-triggers" icon="magnifying-glass" />

  <Card title="User ID" href="#user-id-triggers" icon="user" />
</CardGroup>

## Overview

`TriggerProvider` provides:

* **Real-time cohort change notifications**
* **Immediate callback when users enter/exit cohorts**
* **Query-based triggers** for complex conditions
* **User identity change monitoring**

<Tip>
  Use `TriggerProvider` instead of polling `cohorts` or `activations` for reactive behavior.
</Tip>

## Accessing TriggerProvider

<CodeGroup>
  ```swift Swift theme={"dark"}
  let triggerProvider = Permutive.shared.triggerProvider
  ```

  ```objectivec Objective-C theme={"dark"}
  id<PermutiveTriggerProvider> triggerProvider = Permutive.shared.triggerProvider;
  ```
</CodeGroup>

## Cohort Membership Triggers

Watch for changes in cohort membership:

<CodeGroup>
  ```swift Swift theme={"dark"}
  import Permutive_iOS

  class FeatureController {
      private var premiumTrigger: TriggerAction?

      func watchPremiumStatus() {
          premiumTrigger = Permutive.shared.triggerProvider?.action(
              boolFor: ["premium_subscriber"],
              action: { cohortId, isInCohort in
                  print("Cohort '\(cohortId)' membership: \(isInCohort)")

                  if isInCohort {
                      self.enablePremiumFeatures()
                  } else {
                      self.disablePremiumFeatures()
                  }
              }
          )
      }

      private func enablePremiumFeatures() {
          // Show premium content
      }

      private func disablePremiumFeatures() {
          // Hide premium content
      }

      deinit {
          // Release trigger to stop watching
          premiumTrigger = nil
      }
  }
  ```

  ```objectivec Objective-C theme={"dark"}
  @interface FeatureController ()
  @property (nonatomic, strong) PermutiveTriggerAction *premiumTrigger;
  @end

  @implementation FeatureController

  - (void)watchPremiumStatus {
      NSSet *cohorts = [NSSet setWithObject:@"premium_subscriber"];

      self.premiumTrigger = [Permutive.shared.triggerProvider
          actionWithBoolFor:cohorts
          action:^(NSString *cohortId, BOOL isInCohort) {
              NSLog(@"Cohort '%@' membership: %@", cohortId, isInCohort ? @"YES" : @"NO");

              if (isInCohort) {
                  [self enablePremiumFeatures];
              } else {
                  [self disablePremiumFeatures];
              }
          }];
  }

  - (void)dealloc {
      // Release trigger
      self.premiumTrigger = nil;
  }

  @end
  ```
</CodeGroup>

### Watching Multiple Cohorts

<CodeGroup>
  ```swift Swift theme={"dark"}
  class ContentPersonalizer {
      private var contentTrigger: TriggerAction?

      func watchUserInterests() {
          contentTrigger = Permutive.shared.triggerProvider?.action(
              boolFor: ["sports_enthusiast", "tech_reader", "news_follower"],
              action: { cohortId, isInCohort in
                  if isInCohort {
                      switch cohortId {
                      case "sports_enthusiast":
                          self.boostSportsContent()
                      case "tech_reader":
                          self.boostTechContent()
                      case "news_follower":
                          self.boostNewsContent()
                      default:
                          break
                      }
                  }
              }
          )
      }
  }
  ```

  ```objectivec Objective-C theme={"dark"}
  - (void)watchUserInterests {
      NSSet *cohorts = [NSSet setWithObjects:
          @"sports_enthusiast", @"tech_reader", @"news_follower", nil];

      self.contentTrigger = [Permutive.shared.triggerProvider
          actionWithBoolFor:cohorts
          action:^(NSString *cohortId, BOOL isInCohort) {
              if (isInCohort) {
                  if ([cohortId isEqualToString:@"sports_enthusiast"]) {
                      [self boostSportsContent];
                  } else if ([cohortId isEqualToString:@"tech_reader"]) {
                      [self boostTechContent];
                  } else if ([cohortId isEqualToString:@"news_follower"]) {
                      [self boostNewsContent];
                  }
              }
          }];
  }
  ```
</CodeGroup>

## Query Triggers

For more complex data, use query triggers that return dictionaries or integers:

### Dictionary Queries

<CodeGroup>
  ```swift Swift theme={"dark"}
  class AnalyticsController {
      private var queryTrigger: TriggerAction?

      func watchComplexQueries() {
          queryTrigger = Permutive.shared.triggerProvider?.action(
              dictionaryFor: ["user_profile_query", "engagement_metrics"],
              action: { queryId, result in
                  print("Query '\(queryId)' returned: \(result)")

                  if queryId == "user_profile_query",
                     let preferences = result["preferences"] as? [String: Any] {
                      self.applyUserPreferences(preferences)
                  }
              }
          )
      }
  }
  ```

  ```objectivec Objective-C theme={"dark"}
  - (void)watchComplexQueries {
      NSSet *queries = [NSSet setWithObjects:@"user_profile_query", @"engagement_metrics", nil];

      self.queryTrigger = [Permutive.shared.triggerProvider
          actionWithDictionaryFor:queries
          action:^(NSString *queryId, NSDictionary<id<NSCopying>, NSObject *> *result) {
              NSLog(@"Query '%@' returned: %@", queryId, result);

              if ([queryId isEqualToString:@"user_profile_query"]) {
                  NSDictionary *preferences = result[@"preferences"];
                  if (preferences) {
                      [self applyUserPreferences:preferences];
                  }
              }
          }];
  }
  ```
</CodeGroup>

### Integer Queries

<CodeGroup>
  ```swift Swift theme={"dark"}
  class ScoreController {
      private var scoreTrigger: TriggerAction?

      func watchEngagementScore() {
          scoreTrigger = Permutive.shared.triggerProvider?.action(
              intFor: ["engagement_score"],
              action: { queryId, score in
                  print("Engagement score: \(score)")

                  self.updateUIForScore(score)
              }
          )
      }

      private func updateUIForScore(_ score: Int) {
          // Adjust UI based on engagement level
      }
  }
  ```

  ```objectivec Objective-C theme={"dark"}
  - (void)watchEngagementScore {
      NSSet *queries = [NSSet setWithObject:@"engagement_score"];

      self.scoreTrigger = [Permutive.shared.triggerProvider
          actionWithIntFor:queries
          action:^(NSString *queryId, NSInteger score) {
              NSLog(@"Engagement score: %ld", (long)score);
              [self updateUIForScore:score];
          }];
  }
  ```
</CodeGroup>

## User ID Triggers

Monitor changes to the user identity:

<CodeGroup>
  ```swift Swift theme={"dark"}
  class SessionController {
      private var userIdTrigger: TriggerAction?

      func watchUserIdentityChanges() {
          userIdTrigger = Permutive.shared.triggerProvider?.actionUpdateForUserIdentity { userId in
              print("User identity changed to: \(userId)")

              // Update analytics with new user ID
              Analytics.setUserId(userId)

              // Refresh user-specific data
              self.refreshUserData()
          }
      }

      deinit {
          userIdTrigger = nil
      }
  }
  ```

  ```objectivec Objective-C theme={"dark"}
  @interface SessionController ()
  @property (nonatomic, strong) PermutiveTriggerAction *userIdTrigger;
  @end

  @implementation SessionController

  - (void)watchUserIdentityChanges {
      self.userIdTrigger = [Permutive.shared.triggerProvider
          actionUpdateForUserIdentity:^(NSString *userId) {
              NSLog(@"User identity changed to: %@", userId);
              [Analytics setUserId:userId];
              [self refreshUserData];
          }];
  }

  - (void)dealloc {
      self.userIdTrigger = nil;
  }

  @end
  ```
</CodeGroup>

## TriggerAction Lifecycle

<Warning>
  You must maintain a strong reference to `TriggerAction` objects. Releasing the reference stops the trigger.
</Warning>

### Keeping Triggers Active

```swift theme={"dark"}
class TriggerManager {
    // Store triggers as instance properties
    private var cohortTrigger: TriggerAction?
    private var userIdTrigger: TriggerAction?

    func setupTriggers() {
        cohortTrigger = Permutive.shared.triggerProvider?.action(
            boolFor: ["cohort"],
            action: { _, _ in }
        )

        userIdTrigger = Permutive.shared.triggerProvider?.actionUpdateForUserIdentity { _ in }
    }

    func stopWatching() {
        // Explicitly release triggers
        cohortTrigger = nil
        userIdTrigger = nil
    }

    deinit {
        // Automatically released when controller is deallocated
    }
}
```

## UIViewController Integration

<CodeGroup>
  ```swift Swift theme={"dark"}
  class PersonalizedViewController: UIViewController {
      private var cohortTrigger: TriggerAction?

      override func viewDidLoad() {
          super.viewDidLoad()

          // Start watching cohorts
          cohortTrigger = Permutive.shared.triggerProvider?.action(
              boolFor: ["premium_user", "new_user"],
              action: { [weak self] cohortId, isInCohort in
                  DispatchQueue.main.async {
                      self?.handleCohortChange(cohortId: cohortId, isInCohort: isInCohort)
                  }
              }
          )
      }

      private func handleCohortChange(cohortId: String, isInCohort: Bool) {
          switch cohortId {
          case "premium_user":
              updatePremiumUI(enabled: isInCohort)
          case "new_user":
              showOnboarding(isNew: isInCohort)
          default:
              break
          }
      }

      deinit {
          cohortTrigger = nil
      }
  }
  ```

  ```objectivec Objective-C theme={"dark"}
  @interface PersonalizedViewController ()
  @property (nonatomic, strong) PermutiveTriggerAction *cohortTrigger;
  @end

  @implementation PersonalizedViewController

  - (void)viewDidLoad {
      [super viewDidLoad];

      NSSet *cohorts = [NSSet setWithObjects:@"premium_user", @"new_user", nil];

      __weak typeof(self) weakSelf = self;
      self.cohortTrigger = [Permutive.shared.triggerProvider
          actionWithBoolFor:cohorts
          action:^(NSString *cohortId, BOOL isInCohort) {
              dispatch_async(dispatch_get_main_queue(), ^{
                  [weakSelf handleCohortChange:cohortId isInCohort:isInCohort];
              });
          }];
  }

  - (void)handleCohortChange:(NSString *)cohortId isInCohort:(BOOL)isInCohort {
      if ([cohortId isEqualToString:@"premium_user"]) {
          [self updatePremiumUI:isInCohort];
      } else if ([cohortId isEqualToString:@"new_user"]) {
          [self showOnboarding:isInCohort];
      }
  }

  - (void)dealloc {
      self.cohortTrigger = nil;
  }

  @end
  ```
</CodeGroup>

## SwiftUI Integration

```swift theme={"dark"}
import SwiftUI
import Permutive_iOS

struct PersonalizedView: View {
    @StateObject private var triggerManager = TriggerManager()

    var body: some View {
        VStack {
            if triggerManager.isPremium {
                PremiumContentView()
            } else {
                StandardContentView()
            }
        }
        .onAppear {
            triggerManager.startWatching()
        }
        .onDisappear {
            triggerManager.stopWatching()
        }
    }
}

class TriggerManager: ObservableObject {
    @Published var isPremium = false

    private var trigger: TriggerAction?

    func startWatching() {
        trigger = Permutive.shared.triggerProvider?.action(
            boolFor: ["premium_subscriber"],
            action: { [weak self] _, isInCohort in
                DispatchQueue.main.async {
                    self?.isPremium = isInCohort
                }
            }
        )
    }

    func stopWatching() {
        trigger = nil
    }
}
```

## tvOS Considerations

<Info>
  **tvOS Note:** TriggerProvider works identically on tvOS. Use it to update UI elements based on cohort membership in your focus-based navigation.
</Info>

## Best Practices

<Tabs>
  <Tab title="Do">
    * Store TriggerAction references as instance properties
    * Use `[weak self]` in closures to avoid retain cycles
    * Dispatch UI updates to main thread
    * Release triggers when no longer needed
    * Group related cohorts in a single trigger
  </Tab>

  <Tab title="Don't">
    * Don't create triggers without storing the reference
    * Don't create excessive numbers of triggers
    * Don't perform heavy work in trigger callbacks
    * Don't assume immediate callback on creation
    * Don't forget to handle both enter and exit cases
  </Tab>
</Tabs>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Trigger callbacks not firing">
    **Problem:** No callbacks when cohort membership changes.

    **Solutions:**

    * Verify you're storing a strong reference to TriggerAction
    * Check that cohort IDs match your dashboard
    * Enable debug logging to see cohort updates
    * Ensure SDK is initialized before creating triggers
  </Accordion>

  <Accordion title="Trigger stops working">
    **Problem:** Trigger worked initially but stopped.

    **Cause:** TriggerAction reference was released.

    **Solution:** Store TriggerAction as an instance property, not a local variable.
  </Accordion>

  <Accordion title="UI not updating">
    **Problem:** Callback fires but UI doesn't change.

    **Solution:** Dispatch UI updates to main thread:

    ```swift theme={"dark"}
    DispatchQueue.main.async {
        self.updateUI()
    }
    ```
  </Accordion>
</AccordionGroup>

## Related Documentation

<CardGroup cols={2}>
  <Card title="Cohorts & Activations" icon="users" href="/sdks/mobile/ios/core-concepts/cohorts-and-activations">
    Understanding user segments
  </Card>

  <Card title="Identity Management" icon="user" href="/sdks/mobile/ios/core-concepts/identity-management">
    User identity tracking
  </Card>

  <Card title="Page Tracking" icon="file" href="/sdks/mobile/ios/features/page-tracking">
    Trigger cohort qualification
  </Card>

  <Card title="Issues" icon="triangle-exclamation" href="/sdks/mobile/ios/troubleshooting/common-errors">
    Common problems and solutions
  </Card>
</CardGroup>
