Repository navigation
TrustedForm iOS Mobile SDK
The TrustedForm Certify Mobile SDK is designed to provide documentation of lead generation events that can be used to show evidence of prior express written consent given by consumers who desire to receive marketing communications. Following the instructions below will create a TrustedForm certificate that can be retained for your own records and/or distributed to lead buyers. The example code provided is a template to help you get started. It should be adapted to align with the specific requirements for your business and setup of your mobile application.
The iOS version supports both UIKit and SwiftUI frameworks, offering seamless integration for lead publishers to certify leads generated from their mobile applications.
After you have created certificates, use TrustedForm Retain to access and store it in your ActiveProspect account.
Add a package by selecting File → Add Packages… in Xcode's menu bar.
Search for the Trusted Form SDK using the repo's URL:
https://github.com/activeprospect/trustedform-ios-sdkNext, set the Dependency Rule to be Up to Next Major Version.
Then, select Add Package.
The TrustedForm SDK follows a specific flow to ensure proper lead certification:
- Configuration → 2. Certificate Creation → 3. Tracking Setup → 4. Form Interaction → 5. Screenshot Capture → 6. Cleanup
- When: Application startup
- Where: AppDelegate or App initializer
- Requirements: Valid app ID, access token, Application context
- Result: SDK is ready for use
- When: Before starting any form tracking
- Where: ViewController or SwiftUI View before displaying form
- Requirements: Valid configuration, network connectivity, valid access token
- Result: Certificate object for tracking
- When: After successful certificate creation
- Where: ViewController or SwiftUI View after certificate creation
- Requirements: Valid certificate, valid view hierarchy
- Result: Tracking is active and ready
- When: User interacts with form elements
- Where: Anywhere in the tracked view
- Requirements: Tracking must be active, elements must have proper roles
- Result: User interactions are tracked and recorded
- When: During form interactions and on demand
- Where: Throughout the tracked view
- Requirements: Tracking active, valid view hierarchy, sufficient storage
- Result: Screenshots captured and uploaded
- When: Form submission or view dismissal
-
Where:
viewWillDisappearoronDisappear - Requirements: Valid certificate reference
- Result: Resources cleaned up, tracking stopped
import SwiftUI
import TrustedForm
@main
struct SwiftUIDemoApp: App {
init() {
configureTrustedFormSDK()
}
var body: some Scene {
WindowGroup {
AppContent()
}
}
}
extension SwiftUIDemoApp {
private func configureTrustedFormSDK() {
// Configure the SDK
TrustedForm.default.configure(
appId: "YOUR_APP_ID",
accessToken: "YOUR_ACCESS_TOKEN"
)
// Optional: Enable verbose logging for development
#if DEBUG
TrustedForm.enableVerboseLogging()
#endif
}
}import UIKit
import TrustedForm
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Configure the SDK
TrustedForm.default.configure(
appId: "YOUR_APP_ID",
accessToken: "YOUR_ACCESS_TOKEN"
)
// Optional: Enable verbose logging for development
#if DEBUG
TrustedForm.enableVerboseLogging()
#endif
return true
}
}Required Parameters:
-
appId: Your unique application identifier -
accessToken: Your ActiveProspect API access token
Important Notes:
- Configuration must be called before any other TrustedForm operations
- The SDK will throw
TFError.unconfiguredif accessed before configuration - Configuration is typically done once in your AppDelegate or App initializer
- All parameters are required and must be valid for the SDK to function properly
import SwiftUI
import TrustedForm
struct AppContent: View {
@State private var certificate: Certificate? = nil
var body: some View {
Text("Hello World!")
.onAppear {
guard certificate == nil else { return }
Task {
await setupTracking()
}
}
}
private func setupTracking() async {
do {
// Create a certificate
let certificate = try await TrustedForm.default.createCertificate()
// Start tracking
TrustedForm.default.startTracking(for: certificate)
self.certificate = certificate
} catch {
print("Failed to create certificate: \(error.localizedDescription)")
}
}
private func cleanup() {
// Stop tracking when form is submitted or view disappears
if let certificate = certificate {
TrustedForm.default.stopTracking(for: certificate)
}
}
}import UIKit
import TrustedForm
class ViewController: UIViewController {
private var certificate: Certificate? = nil
override func viewDidLoad() {
super.viewDidLoad()
Task {
await setupTracking()
}
}
private func setupTracking() async {
do {
// Create a certificate
let certificate = try await TrustedForm.default.createCertificate()
// Start tracking
TrustedForm.default.startTracking(for: certificate)
self.certificate = certificate
} catch {
print("Failed to create certificate: \(error.localizedDescription)")
}
}
private func cleanup() {
// Stop tracking when form is submitted or view disappears
if let certificate = certificate {
TrustedForm.default.stopTracking(for: certificate)
}
}
}Prerequisites for Certificate Creation:
- TrustedForm must be properly configured
- Valid network connectivity
- Valid access token with proper permissions
- iOS application context must be available
What Happens During Certificate Creation:
- SDK obtains device information (OS version, screen dimensions, device type)
- SDK retrieves public IP address
- SDK generates unique certificate ID
- Certificate is created on TrustedForm servers
- Local certificate object is returned for tracking
Prerequisites for startTracking():
- Valid certificate object (from successful
createCertificate()) - Valid view hierarchy
- Network connectivity for initial setup
What Happens When Tracking Starts:
- Touch listeners are set up on the view
- Screenshot service is initialized
- Event controller is activated
- Initial screenshot is captured
- Form interaction tracking begins
Important Notes:
- Only one tracking session can be active at a time
- If tracking is already active, calling
startTracking()again will stop the previous session - Tracking must be stopped when the form is submitted or view is dismissed
- Screenshots are automatically captured during tracking
Use TFElementRole to assign specific roles to form elements for tracking consent information.
The SDK provides several element roles for different types of form elements:
-
consentTrackedText- For text input fields (name, email, phone, etc.) -
consentTrackedInput- For checkboxes, toggles, and other input controls -
consentLanguage- For consent text and terms -
consentLanguageInput- For consent checkboxes -
consentOptedAdvertiserInput- For advertiser selection checkboxes -
consentOptedAdvertiserName- For advertiser name labels -
consentTrackedField- For descriptive fields -
submit- For submit buttons
import SwiftUI
import TrustedForm
struct ConsentForm: View {
@State private var firstName = ""
@State private var lastName = ""
@State private var email = ""
@State private var agreeToTerms = false
@State private var advertiserConsent = false
@State private var certificate: Certificate?
let submissionId = UUID().uuidString
var body: some View {
Form {
// Text Field - First Name
TextField("First Name", text: $firstName)
.tfElementRole(
.consentTrackedText(
submissionId: submissionId,
label: "First Name"
),
binding: $firstName
)
// Text Field - Last Name
TextField("Last Name", text: $lastName)
.tfElementRole(
.consentTrackedText(
submissionId: submissionId,
label: "Last Name"
),
binding: $lastName
)
// Text Field - Email
TextField("Email", text: $email)
.tfElementRole(
.consentTrackedText(
submissionId: submissionId,
label: "Email"
),
binding: $email
)
// Consent Text
Text("I agree to the terms and conditions")
.tfElementRole(
.consentLanguage(
submissionId: submissionId
)
)
// Consent Checkbox
Toggle("I agree to terms", isOn: $agreeToTerms)
.tfElementRole(
.consentTrackedInput(
submissionId: submissionId,
label: "Terms Agreement"
),
binding: $agreeToTerms
)
// Advertiser Consent
Toggle(isOn: $advertiserConsent) {
Text("I want to receive email updates")
.tfElementRole(
.consentOptedAdvertiserName(
submissionId: submissionId,
index: 0
)
)
}
.tfElementRole(
.consentOptedAdvertiserInput(
submissionId: submissionId,
label: "I want to receive email updates",
index: 0
),
binding: $advertiserConsent
)
// Submit Button
Button("Submit") {
// Handle form submission
}
.tfElementRole(.submit(submissionId: submissionId))
}
.onAppear {
Task {
do {
certificate = try await TrustedForm.default.createCertificate()
if let certificate = certificate {
TrustedForm.default.startTracking(for: certificate)
}
} catch {
print("Failed to create certificate: \(error)")
}
}
}
.onDisappear {
if let certificate = certificate {
TrustedForm.default.stopTracking(for: certificate)
}
}
}
}import UIKit
import TrustedForm
class ConsentFormViewController: UIViewController {
private var certificate: Certificate?
private let submissionId = UUID().uuidString
private lazy var firstNameField: UITextField = {
let field = UITextField()
field.placeholder = "First Name"
field.borderStyle = .roundedRect
field.tfElementRole = .consentTrackedText(
submissionId: submissionId,
label: "First Name"
)
return field
}()
private lazy var lastNameField: UITextField = {
let field = UITextField()
field.placeholder = "Last Name"
field.borderStyle = .roundedRect
field.tfElementRole = .consentTrackedText(
submissionId: submissionId,
label: "Last Name"
)
return field
}()
private lazy var emailField: UITextField = {
let field = UITextField()
field.placeholder = "Email"
field.borderStyle = .roundedRect
field.tfElementRole = .consentTrackedText(
submissionId: submissionId,
label: "Email"
)
return field
}()
private lazy var consentTextLabel: UILabel = {
let label = UILabel()
label.text = "I agree to the terms and conditions"
label.tfElementRole = .consentLanguage(
submissionId: submissionId
)
return label
}()
private lazy var termsSwitch: UISwitch = {
let toggle = UISwitch()
toggle.tfElementRole = .consentTrackedInput(
submissionId: submissionId,
label: "Terms Agreement"
)
return toggle
}()
private lazy var advertiserDescriptionLabel: UILabel = {
let label = UILabel()
label.text = "I want to receive email updates"
label.tfElementRole = .consentOptedAdvertiserName(
submissionId: submissionId,
index: 0
)
return label
}()
private lazy var advertiserButton: UIButton = {
let button = UIButton()
button.tfElementRole = .consentOptedAdvertiserInput(
submissionId: submissionId,
label: "I want to receive email updates",
index: 0
)
return button
}()
private lazy var submitButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Submit", for: .normal)
button.tfElementRole = .submit(submissionId: submissionId)
button.addTarget(self, action: #selector(submitTapped), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
setupTracking()
}
private func setupUI() {
// Add and layout subviews
// (Layout constraints omitted for brevity)
}
private func setupTracking() {
Task {
do {
certificate = try await TrustedForm.default.createCertificate()
if let certificate = certificate {
TrustedForm.default.startTracking(for: certificate)
}
} catch {
print("Failed to create certificate: \(error)")
}
}
}
@objc private func submitTapped() {
// Handle form submission
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let certificate = certificate {
TrustedForm.default.stopTracking(for: certificate)
}
}
}Mark fields containing sensitive information to ensure they are masked in screenshots:
TextField("SSN", text: $ssn)
.tfElementRole(
.consentTrackedText(
submissionId: submissionId,
label: "SSN"
),
binding: $ssn
)
.sensitive()let ssnField = UITextField()
ssnField.placeholder = "SSN"
ssnField.tfElementRole = .consentTrackedText(
submissionId: submissionId,
label: "SSN"
)
ssnField.isSensitive = trueExample usage:
// Credit Card Field
TextField("Credit Card", text: $creditCard)
.tfElementRole(
.consentTrackedText(
submissionId: submissionId,
label: "credit-card"
),
binding: $creditCard
)
.sensitive()The SDK automatically captures screenshots during form interactions, but you can also manually trigger screenshot capture:
// Manual screenshot capture
if let certificate = certificate {
try? TrustedForm.default.addScreenshot(to: certificate)
}Prerequisites for Screenshot Capture:
- Tracking must be active (
startTracking()called) - Valid view hierarchy
- View must be in a visible state
- Sufficient storage space for screenshot files
When screenshots are automatically captured:
- When tracking starts (
startTracking()) - When tracking stops (
stopTracking()) - When manually triggered (
addScreenshot(to:)) - When submit buttons are tapped (buttons with
TFElementRole.submit)
Screenshot Process:
- Capture: Screenshot is taken of the current view
-
Masking: Sensitive fields (marked with
.sensitive()orisSensitive = true) are automatically masked - Storage: Screenshot is saved locally with timestamp
- Upload: Screenshot is uploaded to TrustedForm servers asynchronously
- Cleanup: Local files are cleaned up after successful upload
Screenshot Features:
- Sensitive fields are automatically masked with configurable color
- Screenshots are stored locally and uploaded to TrustedForm servers
- Multiple screenshots can be captured per certificate
- Screenshots include timestamps and form state
- Automatic retry mechanism for failed uploads
- Local storage cleanup after successful upload
Manual Screenshot Capture:
// Capture screenshot on specific user action
func onFormFieldChanged() {
if let certificate = certificate {
try? TrustedForm.default.addScreenshot(to: certificate)
}
}
// Capture screenshot before form submission
func onSubmitForm() {
if let certificate = certificate {
try? TrustedForm.default.addScreenshot(to: certificate)
}
// Proceed with form submission
}
// Capture screenshot on important form milestones
func onFormStepComplete() {
if let certificate = certificate {
try? TrustedForm.default.addScreenshot(to: certificate)
}
}Screenshot Troubleshooting:
- Ensure tracking is active before capturing screenshots
- Check that the view has a valid hierarchy
- Verify storage permissions if screenshots fail to save
- Monitor network connectivity for upload failures
Here's a more comprehensive example showing multiple element types:
import SwiftUI
import TrustedForm
struct AdvancedConsentForm: View {
@State private var firstName = ""
@State private var lastName = ""
@State private var email = ""
@State private var phone = ""
@State private var ssn = ""
@State private var isMilitary = false
@State private var advertiserConsents = [false, false, false]
@State private var agreeToTerms = false
@State private var certificate: Certificate?
let submissionId = UUID().uuidString
var body: some View {
Form {
// Name Fields
TextField("First Name", text: $firstName)
.tfElementRole(
.consentTrackedText(
submissionId: submissionId,
label: "First Name"
),
binding: $firstName
)
TextField("Last Name", text: $lastName)
.tfElementRole(
.consentTrackedText(
submissionId: submissionId,
label: "Last Name"
),
binding: $lastName
)
// Contact Fields
TextField("Email", text: $email)
.tfElementRole(
.consentTrackedText(
submissionId: submissionId,
label: "Email"
),
binding: $email
)
TextField("Phone", text: $phone)
.tfElementRole(
.consentTrackedText(
submissionId: submissionId,
label: "Phone"
),
binding: $phone
)
// Sensitive Field
TextField("SSN", text: $ssn)
.tfElementRole(
.consentTrackedText(
submissionId: submissionId,
label: "SSN"
),
binding: $ssn
)
.sensitive()
// Military Status Toggle
Toggle("Are you or your spouse a military veteran?", isOn: $isMilitary)
.tfElementRole(
.consentTrackedInput(
submissionId: submissionId,
label: "Military Status"
),
binding: $isMilitary
)
// Consent Language
Text("By submitting this form, you agree to our terms and conditions and consent to receive marketing communications.")
.tfElementRole(
.consentLanguage(
submissionId: submissionId
)
)
// Advertiser Consent Checkboxes
Text("Select advertisers you'd like to hear from:")
.font(.caption)
let advertisers = ["Advertiser A", "Advertiser B", "Advertiser C"]
ForEach(0..<advertisers.count, id: \.self) { index in
Toggle(isOn: $advertiserConsents[index]) {
Text(advertisers[index])
.tfElementRole(
.consentOptedAdvertiserName(
submissionId: submissionId,
index: index
)
)
}
.tfElementRole(
.consentOptedAdvertiserInput(
submissionId: submissionId,
label: advertisers[index],
index: index
),
binding: $advertiserConsents[index]
)
}
// Submit Button
Button("Submit Application") {
// Handle form submission
}
.tfElementRole(.submit(submissionId: submissionId))
}
.onAppear {
Task {
do {
certificate = try await TrustedForm.default.createCertificate()
if let certificate = certificate {
TrustedForm.default.startTracking(for: certificate)
}
} catch {
print("Failed to create certificate: \(error)")
}
}
}
.onDisappear {
if let certificate = certificate {
TrustedForm.default.stopTracking(for: certificate)
}
}
}
}Here's a complete example showing how to integrate TrustedForm in a ViewController with proper lifecycle management:
import UIKit
import TrustedForm
class TrustedFormViewController: UIViewController {
private var certificate: Certificate?
private let submissionId = UUID().uuidString
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
setupTracking()
}
private func setupUI() {
// Setup your UI elements here
// Add form fields, buttons, etc.
}
private func setupTracking() {
Task {
do {
certificate = try await TrustedForm.default.createCertificate()
if let certificate = certificate {
TrustedForm.default.startTracking(for: certificate)
}
} catch {
print("Failed to create certificate: \(error)")
}
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let certificate = certificate {
TrustedForm.default.stopTracking(for: certificate)
}
}
}Key Points for ViewController Implementation:
- Use
viewDidLoadfor certificate creation and tracking setup -
Single Form: Stop tracking in
viewWillDisappearwhen form is complete - Multi-Page Form: Keep tracking across pages, only stop when form is submitted
- Always call
stopTrackingwhen the view disappears - Only one tracking session can be active at a time
- Always start tracking before displaying forms - Ensure tracking is active before users interact with form elements
- Stop tracking when forms are submitted or dismissed - Clean up resources to prevent memory leaks
-
Use appropriate element roles - Choose the correct
TFElementRoletype for each form component -
Mark sensitive fields appropriately - Use
.sensitive()modifier orisSensitive = truefor fields containing PII - Maintain unique submission IDs - Use different submission IDs for different forms or form steps
- Handle errors gracefully - Always handle certificate creation errors
-
Clean up resources - Stop tracking in
viewWillDisappearoronDisappear - Test thoroughly - Verify that all form interactions are properly tracked
- Use meaningful labels - Provide descriptive labels for better certificate documentation
- Consider form state - Track form state changes and user interactions comprehensively
-
Certificate creation fails
- Verify your access token is valid
- Check network connectivity
- Ensure all required configuration parameters are provided
-
Tracking not working
- Ensure
startTracking()is called after certificate creation - Verify form elements have proper
TFElementRolemodifiers - Check that the view hierarchy is valid
- Ensure
-
Screenshots not captured
- Ensure tracking is active when screenshots are needed
- Check that the view has a valid hierarchy
- Verify storage permissions if needed
- Ensure view is in visible state
- Check network connectivity for upload
-
Sensitive fields not masked
- Ensure
.sensitive()modifier orisSensitive = trueis applied to sensitive fields - Verify the field is properly registered with TrustedForm
- Check that tracking is active when field is marked sensitive
- Ensure
-
Certificate creation fails
- Verify access token is valid and has proper permissions
- Check network connectivity
- Ensure all configuration parameters are correct
- Verify application context is available
-
Tracking stops unexpectedly
- Check for view lifecycle issues
- Verify certificate is still valid
- Ensure no other tracking session is interfering
- Check for memory pressure or system resource issues
TrustedForm.default.configure(
appId: String, // Your application ID
accessToken: String // Your access token
)// Create a certificate
let certificate = try await TrustedForm.default.createCertificate()
// Start tracking
TrustedForm.default.startTracking(for: certificate)
// Stop tracking
TrustedForm.default.stopTracking(for: certificate)
// Add screenshot
try TrustedForm.default.addScreenshot(to: certificate)// Text input fields
TFElementRole.consentTrackedText(submissionId: String, label: String)
// Checkboxes and toggles
TFElementRole.consentTrackedInput(submissionId: String, label: String, index: Int? = nil)
// Consent language
TFElementRole.consentLanguage(submissionId: String, index: Int? = nil)
// Submit buttons
TFElementRole.submit(submissionId: String, label: String, index: Int? = nil)
// Advertiser consent
TFElementRole.consentOptedAdvertiserInput(submissionId: String, label: String, index: Int? = nil)
TFElementRole.consentOptedAdvertiserName(submissionId: String, index: Int? = nil)// Text field tracking
.tfElementRole(_ role: TFElementRole, binding: Binding<String>)
// Toggle tracking
.tfElementRole(_ role: TFElementRole, binding: Binding<Bool>)
// Button tracking
.tfElementRole(_ role: TFElementRole)
// Sensitive field masking
.sensitive()// Element role assignment
view.tfElementRole = .consentTrackedText(submissionId: String, label: String)
// Sensitive field marking
view.isSensitive = true