HomeDocumentationAPI Reference
Getting StartedAPI ReferenceDashboardHelp Center
Documentation

Guide for AI Coding Agents to Integrate Luciq on iOS

⚠️ UNIVERSAL EXECUTION RULES ⚠️

Critical Rules - Apply to ALL Platforms:

  • NEVER skip WAIT instructions - always get user confirmation before proceeding
  • NEVER auto-execute optional steps - user must explicitly select them
  • ALWAYS fetch latest SDK version before integration (DO NOT use placeholders like 1.0.0)
  • ALL API parameters must be included (can be nil/null) - never omit parameters

Execution Guidelines:

  • Be specific and concise - save tokens by being to the point
  • Don't create documentation files - only code files required for integration
  • Implement mandatory steps sequentially - complete each before moving to the next
  • Prompt user with current step - describe what's happening, then proceed when done
  • Only ask for optional steps after mandatory ones - show numbered list for selection
  • Use numbered lists for all options - makes selection easier (e.g., "Select 1", "Select 2")
  • Add wrap up step - execute validation only when user selects "Wrap up & validate"
  • Check documentation first - confirm API availability before getting creative beyond examples

Official Documentation:

  • Main docs: https://docs.luciq.ai/
  • Always check official docs when unsure about API signatures or platform specifics

Integration Workflow Overview

┌─────────────────────────────────────────────────────────────┐
│                    MANDATORY STEPS                          │
├─────────────────────────────────────────────────────────────┤
│ Step 1: Collect Required Information                       │
│   ├─ 1A: Get App Token (MCP or Manual)                    │
│   └─ 1B: Determine Integration Method                     │
│                                                             │
│ Step 2: Add SDK Dependency (Platform-Specific)            │
│   └─ Fetch latest version first!                          │
│                                                             │
│ Step 3: Initialize the SDK (Platform-Specific)            │
│   └─ Configure invocation events                          │
│                                                             │
│ 🛑 STOP - Mandatory steps complete                        │
├─────────────────────────────────────────────────────────────┤
│                    OPTIONAL STEPS                           │
├─────────────────────────────────────────────────────────────┤
│ User selects from menu (WAIT for selection):              │
│   1. Configure Network Logging                            │
│   2. Mask Repro Step Screenshots                          │
│   3. Add User Identification                              │
│   4. Wrap up & validate                                   │
├─────────────────────────────────────────────────────────────┤
│                    VERIFICATION                             │
├─────────────────────────────────────────────────────────────┤
│ Step 4: Build → Test → Verify Dashboard                   │
└─────────────────────────────────────────────────────────────┘

Step 1 — Collect Required Information [MANDATORY]

1A: Get App Token

Check MCP Server First:

IF Luciq MCP server is installed:
  1. Fetch all tokens with app names using MCP tools
  2. Display tokens to user in numbered list with app names
  3. Ask: "Which app token would you like to use?"
  4. WAIT for selection
  5. Store selected token
ELSE:
  1. Display to user: "To integrate Luciq SDK, you'll need your App Token."
  2. Display to user:
     "📍 Find your token: Luciq Dashboard → Settings → SDK Integration"
  3. Display on new line with indentation:
     "   Dashboard URL: https://dashboard.luciq.ai"
  4. Ask: "Please provide your Luciq App Token:"
  5. WAIT for user to provide token
  6. Validate token format (non-empty, alphanumeric)
  7. Store token for later use
END IF

Validation:

  • Token should be non-empty
  • Typically 32-40 character hexadecimal string
  • If invalid format, warn user but proceed

1B: Determine Integration Method

Auto-Detection Logic:

Platform-specific detection should look for:

  • iOS: Podfile, Cartfile, SPM packages in .xcodeproj, or none → Manual
  • Android: build.gradle with repositories, pom.xml, or none → Manual
DETECT package managers in project:
  - Count how many are found

IF exactly ONE package manager detected:
  1. Inform user: "Detected [PackageManager], will use this for integration"
  2. Store integration_method
  3. Proceed to Step 2
ELSE IF multiple or zero detected:
  1. Display platform-specific menu (see platform guides)
  2. WAIT for user selection
  3. Store integration_method
  4. Proceed to Step 2
END IF

1C: Choose Configuration Mode

Ask User:

"How would you like to configure the SDK?"

Options:
  1. Default configuration (fastest setup)
  2. Custom configuration (full control)

WAIT for selection

Store selection as config_mode (default/custom)

