From 40a1f4a03496300859d8b4aa5fec834e28fb41ed Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 20:34:17 +0000 Subject: [PATCH 1/6] Add fontScale and preferredContentSizeCategory device attributes --- CHANGELOG.md | 6 ++++++ .../Network/Device Helper/DeviceHelper.swift | 21 +++++++++++++++++++ .../Templating/Models/DeviceTemplate.swift | 2 ++ 3 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9acd565130..6dc3c51b7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ The changelog for `SuperwallKit`. Also see the [releases](https://github.com/superwall/Superwall-iOS/releases) on GitHub. +## Unreleased + +### Enhancements + +- Adds the system-wide text size (Dynamic Type) as device attributes. `fontScale` is a numeric multiplier relative to the default text size (~1.0 at the default size, smaller/larger otherwise), and `preferredContentSizeCategory` is the raw native Dynamic Type category string. + ## 4.16.1 ### Enhancements diff --git a/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift b/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift index ec3e453d40..1732f4c4c9 100644 --- a/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift +++ b/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift @@ -207,6 +207,25 @@ class DeviceHelper { #endif } + var fontScale: Double { + #if os(visionOS) + return 1.0 + #else + let scaledBodyPointSize = UIFontMetrics.default.scaledValue(for: 17.0) + return Double(scaledBodyPointSize / 17.0) + #endif + } + + var preferredContentSizeCategory: String { + #if os(visionOS) + return "unspecified" + #else + let category = UIApplication.sharedApplication?.preferredContentSizeCategory + ?? UIScreen.main.traitCollection.preferredContentSizeCategory + return category.rawValue + #endif + } + var platformWrapper: String? var platformWrapperVersion: String? @@ -606,6 +625,8 @@ class DeviceHelper { timezoneOffset: Int(TimeZone.current.secondsFromGMT()), radioType: radioType, interfaceStyle: interfaceStyle, + fontScale: fontScale, + preferredContentSizeCategory: preferredContentSizeCategory, isLowPowerModeEnabled: isLowPowerModeEnabled == "true", isApplePayAvailable: true, bundleId: bundleId, diff --git a/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift b/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift index 3849d7f1a0..230e1a1587 100644 --- a/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift +++ b/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift @@ -29,6 +29,8 @@ struct DeviceTemplate: Codable { var timezoneOffset: Int var radioType: String var interfaceStyle: String + var fontScale: Double + var preferredContentSizeCategory: String var isLowPowerModeEnabled: Bool var isApplePayAvailable: Bool var bundleId: String From a5bfffd3f1047dd9c32cb2d8f8eec3febfd2e85b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Jul 2026 11:22:26 +0000 Subject: [PATCH 2/6] Use normalized fontSize (px) device attribute --- CHANGELOG.md | 2 +- .../Network/Device Helper/DeviceHelper.swift | 20 ++++--------------- .../Templating/Models/DeviceTemplate.swift | 3 +-- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dc3c51b7d..a266acfbf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The changelog for `SuperwallKit`. Also see the [releases](https://github.com/sup ### Enhancements -- Adds the system-wide text size (Dynamic Type) as device attributes. `fontScale` is a numeric multiplier relative to the default text size (~1.0 at the default size, smaller/larger otherwise), and `preferredContentSizeCategory` is the raw native Dynamic Type category string. +- Adds the system-wide text size (Dynamic Type) as a `fontSize` device attribute. It is a normalized integer point/px value where `16` is the system default, scaling up or down as the user changes their Dynamic Type setting. ## 4.16.1 diff --git a/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift b/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift index 1732f4c4c9..b82429ea07 100644 --- a/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift +++ b/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift @@ -207,22 +207,11 @@ class DeviceHelper { #endif } - var fontScale: Double { + var fontSize: Int { #if os(visionOS) - return 1.0 + return 16 #else - let scaledBodyPointSize = UIFontMetrics.default.scaledValue(for: 17.0) - return Double(scaledBodyPointSize / 17.0) - #endif - } - - var preferredContentSizeCategory: String { - #if os(visionOS) - return "unspecified" - #else - let category = UIApplication.sharedApplication?.preferredContentSizeCategory - ?? UIScreen.main.traitCollection.preferredContentSizeCategory - return category.rawValue + return Int(UIFontMetrics.default.scaledValue(for: 16.0).rounded()) #endif } @@ -625,8 +614,7 @@ class DeviceHelper { timezoneOffset: Int(TimeZone.current.secondsFromGMT()), radioType: radioType, interfaceStyle: interfaceStyle, - fontScale: fontScale, - preferredContentSizeCategory: preferredContentSizeCategory, + fontSize: fontSize, isLowPowerModeEnabled: isLowPowerModeEnabled == "true", isApplePayAvailable: true, bundleId: bundleId, diff --git a/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift b/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift index 230e1a1587..ecdd44738d 100644 --- a/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift +++ b/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift @@ -29,8 +29,7 @@ struct DeviceTemplate: Codable { var timezoneOffset: Int var radioType: String var interfaceStyle: String - var fontScale: Double - var preferredContentSizeCategory: String + var fontSize: Int var isLowPowerModeEnabled: Bool var isApplePayAvailable: Bool var bundleId: String From 0fedd7afda02e44736d0db115666b26567f821d9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Jul 2026 11:25:44 +0000 Subject: [PATCH 3/6] Add fontScale and preferredContentSizeCategory device attributes --- CHANGELOG.md | 2 +- .../Network/Device Helper/DeviceHelper.swift | 35 +++++++++++++++++++ .../Templating/Models/DeviceTemplate.swift | 2 ++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a266acfbf0..d00a1540ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The changelog for `SuperwallKit`. Also see the [releases](https://github.com/sup ### Enhancements -- Adds the system-wide text size (Dynamic Type) as a `fontSize` device attribute. It is a normalized integer point/px value where `16` is the system default, scaling up or down as the user changes their Dynamic Type setting. +- Adds the user's system-wide text size (Dynamic Type) as three device attributes for use in paywalls and audience filters: `fontScale`, a scale multiplier where `1.0` is the system default (values below 1.0 are smaller, above 1.0 are larger/accessibility sizes); `fontSize`, a normalized integer point/px value where `16` is the system default, scaling up or down with the Dynamic Type setting; and `preferredContentSizeCategory`, a clean Dynamic Type token (e.g. `large`) describing the selected size category. ## 4.16.1 diff --git a/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift b/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift index b82429ea07..df3107fbf4 100644 --- a/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift +++ b/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift @@ -215,6 +215,39 @@ class DeviceHelper { #endif } + var fontScale: Double { + #if os(visionOS) + return 1.0 + #else + let scale = UIFontMetrics.default.scaledValue(for: 16.0) / 16.0 + return (scale * 100).rounded() / 100 + #endif + } + + var preferredContentSizeCategory: String { + #if os(visionOS) + return "unspecified" + #else + let category = UIApplication.sharedApplication?.preferredContentSizeCategory + ?? UIScreen.main.traitCollection.preferredContentSizeCategory + switch category { + case .extraSmall: return "xSmall" + case .small: return "small" + case .medium: return "medium" + case .large: return "large" + case .extraLarge: return "xLarge" + case .extraExtraLarge: return "xxLarge" + case .extraExtraExtraLarge: return "xxxLarge" + case .accessibilityMedium: return "accessibilityMedium" + case .accessibilityLarge: return "accessibilityLarge" + case .accessibilityExtraLarge: return "accessibilityXLarge" + case .accessibilityExtraExtraLarge: return "accessibilityXXLarge" + case .accessibilityExtraExtraExtraLarge: return "accessibilityXXXLarge" + default: return "unspecified" + } + #endif + } + var platformWrapper: String? var platformWrapperVersion: String? @@ -615,6 +648,8 @@ class DeviceHelper { radioType: radioType, interfaceStyle: interfaceStyle, fontSize: fontSize, + fontScale: fontScale, + preferredContentSizeCategory: preferredContentSizeCategory, isLowPowerModeEnabled: isLowPowerModeEnabled == "true", isApplePayAvailable: true, bundleId: bundleId, diff --git a/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift b/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift index ecdd44738d..8a77c694b9 100644 --- a/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift +++ b/Sources/SuperwallKit/Paywall/View Controller/Web View/Templating/Models/DeviceTemplate.swift @@ -30,6 +30,8 @@ struct DeviceTemplate: Codable { var radioType: String var interfaceStyle: String var fontSize: Int + var fontScale: Double + var preferredContentSizeCategory: String var isLowPowerModeEnabled: Bool var isApplePayAvailable: Bool var bundleId: String From 3a08143525c4d51bc208bea47ec545611277cefc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20To=CC=88r?= <3296904+yusuftor@users.noreply.github.com> Date: Fri, 3 Jul 2026 13:56:14 +0200 Subject: [PATCH 4/6] Update version and changelog --- CHANGELOG.md | 4 ++-- Sources/SuperwallKit/Misc/Constants.swift | 2 +- SuperwallKit.podspec | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d00a1540ab..d06cfe70fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,11 @@ The changelog for `SuperwallKit`. Also see the [releases](https://github.com/superwall/Superwall-iOS/releases) on GitHub. -## Unreleased +## 4.16.2 ### Enhancements -- Adds the user's system-wide text size (Dynamic Type) as three device attributes for use in paywalls and audience filters: `fontScale`, a scale multiplier where `1.0` is the system default (values below 1.0 are smaller, above 1.0 are larger/accessibility sizes); `fontSize`, a normalized integer point/px value where `16` is the system default, scaling up or down with the Dynamic Type setting; and `preferredContentSizeCategory`, a clean Dynamic Type token (e.g. `large`) describing the selected size category. +- Adds the user's system-wide text size (Dynamic Type) as three device attributes for use in paywalls and audience filters: `fontScale`, `fontSize`, and `preferredContentSizeCategory`. ## 4.16.1 diff --git a/Sources/SuperwallKit/Misc/Constants.swift b/Sources/SuperwallKit/Misc/Constants.swift index e38ade56b0..20f05d6af4 100644 --- a/Sources/SuperwallKit/Misc/Constants.swift +++ b/Sources/SuperwallKit/Misc/Constants.swift @@ -18,5 +18,5 @@ let sdkVersion = """ */ let sdkVersion = """ -4.16.1 +4.16.2 """ diff --git a/SuperwallKit.podspec b/SuperwallKit.podspec index 3115a9e217..d18d4169e7 100644 --- a/SuperwallKit.podspec +++ b/SuperwallKit.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "SuperwallKit" - s.version = "4.16.1" + s.version = "4.16.2" s.summary = "Superwall: In-App Paywalls Made Easy" s.description = "Paywall infrastructure for mobile apps :) we make things like editing your paywall and running price tests as easy as clicking a few buttons. superwall.com" From 08a1d5a31d1dbb89faccbdc9830b0886d816c522 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Jul 2026 12:03:00 +0000 Subject: [PATCH 5/6] Add test for preferredContentSizeCategory token mapping Extract the UIContentSizeCategory-to-dashboard-token switch into a pure static DeviceHelper.contentSizeCategoryToken(for:) and lock every token via unit tests, since these tokens are a backend audience-filter contract. --- .../Network/Device Helper/DeviceHelper.swift | 8 ++++++- .../Network/DeviceHelperTests.swift | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift b/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift index df3107fbf4..3c1c1a2494 100644 --- a/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift +++ b/Sources/SuperwallKit/Network/Device Helper/DeviceHelper.swift @@ -230,6 +230,13 @@ class DeviceHelper { #else let category = UIApplication.sharedApplication?.preferredContentSizeCategory ?? UIScreen.main.traitCollection.preferredContentSizeCategory + return Self.contentSizeCategoryToken(for: category) + #endif + } + + /// Maps a `UIContentSizeCategory` to the fixed dashboard token string used by + /// backend audience filters. These tokens are a backend contract and must not change. + static func contentSizeCategoryToken(for category: UIContentSizeCategory) -> String { switch category { case .extraSmall: return "xSmall" case .small: return "small" @@ -245,7 +252,6 @@ class DeviceHelper { case .accessibilityExtraExtraExtraLarge: return "accessibilityXXXLarge" default: return "unspecified" } - #endif } var platformWrapper: String? diff --git a/Tests/SuperwallKitTests/Network/DeviceHelperTests.swift b/Tests/SuperwallKitTests/Network/DeviceHelperTests.swift index c285e73c09..a0eb286c93 100644 --- a/Tests/SuperwallKitTests/Network/DeviceHelperTests.swift +++ b/Tests/SuperwallKitTests/Network/DeviceHelperTests.swift @@ -8,6 +8,7 @@ import Testing import Combine +import UIKit @testable import SuperwallKit struct DeviceHelperTests { @@ -58,4 +59,25 @@ struct DeviceHelperTests { let paddedVersion = DeviceHelper.makePaddedVersion(using: version) #expect(paddedVersion == "003") } + + // These tokens are a backend/audience-filter contract and must not change. + @Test func contentSizeCategoryToken_mapsEveryCategory() { + #expect(DeviceHelper.contentSizeCategoryToken(for: .extraSmall) == "xSmall") + #expect(DeviceHelper.contentSizeCategoryToken(for: .small) == "small") + #expect(DeviceHelper.contentSizeCategoryToken(for: .medium) == "medium") + #expect(DeviceHelper.contentSizeCategoryToken(for: .large) == "large") + #expect(DeviceHelper.contentSizeCategoryToken(for: .extraLarge) == "xLarge") + #expect(DeviceHelper.contentSizeCategoryToken(for: .extraExtraLarge) == "xxLarge") + #expect(DeviceHelper.contentSizeCategoryToken(for: .extraExtraExtraLarge) == "xxxLarge") + #expect(DeviceHelper.contentSizeCategoryToken(for: .accessibilityMedium) == "accessibilityMedium") + #expect(DeviceHelper.contentSizeCategoryToken(for: .accessibilityLarge) == "accessibilityLarge") + #expect(DeviceHelper.contentSizeCategoryToken(for: .accessibilityExtraLarge) == "accessibilityXLarge") + #expect(DeviceHelper.contentSizeCategoryToken(for: .accessibilityExtraExtraLarge) == "accessibilityXXLarge") + #expect(DeviceHelper.contentSizeCategoryToken(for: .accessibilityExtraExtraExtraLarge) == "accessibilityXXXLarge") + } + + @Test func contentSizeCategoryToken_unknownCategoryIsUnspecified() { + #expect(DeviceHelper.contentSizeCategoryToken(for: .unspecified) == "unspecified") + #expect(DeviceHelper.contentSizeCategoryToken(for: UIContentSizeCategory("someUnknownValue")) == "unspecified") + } } From 9480901202ef25aed8bd9543bfd24f48e6fba8ee Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Jul 2026 12:06:41 +0000 Subject: [PATCH 6/6] Fix DeviceHelperTests: use UIContentSizeCategory(rawValue:) initializer --- Tests/SuperwallKitTests/Network/DeviceHelperTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/SuperwallKitTests/Network/DeviceHelperTests.swift b/Tests/SuperwallKitTests/Network/DeviceHelperTests.swift index a0eb286c93..905cfc199f 100644 --- a/Tests/SuperwallKitTests/Network/DeviceHelperTests.swift +++ b/Tests/SuperwallKitTests/Network/DeviceHelperTests.swift @@ -78,6 +78,6 @@ struct DeviceHelperTests { @Test func contentSizeCategoryToken_unknownCategoryIsUnspecified() { #expect(DeviceHelper.contentSizeCategoryToken(for: .unspecified) == "unspecified") - #expect(DeviceHelper.contentSizeCategoryToken(for: UIContentSizeCategory("someUnknownValue")) == "unspecified") + #expect(DeviceHelper.contentSizeCategoryToken(for: UIContentSizeCategory(rawValue: "someUnknownValue")) == "unspecified") } }