How to build personalized paywalls with user attributes and Adapty segments

June 10, 2025 
by 
How To Build Personalized Paywalls With User Attributes And Adapty Segments 1 Preview 1

Generic paywalls don’t work for everyone. In a fitness app, users have diverse goals: muscle gain, cardio stamina, yoga balance. Why treat them all the same?

With Adapty and HealthKit, you can build personalized paywalls based on the user’s workout activity type, offering content and offers tailored to their actual fitness habits.

This guide walks you through how to:

  • Detect a user’s workout type via HealthKit
  • Store it as a custom attribute in Adapty
  • Segment users in the Adapty dashboard
  • Serve different paywalls to each workout segment dynamically in SwiftUI

Let’s get started.

What’s wrong with generic paywalls?

A running enthusiast might respond better to “speed endurance plans,” while a cyclist would benefit more from “road performance packs.” Hardcoding these conditions quickly becomes unmanageable. Firebase Remote Config helps, but lacks strong analytics, A/B testing, or easy paywall editing.

With Adapty, we offload this logic to the dashboard, allowing for seamless visual and logical updates without modifying app code.

How can I get user workout data from HealthKit?

In my own app StrongerMe, I implemented this setup to show targeted offers to runners, swimmers, cyclists, and walkers. This allows each type of athlete to see offers that resonate with their habits and goals. For “Other” we will show the common paywall.

What HealthKit permissions are required?

Before accessing workout data, add the required HealthKit permissions and entries in your Info.plist:

<key>NSHealthShareUsageDescription</key>
<string>We use your activity data to personalize your fitness experience and offers.</string>
<key>NSHealthUpdateUsageDescription</key>
<string>We update your activity preferences based on your workouts.</string>

Find more info about the keys:

After that you can request the authorization in your app:

let healthStore = HKHealthStore()
let typesToRead: Set = [HKObjectType.workoutType()]

healthStore.requestAuthorization(toShare: [], read: typesToRead) { success, error in
    // Handle result like store HealthKit flag in UserDefault
}

How can I detect the most recent workout?

We focus on four types for this example:

  • running
  • walking
  • cycling
  • swimming
  • other
import HealthKit

func getMostRecentWorkoutType() async throws -> String? {
    let store = HKHealthStore()
    let workoutType = HKObjectType.workoutType()

    let predicate = HKQuery.predicateForSamples(withStart: Date.distantPast, end: Date(), options: [])
    let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)

    let query = HKSampleQuery(sampleType: workoutType, predicate: predicate, limit: 1, sortDescriptors: [sortDescriptor]) { _, samples, _ in
        guard let workout = samples?.first as? HKWorkout else { return }

        let workoutType = workout.workoutActivityType
        let exerciseType: String

        switch workoutType {
        case .running: exerciseType = "running"     
        case .walking: exerciseType = "walking"    
        case .cycling: exerciseType = "cycling"    
        case .swimming: exerciseType = "swimming"   
        default: exerciseType = "other"
        }

        // Now update Adapty attribute
        var builder = AdaptyProfileParameters.Builder()
        try? builder = builder.with(customAttribute: exerciseType, forKey: "exercise_type")
        try? await Adapty.updateProfile(params: builder.build())
    }

    store.execute(query)
    return nil
}

How can I store workout data in Adapty?

Once you have the workout type string, pass it to Adapty as a custom attribute. Of course we need no know about this field before showing the Paywall. You can set the default type early on login or app open:

var builder = AdaptyProfileParameters.Builder()
try? builder = builder.with(customAttribute: "running", forKey: "exercise_type")
try? await Adapty.updateProfile(params: builder.build())

What are user attribute limits?

  • Up to 30 custom attributes per user
  • Key name: up to 30 characters (letters, numbers, _, -, or . allowed)
  • Value: string or float, max 50 characters long

Check User Attributes in Adapty

Should you identify the user?

If your app supports accounts, call identify after login:

try await Adapty.identify("user_id")

Keep in mind: If you’ve assigned any data to an anonymous user such as custom attributes or tracking parameters from third-party sources make sure to reassociate that data with the user once they are identified.

How can I create segments based on exercise type?

We now need a criteria – Segment – which will be linked to exercise_type from HealthKit.

⚠️ Segments are available only on a paid Adapty subscription plan.

How do you create segments in the Adapty dashboard?

In the Adapty Dashboard:

  • Go to Segments, click Create Segment and Create custom attribute.
Adapty dashboard showing new segment creation form with "Runners" as segment name, description "All users who usual prefer Running exercise", and dropdown to create custom attribute for exercise type segmentation
New segment creation
  • Create a Custom attribute. It’s better to populate the expected values so we can use it in Segment filtering later.