If config_mode == default:

Inform user: "Using default configuration:
  • Invocation: shake
  • Network logging: enabled
  • Network masking: common sensitive fields
  • Auto masking: all types (text inputs, labels, media)
  • APM (Android): enabled
  • Repro steps: default SDK settings
  • User identification: skip (can add later)"

Store the following:
  - invocation_events = [shake]
  - network_logging_enabled = true
  - network_masking_enabled = true
  - predefined_headers_to_mask = ["Authorization", "Cookie", "X-API-Key", "token"]
  - predefined_body_fields_to_mask = ["password", "token", "ssn"]
  - auto_masking_types = [textInputs, labels, media] (all three types)
  - repro_steps_config = skip (use defaults)
  - user_identification = skip

FOR ANDROID ONLY:
  - apm_enabled = true
  - Continue to Step 1D (Compose detection)

FOR iOS:
  - Proceed to Step 2

FOR CUSTOM MODE:
  - Continue with platform-specific interactive steps

If config_mode == custom:

  • Continue with existing flow (Step 1C/1D for Android, Step 2 for iOS)

Step 2 — Add SDK Dependency [MANDATORY - Platform-Specific]

Pre-Dependency Critical Step:

⚠️ MUST FETCH LATEST VERSION FIRST:

1. Fetch latest version from platform-specific GitHub releases:
   - iOS: https://github.com/luciqai/luciq-ios-sdk/releases/latest
   - Android: https://github.com/luciqai/luciq-android-sdk/releases/latest

2. Extract version number (e.g., "19.1.0")

3. Store version for dependency configuration

4. DO NOT use placeholder versions like "1.0.0" or "latest"

Then proceed to platform-specific dependency installation (see platform guides)


Step 3 — Initialize the SDK [MANDATORY - Platform-Specific]

Invocation Events Configuration

Check Configuration Mode:

IF config_mode == default:
  Use predefined invocation events: [shake]
  Skip to platform-specific initialization
  ELSE IF config_mode == custom:
  Continue with interactive prompts below
END IF

For Custom Mode - Ask User:

"Would you like to initialize the SDK with default invocation events (shake and screenshot)?"

Options: yes / no

WAIT for response

If YES:

  • Use default: [shake, screenshot]
  • See platform guide for syntax

If NO:

"Which invocation events (1 or more) should trigger Luciq?"

Options (select all that apply):
  - shake: Device shake gesture
  - screenshot: Screenshot capture
  - floatingButton: Persistent floating button overlay
  - none: Manual invocation only (via code)

WAIT for selection

Invocation Event Mapping:

  • none → Empty array []
  • Single selection → [selected]
  • Multiple → [event1, event2, ...]

Apply configuration in platform-specific syntax (see platform guides)


🛑 MANDATORY STEPS COMPLETE - STOP HERE


Optional Steps Menu

Check Configuration Mode First:

IF config_mode == default:
  Auto-apply the following configurations:
    1. Configure Network Logging → Apply predefined masking + add interceptor (Android: manual OkHttp required)
    2. Mask Repro Step Screenshots → Apply all types masking
    3. Configure Repro Steps Mode → Skip (use SDK defaults)
    4. Add User Identification → Skip

  After auto-configuration, display:
    "Default configuration applied!

    Optional: Would you like to customize any settings?
      1. Modify network logging
      2. Modify screenshot masking
      3. Configure repro steps mode
      4. Add user identification
      5. Wrap up & validate (build and test)

    Enter number or 'skip' to finish"

  WAIT for selection

ELSE IF config_mode == custom:
  Display standard menu:
    "Luciq SDK core integration complete!

    Optional configurations:
      1. Configure Network Logging (intercept & mask sensitive data)
      2. Mask Repro Step Screenshots (auto-blur sensitive UI elements)
      3. Configure Repro Steps Mode (control screenshot inclusion per product)
      4. Add User Identification (link reports to users)
      5. Wrap up & validate (build and test)

    Which would you like to configure? (Enter number or 'skip' to finish)"

  WAIT for selection
END IF

DO NOT proceed with ANY optional step until user selects it

Optional Step 1 — Configure Network Logging

Concepts (Platform-Agnostic):

Purpose: Automatically capture network requests/responses and optionally mask sensitive data

Part A: Network Capture

Display options:
  1. Keep automatic network capture enabled (default)
  2. Disable automatic network capture

WAIT for selection

IF option 2:
  - Apply platform-specific disable code (see platform guide)
  - END
