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

Cohort Triggers

Query Triggers

User ID

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
Use TriggerProvider instead of polling cohorts or activations for reactive behavior.

Accessing TriggerProvider

let triggerProvider = Permutive.shared.triggerProvider
id<PermutiveTriggerProvider> triggerProvider = Permutive.shared.triggerProvider;

Cohort Membership Triggers

Watch for changes in cohort membership:
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
    }
}
@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

Watching Multiple Cohorts

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
                    }
                }
            }
        )
    }
}
- (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];
                }
            }
        }];
}

Query Triggers

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

Dictionary Queries

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)
                }
            }
        )
    }
}
- (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];
                }
            }
        }];
}

Integer Queries

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
    }
}
- (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];
        }];
}

User ID Triggers

Monitor changes to the user identity:
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
    }
}
@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

TriggerAction Lifecycle

You must maintain a strong reference to TriggerAction objects. Releasing the reference stops the trigger.

Keeping Triggers Active

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

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
    }
}
@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

SwiftUI Integration

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

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

Best Practices

  • 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

Troubleshooting

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
Problem: Trigger worked initially but stopped.Cause: TriggerAction reference was released.Solution: Store TriggerAction as an instance property, not a local variable.
Problem: Callback fires but UI doesn’t change.Solution: Dispatch UI updates to main thread:
DispatchQueue.main.async {
    self.updateUI()
}

Cohorts & Activations

Understanding user segments

Identity Management

User identity tracking

Page Tracking

Trigger cohort qualification

Issues

Common problems and solutions