{"id":29916274,"url":"https://github.com/tenmax/tenmax-beacon-library-sdk-ios","last_synced_at":"2025-08-02T04:43:27.800Z","repository":{"id":306001402,"uuid":"966509130","full_name":"tenmax/tenmax-beacon-library-sdk-ios","owner":"tenmax","description":null,"archived":false,"fork":false,"pushed_at":"2025-07-23T04:38:39.000Z","size":695,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-23T06:08:33.851Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tenmax.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-15T03:14:08.000Z","updated_at":"2025-07-23T04:29:18.000Z","dependencies_parsed_at":"2025-07-23T06:18:38.210Z","dependency_job_id":null,"html_url":"https://github.com/tenmax/tenmax-beacon-library-sdk-ios","commit_stats":null,"previous_names":["tenmax/tenmax-beacon-library-sdk-ios"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tenmax/tenmax-beacon-library-sdk-ios","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenmax%2Ftenmax-beacon-library-sdk-ios","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenmax%2Ftenmax-beacon-library-sdk-ios/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenmax%2Ftenmax-beacon-library-sdk-ios/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenmax%2Ftenmax-beacon-library-sdk-ios/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tenmax","download_url":"https://codeload.github.com/tenmax/tenmax-beacon-library-sdk-ios/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenmax%2Ftenmax-beacon-library-sdk-ios/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268334820,"owners_count":24233828,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-08-02T04:43:19.757Z","updated_at":"2025-08-02T04:43:27.784Z","avatar_url":"https://github.com/tenmax.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TenMax Beacon SDK for iOS\n\n[![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)](./VERSION)\n[![Platform](https://img.shields.io/badge/platform-iOS%2012.0%2B-lightgrey.svg)](https://developer.apple.com/ios/)\n[![Swift](https://img.shields.io/badge/swift-5.5%2B-orange.svg)](https://swift.org/)\n\nA Swift package for integrating TenMax Beacon tracking functionality into iOS applications.\n\n## Features\n\n- **Beacon Detection**: Supports both Apple iBeacon and custom Bluetooth beacon detection with sequential scanning\n- **Background Scanning**: Continues beacon scanning even when the app is in the background using Core Location\n- **Frequency Capping**: Intelligent frequency control to prevent spam notifications with configurable intervals\n- **Beacon Deduplication**: Prevents processing duplicate beacon data within 30-second time windows (configurable)\n- **Network Connectivity**: Automatic network connectivity checking before API calls using Reachability\n- **Notification Management**: Handles local notifications with click tracking and creative data content support\n- **Environment Support**: Separate configurations for staging and production environments with sdk.plist configuration\n- **Thread Safety**: All operations are thread-safe with proper concurrent queue management and automatic main thread callback execution\n- **Comprehensive Testing**: Extensive test suite with Quick and Nimble frameworks and mock infrastructure\n- **Privacy Compliance**: Includes PrivacyInfo.xcprivacy for App Store privacy requirements\n\n## Installation\n\n### Swift Package Manager\n\nThe [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into Xcode.\n\n#### Xcode Project Integration\n\nGo to `File \u003e Add Package Dependencies...`, paste `https://github.com/tenmax/tenmax-beacon-library-sdk-ios` into package URL. After the package is found, you can indicate the exact version of SDK. Then, click the `Add Package` to add the SDK package into your Xcode project.\n\n#### Swift Package Integration\n\nAdding TenMaxBeaconSDK as a dependency into your `Package.swift` and indicating the SDK version.\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/tenmax/tenmax-beacon-library-sdk-ios\", .upToNextMajor(from: \"1.0.0\"))\n]\n```\n\nNormally you'll need to add the `TenMaxBeaconSDK` target:\n\n```swift\n.product(name: \"TenMaxBeaconSDK\", package: \"TenMaxBeaconSDK\")\n```\n\nThe Package.swift sample like this:\n\n```swift\nlet package = Package(\n    name: \"YourPackageName\",\n    products: [\n        .library(name: \"YourPackageName\", targets: [\"YourTargetName\"]),\n    ],\n    dependencies: [\n      .package(url: \"https://github.com/tenmax/tenmax-beacon-library-sdk-ios\", .upToNextMajor(from: \"1.0.0\"))\n    ],\n    targets: [\n        .target(\n            name: \"YourTargetName\",\n            dependencies: [\n              .product(name: \"TenMaxBeaconSDK\", package: \"tenmax-beacon-library-sdk-ios\")\n            ],\n        ),\n    ]\n)\n```\n\n### Carthage\n\n[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate TenMaxBeaconSDK into your Xcode project using Carthage, specify it in your `Cartfile`:\n\n```\nbinary \"https://raw.githubusercontent.com/tenmax/tenmax-beacon-library-sdk-ios/main/public/TenMaxBeaconSDK.json\"\n```\n\nAfter Carthage downloaded the `TenMaxBeaconSDK.xcframework` file, you can find the file in the folder `./Carthage/Build`. Make sure you have added `TenMaxBeaconSDK.xcframework` to the \"Linked Frameworks and Libraries\" section of your target.\n\n## SDK Configuration\n\n### App Configuration\n\nUpdate your app's `Info.plist` file to add required permission descriptions:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003c!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"\u003e\n\u003cplist version=\"1.0\"\u003e\n\u003cdict\u003e\n    \u003c!-- Location Permission --\u003e\n    \u003ckey\u003eNSLocationAlwaysAndWhenInUseUsageDescription\u003c/key\u003e\n    \u003cstring\u003eWe need location permission to detect nearby beacons and provide relevant services.\u003c/string\u003e\n    \u003ckey\u003eNSLocationWhenInUseUsageDescription\u003c/key\u003e\n    \u003cstring\u003eWe need location permission to detect nearby beacons and provide relevant services.\u003c/string\u003e\n    \n    \u003c!-- Bluetooth Permission --\u003e\n    \u003ckey\u003eNSBluetoothAlwaysUsageDescription\u003c/key\u003e\n    \u003cstring\u003eWe need Bluetooth permission to scan for nearby beacons and provide relevant services.\u003c/string\u003e\n    \n    \u003c!-- App Tracking Transparency --\u003e\n    \u003ckey\u003eNSUserTrackingUsageDescription\u003c/key\u003e\n    \u003cstring\u003eThis app uses advertising identifier to provide personalized beacon-based advertising content and analytics.\u003c/string\u003e\n    \n    \u003c!-- Background Modes --\u003e\n    \u003ckey\u003eUIBackgroundModes\u003c/key\u003e\n    \u003carray\u003e\n        \u003cstring\u003ebluetooth-central\u003c/string\u003e\n        \u003cstring\u003elocation\u003c/string\u003e\n    \u003c/array\u003e\n\u003c/dict\u003e\n\u003c/plist\u003e\n```\n\n### SDK Initiation\n\nTenMax Beacon SDK must be initiated before use, thus, we recommend you to initiate it in your `AppDelegate` class. The SDK would run the initiation in an independent thread pool so won't increase your application launch time.\n\n```swift\nimport TenMaxBeaconSDK\n\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -\u003e Bool {\n\n        // Create client profile\n        // Phone number and email are optional and require user consent\n        // UUID v1 is automatically generated and stored in UserDefaults\n        // Device information is automatically collected\n        // User data (phone, email) is automatically persisted across app restarts\n        let clientProfile = ClientProfile(\n            phoneNumber: \"0912345678\",    // Optional: will be persisted and restored\n            email: \"user@example.com\",    // Optional: will be persisted and restored\n            appName: \"YourAppName\",\n            advertisingId: \"advertising-id-here\"    // Optional: auto-retrieved from system if authorized\n        )\n\n        // Initialize SDK\n        let callback = BeaconCallback()\n        TenMaxAdBeaconSDK.shared().initiate(\n            clientProfile: clientProfile,\n            callback: callback,\n            environment: .production\n        )\n\n        return true\n    }\n}\n```\n\n**Note**: You must specify the environment (`.stage` or `.production`) during initialization. The SDK will select the appropriate API endpoints based on the environment from sdk.plist configuration file:\n\n- **Stage Environment**: Uses beta-beacon-event.tenmax.io for events and storage.googleapis.com for creative CDN\n- **Production Environment**: Uses beacon-event.tenmax.io for events and dsp-creative-cdn.tenmax.io for creative CDN\n\nEnvironment can only be set during initialization and cannot be changed later.\n\n## Usage\n\n### Implementing Beacon Callback\n\nCreate a callback class to handle beacon events:\n\n```swift\nimport TenMaxBeaconSDK\n\n// Implement callback interface\nclass BeaconCallback: TenMaxAdBeaconCallback {\n    func onInitialized() {\n        print(\"SDK initialized successfully\")\n\n        // Start SDK\n        TenMaxAdBeaconSDK.shared().start()\n    }\n\n    func onCreativeReceived(creative: TenMaxAdCreative) {\n        print(\"Received creative with data: \\(creative.data ?? \"No data\")\")\n    }\n\n    func onError(error: TenMaxAdBeaconError) {\n        print(\"Error: \\(error.message)\")\n    }\n\n    func onNotificationClicked(creative: TenMaxAdCreative) {\n        print(\"Notification clicked with data: \\(creative.data ?? \"\"), creativeId: \\(creative.creativeId)\")\n        // Handle data navigation\n    }\n}\n```\n\n### Starting and Stopping the SDK\n\n```swift\n// Start SDK (call after successful initialization)\nTenMaxAdBeaconSDK.shared().start()\n\n// Stop SDK\nTenMaxAdBeaconSDK.shared().stop()\n\n// Check if SDK is currently scanning\nlet isScanning = TenMaxAdBeaconSDK.shared().isScanning\nprint(\"SDK is scanning: \\(isScanning)\")\n```\n\n### Updating Client Profile\n\n```swift\n// Update client profile\nlet updatedProfile = ClientProfile(\n    phoneNumber: \"0987654321\",\n    email: \"new-email@example.com\",\n    appName: \"YourAppName\",\n    advertisingId: \"updated-advertising-id\"\n)\nTenMaxAdBeaconSDK.shared().updateClientProfile(updatedProfile)\n```\n\n### Data Persistence\n\nThe SDK automatically persists user profile data across app restarts. This ensures that user information is retained even when the app is killed and restarted.\n\n#### Automatic Data Persistence\n\n```swift\n// First time - provide user data\nlet profile = ClientProfile(\n    phoneNumber: \"0912345678\",\n    email: \"user@example.com\",\n    appName: \"YourApp\"\n)\n\n// After app restart - data is automatically restored\nlet profileAfterRestart = ClientProfile(appName: \"YourApp\")\n// phoneNumber and email are automatically loaded from previous session\n```\n\n#### Automatic Advertising ID Retrieval\n\n```swift\n// When advertisingId is nil, SDK automatically retrieves from system if authorized\nlet profile = ClientProfile(\n    phoneNumber: \"0912345678\",\n    email: \"user@example.com\",\n    appName: \"YourApp\",\n    advertisingId: nil  // SDK will auto-retrieve system IDFA if authorized\n)\n\n// Explicit advertisingId always takes precedence\nlet profileWithExplicitId = ClientProfile(\n    phoneNumber: \"0912345678\",\n    email: \"user@example.com\",\n    appName: \"YourApp\",\n    advertisingId: \"explicit-id\"  // Uses this value instead of system IDFA\n)\n```\n\n#### Clearing Persisted Data\n\n```swift\n// Clear all persisted user data\nClientProfile.clearPersistedData()\n\n// After clearing, create new profile with fresh data\nlet freshProfile = ClientProfile(\n    phoneNumber: \"new-phone\",\n    email: \"new-email@example.com\",\n    appName: \"YourApp\"\n)\n```\n\n### Creative Content Handling\n\nWhen a beacon is detected, the SDK will automatically:\n\n1. **Scan for Beacons**: Continuously scan for both iBeacons and custom beacons\n2. **Fetch Creative Content**: Retrieve relevant advertising content based on beacon proximity\n3. **Deliver Content**: Call your callback with the creative content\n4. **Handle Notifications**: Optionally trigger local notifications\n\n## Demo Application\n\nThe `demo` folder contains a complete demonstration application showing:\n\n- SDK integration using XCFramework\n- Permission management\n- User profile configuration\n- Real-time beacon detection\n- Creative content display\n- Notification handling\n\nTo run the demo:\n\n1. Navigate to `demo`\n2. Open `TenMaxBeaconPublicDemoApp.xcworkspace`\n3. Build and run on a physical device (required for beacon functionality)\n\n## Error Handling\n\nThe SDK provides comprehensive error handling through the `TenMaxAdBeaconError` class. Common error types include:\n\n- **Permission Errors**: Location, Bluetooth, or Notification permissions denied\n- **Configuration Errors**: SDK initialization, beacon settings, or API configuration issues\n- **Network Errors**: Internet connectivity issues\n- **Frequency Cap Exceeded**: Creative content blocked due to frequency limits\n- **Beacon Errors**: Issues with beacon detection or processing\n\n### Comprehensive Error Handling\n\n```swift\nclass BeaconCallback: TenMaxAdBeaconCallback {\n\n    func onInitialized() {\n        print(\"SDK initialized successfully\")\n        // Start scanning after successful initialization\n        TenMaxAdBeaconSDK.shared().start()\n    }\n\n    func onCreativeReceived(creative: TenMaxAdCreative) {\n        print(\"Creative received with data: \\(creative.data ?? \"No data\")\")\n\n        // Handle data navigation\n        if let data = creative.data {\n            handleData(data)\n        }\n    }\n\n    func onError(error: TenMaxAdBeaconError) {\n        print(\"SDK Error: \\(error.message)\")\n\n        switch error.code {\n        case .unauthorizedLocationPermission:\n            showLocationPermissionAlert()\n\n        case .locationPermissionNotAlways:\n            showAlwaysLocationPermissionAlert()\n\n        case .bluetoothDisabled:\n            showBluetoothDisabledAlert()\n\n        case .bluetoothPermissionDenied:\n            showBluetoothPermissionAlert()\n\n        case .unauthorizedNotificationPermission:\n            showNotificationPermissionAlert()\n\n        case .internetConnectionUnavailable:\n            showOfflineMessage()\n\n        case .frequencyCapExceeded:\n            // This is normal behavior - creative was blocked due to frequency limits\n            print(\"Creative blocked due to frequency capping\")\n\n        case .configurationError:\n            // Handle configuration issues (Please contact with app_support@tenmax.io)\n            print(\"Configuration error: Check SDK initialization and configuration\")\n\n        case .notFoundCreative:\n            print(\"No creative content available for this beacon\")\n\n        case .invalidCreative:\n            print(\"Invalid creative data received from server\")\n\n        default:\n            showGenericErrorAlert(message: error.message)\n        }\n    }\n\n    func onNotificationClicked(creative: TenMaxAdCreative) {\n        print(\"Notification clicked with data: \\(creative.data ?? \"\"), creativeId: \\(creative.creativeId)\")\n        if let data = creative.data {\n            handleData(data)\n        }\n    }\n\n    // Helper methods for handling data and showing alerts\n    private func handleData(_ data: String) {\n        // Implement your data handling logic\n        if let url = URL(string: data) {\n            // Navigate to appropriate screen\n            // UIApplication.shared.open(url)\n        }\n    }\n\n    private func showLocationPermissionAlert() {\n        // Show alert guiding user to enable location permission\n    }\n\n    // ... other helper methods\n}\n```\n\n## Advanced Features\n\n### Environment Configuration\n\nThe SDK supports both staging and production environments:\n\n```swift\n// For development and testing\nTenMaxAdBeaconSDK.shared().initiate(\n    clientProfile: clientProfile,\n    environment: .stage,\n    callback: callback\n)\n\n// For production release\nTenMaxAdBeaconSDK.shared().initiate(\n    clientProfile: clientProfile,\n    environment: .production,\n    callback: callback\n)\n```\n\n### Background Scanning\n\nThe SDK supports background beacon scanning when proper permissions are granted and background modes are configured in your app's Info.plist.\n\n### Thread Safety\n\nAll SDK callbacks are automatically executed on the main thread, ensuring safe UI updates without additional threading considerations.\n\n## System Requirements\n\n- **iOS**: 12.0 or later\n- **Swift**: 5.5 or later\n- **Xcode**: 16.2.0 or later\n- **Deployment Target**: iOS 12.0+\n\n### Device Requirements\n\n- **Bluetooth**: Bluetooth 4.0 (Bluetooth Low Energy) or later\n- **Location Services**: Required for beacon detection\n- **Background App Refresh**: Recommended for optimal background scanning\n- **Device**: Physical iOS device (beacon functionality requires hardware)\n\n## Privacy and Data Handling\n\n### Data Collection and Storage\n\nThe TenMax Beacon SDK collects and stores the following types of data:\n\n#### Automatically Collected Data\n- **Device Information**: Device model, system name and version\n- **UUID**: Unique identifier generated locally and stored permanently\n- **Advertising ID**: System advertising identifier (IDFA) when available and authorized\n\n#### User-Provided Data\n- **Phone Number**: Stored locally in UserDefaults when provided\n- **Email Address**: Stored locally in UserDefaults when provided\n- **App Name**: Stored locally in UserDefaults\n\n### Data Persistence\n\n- **Local Storage**: User profile data is stored locally using UserDefaults\n- **Automatic Cleanup**: Data is automatically removed when the app is uninstalled\n- **Manual Cleanup**: Use `ClientProfile.clearPersistedData()` to manually clear stored data\n\n### Advertising ID Handling\n\n- **iOS 14+ Compliance**: Automatically checks ATTrackingManager authorization status\n- **Fallback Behavior**: When advertising ID is unavailable or unauthorized, SDK continues to function normally\n- **User Control**: Respects user's tracking preferences set in iOS Settings\n\n### Privacy Compliance\n\n- **PrivacyInfo.xcprivacy**: Includes App Store privacy manifest\n- **User Consent**: Obtain appropriate user consent before collecting personal information\n- **Data Minimization**: Only collects data necessary for beacon functionality\n- **Transparency**: All data collection is documented and transparent\n\n### Best Practices\n\n```swift\n// Always obtain user consent before collecting personal data\nfunc requestUserConsent() {\n    // Your consent mechanism here\n    if userConsentedToDataCollection {\n        let profile = ClientProfile(\n            phoneNumber: userPhoneNumber,\n            email: userEmail,\n            appName: \"YourApp\"\n        )\n        // Initialize SDK with profile\n    }\n}\n\n// Clear data when user withdraws consent\nfunc handleConsentWithdrawal() {\n    ClientProfile.clearPersistedData()\n    TenMaxAdBeaconSDK.shared().stop()\n}\n```\n\n## Apple Privacy Survey for TenMax SDK\n\niOS publisher should provide the information that data their apps collect, including the data collected by third-party SDKs. For your convenience, TenMax SDK provides the information on its data collection in the [Apple Privacy Survey for TenMax SDK](Privacy.md).\n\n## Issues and Contact\n\nIf you have any issue when using TenMax Beacon SDK, please contact [app_support@tenmax.io](mailto:app_support@tenmax.io). We would help you as soon as possible.\n\n## User Data Deletion Notice\n\nFor requests to delete the privacy data linked to users, please submit the request via [User Data Deletion Notice Form](https://forms.office.com/r/SnU40q6VmQ).\n\n## License\n\nTenMax\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftenmax%2Ftenmax-beacon-library-sdk-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftenmax%2Ftenmax-beacon-library-sdk-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftenmax%2Ftenmax-beacon-library-sdk-ios/lists"}