Adapty custom attribute creation dialog showing "Exercise Type" attribute setup with key "exercise_type", String type selected, and predefined values: running, walking, cycling with Add value button and Save/Cancel options
New custom attribute creation
  • Add info and filtering for our attribute
Adapty "Runners" segment configuration showing segment name field, description "All users who usually prefer Running exercises", and filtering rule: Custom Exercise Type (string) equals "running"

Create these segments:

  • “Runners”Exercise Type == running
  • “Riders”Exercise Type == cycling
  • “Swimmers”Exercise Type == swimming
  • “Walkers”Exercise Type == walking
  • “Other”Exercise Type == other

Each segment can then be tied to unique audiences and personalized paywalls.

The list for me is looking like this:

Adapty Segments dashboard showing five created exercise segments: Runners, Walkers, Swimmers, Other, and Riders, each with 0 users and created on Jun 7 2025, with descriptions for running, walking, swimming, other, and cycling exercises respectively
All created segments

How do you assign paywalls to segments?

Creating paywalls

Now we need paywalls to split among the segments. Let’s create a pack of them to match our exercises. The docs cover the whole creation process here.

Adapty Paywalls dashboard displaying five exercise-specific paywalls: Monthly[Running], Monthly[Other], Monthly[Cycling], Monthly[Swimming], and Monthly[Walking], all showing $0 revenue, proceeds, and net proceeds with 0 purchases and trials

All of them have separate images, text to attract the specified audience. Isn’t that great to have content based on exercise?!

Three mobile personalized paywalls showing exercise-specific designs: left shows running paywall with woman runner and "START NEW LEVEL OF RUNNING TODAY!", center shows cycling paywall with cyclist and "START NEW LEVEL OF CYCLING TODAY!", right shows walking paywall with woman in gym and "START NEW LEVEL OF WALKING TODAY!" - each with tailored imagery and messaging

What are Audiences in Adapty?

An Audience in Adapty defines which users will see a specific version of a paywall. It is a dynamic group that can include one or more Segments based on custom attributes like exercise_type.

For example, in the placement “main_subscription”:

  • The “Running Paywall” audience includes the segment “Runners”
  • The “Riders Paywall” audience includes “Riders”

Check more on Audiences in Adapty

Right now there is All users audience setup. This means our segmentation doesn’t split users right now. But not for long.

Adapty Main placement analytics dashboard showing "All users" audience with $0 revenue, 0 unique views, $0.00 ARPAS, 0 trials, 0 purchases, and 0 refunds - demonstrating current unsegmented setup before implementing exercise-based audiences

Let’s tap Edit placement and then Add audience:

Adapty "Add audience" dialog showing "Runners" audience selection with "Run Paywall" option selected and "Monthly[Running]" paywall chosen from

Need to select our Segment (Runners in example) and related Paywall. After that it will appear in our placements splitting.

Adapty Main placement configuration showing two audiences: "Runners" audience (#1) assigned Monthly[Running] paywall started Jun 7 2025, and "All Users" audience (#2) assigned Monthly[Other] paywall, with options to change paywalls or run A/B tests for each audience

By repeating the same steps for each Segment and Paywall we will get smooth coverage of all exercises. Prioritize audiences if overlaps exist. And all this without app modification.

How do you fetch and display paywall in SwiftUI?

Now all that is left is to show our paywall.

let placementId = "main_placement"
let paywall = try await Adapty.getPaywall(placementId: placementId)
let config = try await AdaptyUI.getPaywallConfiguration(forPaywall: paywall)

.paywall(isPresented: $showPaywall, paywallConfiguration: config) { action in
    if case .close = action {
        showPaywall = false
    }
    // handle purchases if needed
}

Adapty will automatically serve the matching paywall based on exercise_type segment.

How does this compare to Firebase Remote Config / Hardcoded logic?

FeatureAdaptyFirebase Remote ConfigHardcoded logic
Visual paywall editor
A/B testing
Analytics⚠️ Basic
Easy updates⚠️ Config only
Segmentation
Purchase handling
Compare list of features

Conclusion

Using exercise_type from HealthKit as a custom attribute in Adapty allows you to:

  • Personalize paywalls based on actual fitness behavior
  • Avoid manual logic in code or Firebase config
  • Adjust offers, creatives, and pricing per segment in the dashboard
  • Iterate quickly with analytics and A/B testing

This approach results in better user experience and improved conversions with minimal developer effort.

Anton Gubarenko
Senior iOS Engineer & Contractor with over 10 years of experience, helping organizations worldwide shape and deliver their mobile strategies. Passionate about building intuitive, high-quality mobile experiences that truly resonate with users.
Tutorial

On this page

Ready to create your first paywall with Adapty?
Build money-making paywalls without coding
Get started for free