ELSE IF option 1:
  - Continue with default (enabled)
END IF

Part B: Mask Sensitive Data

Display: "Specify which fields to mask:
          Options:
            1. Enter comma-separated field names
            2. Skip (no masking)

          For option 1, specify:
            - Headers (e.g., Authorization, Cookie, X-API-Key)
            - Body fields (e.g., password, token, ssn)"

WAIT for input

IF option 1 selected and input provided:
  Parse input:
    - Split by comma
    - Trim whitespace
    - Create arrays: headersToMask[], bodyFieldsToMask[]

  Apply platform-specific masking code (see platform guide):
    - Use loops for headers
    - Use recursion for nested body fields
    - Handle arrays and nested objects
ELSE IF option 2:
  - Leave network logs unmasked
END IF

Implementation Requirements:

  • Masking code MUST be placed AFTER SDK initialization
  • Headers: Simple loop through header names
  • Body: Recursive function to handle nested JSON at any depth
  • Performance: Use Set/HashSet for O(1) field lookup

Optional Step 2 — Mask Repro Step Screenshots

Concepts (Platform-Agnostic):

Purpose: Automatically blur/mask sensitive UI elements in screenshot repro steps

Display options - Which types of elements should be masked?
  1. Text inputs only
  2. Labels only
  3. Images/media only
  4. Text inputs + labels
  5. All (text inputs + labels + media)
  6. Custom selection
  7. Skip (no screenshot masking)

WAIT for selection

IF option 7:
  - Skip screenshot masking
  - END
ELSE:
  Map selection to mask types:
    1 → [textInputs]
    2 → [labels]
    3 → [images]
    4 → [textInputs, labels]
    5 → [textInputs, labels, images]
    6 → Ask for custom combination, then apply

  Apply platform-specific masking API (see platform guide)
END IF

Available Mask Types:

  • iOS:
    • .textInputs: Text input fields, password fields
    • .labels: Text labels, buttons with text
    • .media: Comprehensive masking (includes all types)
  • Android:
    • MaskingType.TEXT_INPUTS: Text input fields, password fields
    • MaskingType.LABELS: Text labels, buttons with text
    • MaskingType.MEDIA: Images, media content

Optional Step 3 — Configure Repro Steps Mode

Concepts (Platform-Agnostic):

Purpose: Control which products include screenshots in their repro steps

Default Behavior:

  • Bug Reporting: Screenshots enabled
  • Crash Reporting: Screenshots disabled
  • Session Replay: Screenshots enabled
Display: "Select products to ENABLE screenshots for:"

Options (multiple selection):
  1. Bug Reporting
  2. Crash Reporting
  3. Session Replay
  4. All products
  5. None (disable screenshots for all)
  6. Keep defaults (Bug Reporting + Session Replay with screenshots, Crash without)

Enter selections (e.g., "1,3" or "4"):

WAIT for input

IF option 6:
  - Keep default configuration
  - END
ELSE IF option 5 or "none" selected:
  - Disable screenshots for all products
  - Apply platform-specific configuration (see platform guide)
  - END
ELSE:
  Parse selections:
    - Selected products → Enable with screenshots
    - Non-selected products → Enable WITHOUT screenshots

  Apply platform-specific configuration (see platform guide)
END IF

Issue Type Mapping:

  • Bug Reporting → .bug / IssueType.Bug
  • Crash Reporting → .crash / IssueType.Crash
  • Session Replay → .sessionReplay / IssueType.SessionReplay
  • All products → .all / IssueType.All

Optional Step 4 — Add User Identification

Concepts (Platform-Agnostic):

Purpose: Link bug reports to specific user accounts for better tracking

Display options - How would you like to identify users?
  1. Using email
  2. Using user ID
  3. Using both email and ID
  4. Skip (no user identification)

WAIT for selection

IF option 4:
  - Skip user identification
  - END
ELSE:
  Store user_id_method
  Proceed to Step 3A and 3B
END IF

Step 3A: Identify Login Flows

Search Strategy:

  1. Search for authentication/login methods in codebase
  2. Look for successful login callbacks/handlers
  3. Identify where user data becomes available

Placement Rules:

  • Add identification AFTER authentication succeeds
  • Add BEFORE any navigation/routing
  • Ensure user data (email/id/name) is available at this point

API Signature (All Platforms):

identifyUser(
  id: String/null,      // User ID - required param, can be null
  email: String/null,   // Email - required param, can be null
  name: String/null     // Name - required param, can be null
)

Examples by Selection:

  • Option 1 (email): identifyUser(null, user.email, user.name)
  • Option 2 (ID): identifyUser(user.id, null, user.name)
  • Option 3 (both): identifyUser(user.id, user.email, user.name)

Platform-Specific Syntax: See platform guides


Step 3B: Identify Logout Flows

Search Strategy:

  1. Search for logout/signout methods in codebase
  2. Look for session clearing logic
  3. Identify where user data is removed

Placement Rules:

  • Add logout call BEFORE clearing user session/data
  • Ensure it's called on all logout paths

API Signature (All Platforms):

logOut() / logout()  // No parameters

Platform-Specific Syntax: See platform guides


Step 4 — Verification & Testing (Wrap up & validate) [USER-INITIATED ONLY]

⚠️ CRITICAL: This step is ONLY executed when the user explicitly selects "Wrap up & validate" from the optional steps menu. Do NOT run automatically after mandatory steps complete.

After Mandatory Steps Complete

Display this summary and menu:

✅ Luciq SDK integration complete!

Platform: <iOS/Android>
SDK Version: <version>
Integration Method: <SPM/Gradle/etc>

Configuration:
  • App Token: <first 8 chars>...
  • Invocation Events: <configured events>
  • Network Logging: <enabled/disabled>
  • Network Masking:
      - Headers: <list or none>
      - Body Fields: <list or none>
  • Screenshot Masking: <types or none>
  • User Identification: <configured or not configured>

Next Steps - Select an option:
1. Configure optional features
2. Wrap up & validate

WAIT for user selection. Do NOT proceed to build verification unless user selects option 2.


1. Build Verification [ONLY WHEN USER REQUESTS]

Execute platform-specific build command:

  • iOS: xcodebuild -project ... -scheme ... build
  • Android: ./gradlew assembleDebug

Expected Result: BUILD SUCCEEDED with no errors

Common Build Errors & Solutions:

ErrorLikely CauseSolution
"no such module" / "unresolved import"Wrong import statementCheck platform guide for correct import
"version not found"Invalid SDK versionVerify version fetched from releases
"package not resolved"Dependency not addedRe-run package manager sync

2. Runtime Testing [MANUAL]

After successful build, instruct user to perform these steps:

1. Build and run the app on simulator/emulator or device

2. Trigger Luciq using configured invocation method:
   - IF shake enabled: Shake the device/simulator
   - IF screenshot enabled: Take a screenshot
   - IF floatingButton enabled: Tap the floating button

3. Verify Luciq UI appears

4. Fill out and submit a test bug report

5. Check Luciq dashboard at https://dashboard.luciq.ai
   - Verify report appears
   - Verify user identification (if configured)
   - Verify network logs (if configured)

3. Final Summary [DISPLAY AFTER BUILD SUCCESS]

Generate and display summary after successful validation:

✅ Luciq SDK successfully integrated and validated!

Platform: <iOS/Android>
SDK Version: <version>
Integration Method: <SPM/Gradle/etc>

Configuration:
  • App Token: <first 8 chars>...
  • Invocation Events: <configured events>
  • Network Logging: <enabled/disabled>
  • Network Masking:
      - Headers: <list or none>
      - Body Fields: <list or none>
  • Screenshot Masking: <types or none>
  • User Identification: <configured or not configured>

Build Status: ✅ SUCCEEDED

Next Steps:
  • ✅ Build completed successfully
  • Run the app and test SDK invocation
  • Submit a test report
  • Verify report in Luciq dashboard

Integration Complete - Ready for Testing!

Error Handling & Troubleshooting

Common Issues Across Platforms:

1. SDK Not Initializing:

  • Verify token is correct
  • Check initialization code placement (usually in app entry point)
  • Ensure import statement is correct

2. Network Logging Not Working:

  • Verify masking code placed AFTER SDK initialization
  • Check that network interceptor is properly configured
  • Ensure app has network permissions

3. User Identification Not Showing:

  • Verify identifyUser called after successful login
  • Check that logout is called on all logout paths
  • Ensure parameters are not all null

4. Reports Not Appearing in Dashboard:

  • Verify device/simulator has internet connection
  • Check app token is correct
  • Wait a few minutes for sync
  • Check dashboard filters

Extension Points for Platform Guides

Platform-specific guides should include:

Required Sections:

  1. Platform-Specific Critical Rules (import naming, package names, etc.)
  2. Pre-Integration Checklist (platform-specific gotchas)
  3. Step 2 Implementation (dependency management code)
  4. Step 3 Implementation (SDK initialization code)
  5. Optional Step 1 Implementation (network logging code)
  6. Optional Step 2 Implementation (screenshot masking code)
  7. Optional Step 3 Implementation (user identification code)
  8. Step 4 Implementation (build commands, platform-specific testing)

Reference Format:

## Step X — [Title]

> 📋 See [common-workflow.md#step-x](common-workflow.md#step-x) for detailed workflow

### Platform-Specific Implementation

[Platform-specific code and instructions]

Luciq SDK Integration - iOS Guide

Purpose: iOS-specific implementation details for Luciq SDK integration. This guide provides iOS-specific code, configuration, and platform requirements. It references the common workflow for high-level steps - when concatenated with common-workflow.md, all references will be in the same document.


⚠️ iOS-SPECIFIC CRITICAL RULES ⚠️

Package vs Import Naming (MUST FOLLOW):

  1. SPM Package Product Name: Use "Luciq" (in project.pbxproj, Podfile, etc.)
  2. Swift Import Statement: ALWAYS use import LuciqSDK (NOT import Luciq)
  3. Why Different?: The binary XCFramework is named "LuciqSDK.framework" internally
  4. If Build Fails with "no such module 'Luciq'": You forgot to use import LuciqSDK

iOS-Specific Execution Rules:

  • Network logging code goes AFTER Luciq.start()
  • ALL API parameters must be included (can be nil) - never omit parameters
  • Use exact for SPM version requirement (not upToNextMajorVersion)
  • Place SDK initialization in AppDelegate or main App struct's init()

Official iOS Documentation:



IOS-SPECIFIC IMPLEMENTATION DETAILS


Pre-Integration Checklist

Before starting, verify you understand these iOS-specific points:

  • Package product is "Luciq" but import is import LuciqSDK
  • Network logging code goes AFTER Luciq.start()
  • Use exact for SPM (not range)
  • Build errors about "no such module" mean wrong import
  • ALL parameters in API calls are required (can be nil)

Step 1 — Collect Required Information [MANDATORY]

📋

iOS-Specific Implementation

Package Manager Detection:

Check for:

  • Podfile → CocoaPods
  • Cartfile → Carthage
  • .xcodeproj with SPM packages → SPM
  • None → Manual

Integration Method Menu (if multiple/none detected):

  1. Swift Package Manager (SPM) [Recommended]
  2. CocoaPods
  3. Carthage
  4. Manual (XCFramework)

1C: Choose Configuration Mode

📋

For iOS:

  • If config_mode == default: Proceed to Step 2 with predefined configurations
  • If config_mode == custom: Continue with interactive prompts in subsequent steps

Step 2 — Add the Luciq SDK Dependency [MANDATORY]

📋

Fetch latest version from: https://github.com/luciqai/luciq-ios-sdk/releases/latest

iOS Implementation

If integration_method == spm:

// Add to Xcode:
// File → Add Package Dependencies...
// Enter URL: https://github.com/luciqai/luciq-ios-sdk

// In project.pbxproj, set:
repositoryURL = "https://github.com/luciqai/luciq-ios-sdk"
requirement = {
    kind = exact;
    version = <FETCHED_VERSION>; // e.g., 19.1.0
}

// Add package product "Luciq" to target

If integration_method == cocoapods:

# In Podfile:
pod 'Luciq', '~> <MAJOR_VERSION>'  # e.g., '~> 19.0'

# Then run:
pod install

If integration_method == carthage:

# In Cartfile:
binary "https://raw.githubusercontent.com/luciqai/luciq-ios-sdk/main/Luciq.json"

# Then run:
carthage update --platform iOS

If integration_method == manual:

1. Download: https://github.com/luciqai/luciq-ios-sdk/releases/latest/download/Luciq-XCFramework.zip
2. Unzip to reveal Luciq.xcframework
3. Drag into Xcode project → Frameworks, Libraries, and Embedded Content
4. Select "Embed & Sign"

Step 3 — Initialize the SDK [MANDATORY]

📋

iOS Implementation

⚠️ CRITICAL: ALWAYS use import LuciqSDK (not import Luciq)

Where to place:

  • SwiftUI: App struct's init()
  • UIKit: AppDelegate's application(_:didFinishLaunchingWithOptions:)

Option 1: SwiftUI App

import SwiftUI
import LuciqSDK  // ← CRITICAL: Import is LuciqSDK, not Luciq

@main
struct YourApp: App {
    init() {
        // Initialize Luciq with configured events
        Luciq.start(withToken: "<APP_TOKEN>", invocationEvents: <EVENTS>)
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Option 2: UIKit App (AppDelegate)

import UIKit
import LuciqSDK  // ← CRITICAL: Import is LuciqSDK, not Luciq

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Initialize Luciq with configured events
        Luciq.start(withToken: "<APP_TOKEN>", invocationEvents: <EVENTS>)

        return true
    }

    // ... rest of AppDelegate methods ...
}

Invocation Events Syntax:

Check config_mode and apply accordingly:

IF config_mode == default:
  // Default mode: shake only
  Luciq.start(withToken: "<APP_TOKEN>", invocationEvents: [.shake])

ELSE IF config_mode == custom:
  // Custom default: shake + screenshot
  Luciq.start(withToken: "<APP_TOKEN>", invocationEvents: [.shake, .screenshot])

  // Floating button only
  Luciq.start(withToken: "<APP_TOKEN>", invocationEvents: [.floatingButton])

  // Multiple events
  Luciq.start(withToken: "<APP_TOKEN>", invocationEvents: [.shake, .screenshot, .floatingButton])

  // None (manual only)
  Luciq.start(withToken: "<APP_TOKEN>", invocationEvents: [])
END IF

Note: The class name is Luciq but the import is LuciqSDK


Optional Step 1 — Configure Network Logging

📋

iOS Implementation

Check Configuration Mode:

IF config_mode == default:
  Network logging is enabled by default
  Apply predefined masking with:
    - Headers: ["Authorization", "Cookie", "X-API-Key", "token"]
    - Body fields: ["password", "token", "ssn"]
ELSE IF config_mode == custom:
  Ask user for network logging preferences
END IF

Disable Automatic Capture (call BEFORE Luciq.start()) - Custom Mode Only:

NetworkLogger.disableAutomaticCapturingOfNetworkLogs()

Mask Sensitive Data (call AFTER Luciq.start()):

Option 1: SwiftUI App

import SwiftUI
import LuciqSDK

@main
struct YourApp: App {
    init() {
        // Step 1: Initialize SDK first
        Luciq.start(withToken: "<APP_TOKEN>", invocationEvents: [.floatingButton])

        // Step 2: Configure network masking AFTER start
        configureNetworkMasking()
    }

    private func configureNetworkMasking() {
        // Define fields based on config_mode
        let headersToMask: [String] = IF config_mode == default:
            ["Authorization", "Cookie", "X-API-Key", "token"]  // Predefined for default mode
        ELSE:
            ["Authorization", "Cookie", "X-API-Key"]  // Custom user input

        let bodyFieldsToMask: Set<String> = IF config_mode == default:
            ["password", "token", "ssn"]  // Predefined for default mode
        ELSE:
            ["password", "token", "ssn", "uuid"]  // Custom user input

        NetworkLogger.setRequestObfuscationHandler { request in
            var mutableRequest = (request as NSURLRequest).mutableCopy() as! NSMutableURLRequest

            // Mask headers using loop
            for header in headersToMask {
                if mutableRequest.value(forHTTPHeaderField: header) != nil {
                    mutableRequest.setValue("*****", forHTTPHeaderField: header)
                }
            }

            // Mask body fields using recursive function
            if let body = mutableRequest.httpBody,
               let jsonObject = try? JSONSerialization.jsonObject(with: body) {
                let maskedObject = Self.maskFields(in: jsonObject, fieldsToMask: bodyFieldsToMask)
                if let newBody = try? JSONSerialization.data(withJSONObject: maskedObject) {
                    mutableRequest.httpBody = newBody
                }
            }

            return mutableRequest.copy() as! URLRequest
        }
    }

    // Recursive helper to mask fields at any depth
    private static func maskFields(in object: Any, fieldsToMask: Set<String>) -> Any {
        if var dictionary = object as? [String: Any] {
            for (key, value) in dictionary {
                if fieldsToMask.contains(key) {
                    dictionary[key] = "*****"
                } else if value is [String: Any] || value is [Any] {
                    dictionary[key] = maskFields(in: value, fieldsToMask: fieldsToMask)
                }
            }
            return dictionary
        } else if let array = object as? [Any] {
            return array.map { maskFields(in: $0, fieldsToMask: fieldsToMask) }
        }
        return object
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Option 2: UIKit App (AppDelegate)

import UIKit
import LuciqSDK

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Step 1: Initialize SDK first
        Luciq.start(withToken: "<APP_TOKEN>", invocationEvents: [.floatingButton])

        // Step 2: Configure network masking AFTER start
        configureNetworkMasking()

        return true
    }

    private func configureNetworkMasking() {
        // Define fields based on config_mode
        let headersToMask: [String] = IF config_mode == default:
            ["Authorization", "Cookie", "X-API-Key", "token"]  // Predefined for default mode
        ELSE:
            ["Authorization", "Cookie", "X-API-Key"]  // Custom user input

        let bodyFieldsToMask: Set<String> = IF config_mode == default:
            ["password", "token", "ssn"]  // Predefined for default mode
        ELSE:
            ["password", "token", "ssn", "uuid"]  // Custom user input

        NetworkLogger.setRequestObfuscationHandler { request in
            var mutableRequest = (request as NSURLRequest).mutableCopy() as! NSMutableURLRequest

            // Mask headers using loop
            for header in headersToMask {
                if mutableRequest.value(forHTTPHeaderField: header) != nil {
                    mutableRequest.setValue("*****", forHTTPHeaderField: header)
                }
            }

            // Mask body fields using recursive function
            if let body = mutableRequest.httpBody,
               let jsonObject = try? JSONSerialization.jsonObject(with: body) {
                let maskedObject = self.maskFields(in: jsonObject, fieldsToMask: bodyFieldsToMask)
                if let newBody = try? JSONSerialization.data(withJSONObject: maskedObject) {
                    mutableRequest.httpBody = newBody
                }
            }

            return mutableRequest.copy() as! URLRequest
        }
    }

    // Recursive helper to mask fields at any depth
    private func maskFields(in object: Any, fieldsToMask: Set<String>) -> Any {
        if var dictionary = object as? [String: Any] {
            for (key, value) in dictionary {
                if fieldsToMask.contains(key) {
                    dictionary[key] = "*****"
                } else if value is [String: Any] || value is [Any] {
                    dictionary[key] = maskFields(in: value, fieldsToMask: fieldsToMask)
                }
            }
            return dictionary
        } else if let array = object as? [Any] {
            return array.map { maskFields(in: $0, fieldsToMask: fieldsToMask) }
        }
        return object
    }

    // ... rest of AppDelegate methods ...
}

Key Points:

  • Masking MUST be called AFTER Luciq.start()
  • Use Set<String> for O(1) lookup performance
  • Recursive function handles nested JSON at any depth
  • For SwiftUI: Use Self.maskFields (static) inside init closure
  • For UIKit: Use self.maskFields (instance) in AppDelegate

Optional Step 2 — Mask Repro Step Screenshots

📋

iOS Implementation

Check Configuration Mode:

IF config_mode == default:
  Apply all masking types automatically:
    Luciq.setAutoMaskScreenshots([.textInputs, .labels, .media])
ELSE IF config_mode == custom:
  Ask user for masking preferences
END IF
// Can be called anywhere after Luciq.start()

// Default mode: All types (maximum privacy)
IF config_mode == default:
  Luciq.setAutoMaskScreenshots([.textInputs, .labels, .media])

// Custom mode options:
ELSE IF config_mode == custom:
  // For text inputs only:
  Luciq.setAutoMaskScreenshots([.textInputs])

  // For labels only:
  Luciq.setAutoMaskScreenshots([.labels])

  // For text inputs + labels:
  Luciq.setAutoMaskScreenshots([.textInputs, .labels])

  // For all types (comprehensive):
  Luciq.setAutoMaskScreenshots([.media])
END IF

Available iOS Mask Types:

  • .textInputs - UITextField, UITextView, secure text fields
  • .labels - UILabel, UIButton text
  • .media - Comprehensive masking (includes all types)

Optional Step 3 — Configure Repro Steps Mode

📋

iOS Implementation

Based on user selections, apply configuration:

// Example: User selected Bug Reporting and Session Replay (1,3)
Luciq.setReproStepsFor(.bug, with: .enable)
Luciq.setReproStepsFor(.sessionReplay, with: .enable)
Luciq.setReproStepsFor(.crash, with: .enabledWithNoScreenshots)  // Not selected

// Example: User selected All products (4)
Luciq.setReproStepsFor(.all, with: .enable)

// Example: User selected only Crash Reporting (2)
Luciq.setReproStepsFor(.crash, with: .enable)
Luciq.setReproStepsFor(.bug, with: .enabledWithNoScreenshots)  // Not selected
Luciq.setReproStepsFor(.sessionReplay, with: .enabledWithNoScreenshots)  // Not selected

// Example: User selected None (5)
Luciq.setReproStepsFor(.all, with: .enabledWithNoScreenshots)

Issue Type Options:

  • .bug - Bug Reporting
  • .crash - Crash Reporting
  • .sessionReplay - Session Replay
  • .all - All products

Key Points:

  • Selected products → .enable (with screenshots)
  • Non-selected products → .enabledWithNoScreenshots (without screenshots)

Optional Step 4 — Add User Identification

📋

iOS Implementation - Login Flow

import LuciqSDK

class AuthenticationManager: ObservableObject {
    func signIn(email: String, password: String) -> Bool {
        // ... authentication logic ...

        if authenticationSuccessful {
            // Identify user AFTER successful auth, BEFORE navigation
            Luciq.identifyUser(
                withID: nil,              // or user.id
                email: user.email,        // or nil
                name: user.name           // or nil
            )

            currentUser = user
            isAuthenticated = true
            return true
        }

        return false
    }

    func signUp(email: String, password: String, name: String) -> Bool {
        // ... signup logic ...

        if signupSuccessful {
            // Identify user after signup
            Luciq.identifyUser(
                withID: nil,
                email: email,
                name: name
            )

            return true
        }

        return false
    }
}

API Signature:

Luciq.identifyUser(
    withID: String?,     // Required parameter, can be nil
    email: String?,      // Required parameter, can be nil
    name: String?        // Required parameter, can be nil
)

Examples:

  • Email only: Luciq.identifyUser(withID: nil, email: user.email, name: user.name)
  • ID only: Luciq.identifyUser(withID: user.id, email: nil, name: user.name)
  • Both: Luciq.identifyUser(withID: user.id, email: user.email, name: user.name)

iOS Implementation - Logout Flow

class AuthenticationManager: ObservableObject {
    func logout() {
        // Call Luciq logout BEFORE clearing session
        Luciq.logOut()

        // Then clear user data
        currentUser = nil
        isAuthenticated = false
        UserDefaults.standard.removeObject(forKey: "loggedInUserEmail")
    }
}

Step 4 — Verification & Testing [WRAP UP & VALIDATE]

📋

iOS Build Verification

xcodebuild -project YourProject.xcodeproj \
           -scheme YourScheme \
           -configuration Debug \
           -destination 'platform=iOS Simulator,name=iPhone 15 Pro' \
           build

Expected: ** BUILD SUCCEEDED **

iOS-Specific Build Errors:

ErrorCauseFix
no such module 'Luciq'Used import Luciq instead of import LuciqSDKChange to import LuciqSDK
version not foundInvalid SPM versionCheck version from releases/latest
Missing package product 'Luciq'Package not added to targetAdd "Luciq" product in Xcode

iOS Runtime Testing

  1. Run app in Xcode (Cmd+R)
  2. Trigger Luciq:
    • Shake: Hardware → Shake (Cmd+Ctrl+Z in simulator)
    • Screenshot: Cmd+S in simulator
    • Floating Button: Tap the button overlay
  3. Verify Luciq UI appears
  4. Submit test report
  5. Check dashboard

iOS-Specific Troubleshooting

Issue: "Command SwiftCompile failed"

Cause: Import or syntax error Fix: Verify all imports are import LuciqSDK, check Swift syntax

Issue: NetworkLogger not found

Cause: Import missing Fix: Ensure import LuciqSDK at top of file

Issue: Shake not working in simulator

Cause: Simulator gesture needed Fix: Use Hardware → Shake or Cmd+Ctrl+Z

Issue: Reports missing user info

Cause: identifyUser called with all nil parameters Fix: Pass at least email or ID (not all nil)


Quick Reference

// ALWAYS use this import
import LuciqSDK

// Basic initialization
Luciq.start(withToken: "token", invocationEvents: [.shake, .screenshot])

// User identification
Luciq.identifyUser(withID: nil, email: "[email protected]", name: "John")

// Logout
Luciq.logOut()

// Screenshot masking
Luciq.setAutoMaskScreenshots([.textInputs, .labels])

// Network masking (after start)
NetworkLogger.setRequestObfuscationHandler { request in
    // ... masking logic ...
}