{"id":30719844,"url":"https://github.com/dankinsoid/swift-ads","last_synced_at":"2025-09-03T10:42:25.682Z","repository":{"id":307129622,"uuid":"1028437062","full_name":"dankinsoid/swift-ads","owner":"dankinsoid","description":"Swift iOS Ads interface","archived":false,"fork":false,"pushed_at":"2025-07-29T15:09:30.000Z","size":17,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-29T17:22:13.140Z","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":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dankinsoid.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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-07-29T14:24:59.000Z","updated_at":"2025-07-29T15:09:33.000Z","dependencies_parsed_at":"2025-07-29T17:26:46.011Z","dependency_job_id":"2019b711-9e87-47b8-94ac-7398b6bc3a30","html_url":"https://github.com/dankinsoid/swift-ads","commit_stats":null,"previous_names":["dankinsoid/swift-ads"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/dankinsoid/swift-ads","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankinsoid%2Fswift-ads","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankinsoid%2Fswift-ads/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankinsoid%2Fswift-ads/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankinsoid%2Fswift-ads/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dankinsoid","download_url":"https://codeload.github.com/dankinsoid/swift-ads/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankinsoid%2Fswift-ads/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273431361,"owners_count":25104491,"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-09-03T02:00:09.631Z","response_time":76,"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-09-03T10:42:24.590Z","updated_at":"2025-09-03T10:42:25.665Z","avatar_url":"https://github.com/dankinsoid.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SwiftAds\n\nA Swift package that provides a common interface for advertising systems across different platforms and ad networks. Similar to [swift-log](https://github.com/apple/swift-log) and [swift-metrics](https://github.com/apple/swift-metrics), SwiftAds defines a standardized API that allows you to switch between different ad implementations without changing your application code.\n\n## Features\n\n- 🎯 **Unified API** - Single interface for all ad types (banners, interstitials, rewarded videos)\n- 🔧 **Pluggable Backends** - Easy integration with any ad network\n- 🚀 **Async/Await Support** - Modern Swift concurrency\n- 📱 **Cross-Platform** - iOS, macOS, tvOS, watchOS support\n- 🎮 **Placement Targeting** - Flexible ad placement management\n- 🚫 **NOOP Handler** - Built-in no-operation handler\n- 🔄 **Multiplex Handler** - Fallback support with multiple ad networks\n\n## Installation\n\n### Swift Package Manager\n\nAdd SwiftAds to your `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/dankinsoid/swift-ads.git\", from: \"1.0.0\")\n]\n```\n\nOr add it through Xcode:\n1. File → Add Package Dependencies\n2. Enter: `https://github.com/dankinsoid/swift-ads.git`\n\n## Quick Start\n\n### 1. Bootstrap the Ads System\n\nFirst, configure SwiftAds with your preferred ad handler during app startup:\n\n```swift\nimport SwiftAds\n\n// Bootstrap with your ad handler (once per app lifecycle)\nAdsSystem.bootstrap(YourCustomAdsHandler())\n```\n\n### 2. Display Ads\n\n```swift\nimport SwiftAds\n\n// Create ads instance\nlet ads = Ads()\n\n// Show interstitial ad\ntry await ads.showInterstitial(id: \"your-ad-id\")\n\n// Show rewarded video\ntry await ads.showRewarderVideo(id: \"your-rewarded-ad-id\")\n\n// Load and display banner\nlet bannerView = try await ads.loadBanner(\n    id: \"your-banner-id\", \n    in: viewController, \n    size: .standart\n)\nview.addSubview(bannerView)\n```\n\n### 3. Preload Ads for Better Performance\n\n```swift\n// Preload ads for faster display\ntry await ads.loadInterstitial(id: \"your-ad-id\")\ntry await ads.loadRewarderVideo(id: \"your-rewarded-ad-id\")\n```\n\n## Ad Placements\n\nUse placements to organize and target different ad locations in your app:\n\n```swift\n// Create ads instance with placement\nlet mainMenuAds = Ads(placement: \"main_menu\")\nlet gameOverAds = Ads(placement: \"game_over\")\n\n// Different placements can show different ads\ntry await mainMenuAds.showInterstitial(id: \"menu-ad\")\ntry await gameOverAds.showInterstitial(id: \"game-over-ad\")\n```\n\n## Banner Sizes\n\nSwiftAds supports various banner sizes:\n\n```swift\n// Standard banner sizes\nlet standardBanner = try await ads.loadBanner(id: \"banner-id\", in: vc, size: .standart)\nlet mediumBanner = try await ads.loadBanner(id: \"banner-id\", in: vc, size: .medium)\nlet largeBanner = try await ads.loadBanner(id: \"banner-id\", in: vc, size: .large)\n\n// Adaptive banner (adjusts to screen width)\nlet adaptiveBanner = try await ads.loadBanner(id: \"banner-id\", in: vc, size: .adaptive)\n\n// Custom size\nlet customBanner = try await ads.loadBanner(\n    id: \"banner-id\", \n    in: vc, \n    size: .custom(width: 320, height: 100)\n)\n```\n\n## Creating Custom Ad Handlers\n\nImplement the `AdsHandler` protocol to integrate with your preferred ad network:\n\n```swift\nimport SwiftAds\n\nstruct MyAdMobHandler: AdsHandler {\n    \n    func initAds() async throws {\n        // Initialize your ad network SDK\n    }\n    \n    @MainActor\n    func loadBanner(\n        in controller: UIViewController, \n        size: Ads.Size, \n        id: String, \n        placement: String?\n    ) async throws -\u003e UIView {\n        // Return configured banner view\n    }\n    \n    func loadInterstitial(id: String, placement: String?) async throws {\n        // Preload interstitial ad\n    }\n    \n    func loadRewarderVideo(id: String, placement: String?) async throws {\n        // Preload rewarded video ad\n    }\n    \n    @MainActor\n    func showInterstitial(\n        from controller: UIViewController, \n        id: String, \n        placement: String?\n    ) async throws {\n        // Display interstitial ad\n    }\n    \n    @MainActor\n    func showRewarderVideo(\n        from controller: UIViewController, \n        id: String, \n        placement: String?\n    ) async throws {\n        // Display rewarded video ad\n    }\n}\n\n// Bootstrap your handler\nAdsSystem.bootstrap(MyAdMobHandler())\n```\n\n### Example Implementation\n\nFor a complete handler implementation example, see:\n- [**AppLovinAdsHandler**](https://github.com/dankinsoid/AppLovinAdsHandler) - Full AppLovin integration with SwiftAds\n\n## Multiplex Handler (Fallback Support)\n\nUse `MultiplexAdsHandler` to combine multiple ad networks with automatic fallback:\n\n```swift\nimport SwiftAds\n\n// Create multiplex handler with fallback priority\nlet multiplexHandler = MultiplexAdsHandler(\n    PrimaryAdNetworkHandler(),    // Try this first\n    SecondaryAdNetworkHandler(),  // Fallback to this if first fails\n    NOOPAdsHandler()             // Final fallback (always succeeds)\n)\n\n// Bootstrap with multiplex handler\nAdsSystem.bootstrap(multiplexHandler)\n```\n\nThe `MultiplexAdsHandler` tries each handler in sequence until one succeeds:\n\n1. **Primary Network**: Attempts to load/show ads with your main provider\n2. **Secondary Network**: Falls back if primary fails (network issues, no fill, etc.)\n3. **Tertiary/NOOP**: Final fallback to ensure ads never completely break your app\n\nBenefits:\n- **Higher Fill Rates**: Multiple networks increase ad availability\n- **Redundancy**: App continues working if one network fails\n- **Revenue Optimization**: Waterfall approach maximizes monetization\n\n## Exponential Retry Handler\n\nUse `ExponentialRetryAdsHandler` to add automatic retry logic with exponential backoff for **load operations only**:\n\n```swift\nimport SwiftAds\n\n// Wrap any handler with exponential retry logic\nlet retryHandler = ExponentialRetryAdsHandler(\n    wrapping: YourAdNetworkHandler(),\n    maxRetries: 6        // Default: 6 attempts\n)\n\n// Bootstrap with retry handler\nAdsSystem.bootstrap(retryHandler)\n```\n\nThe retry delays follow exponential backoff (only for load functions):\n- 1st retry: 2 seconds (2^1)\n- 2nd retry: 4 seconds (2^2)\n- 3rd retry: 8 seconds (2^3)\n- 4th retry: 16 seconds (2^4)\n- 5th retry: 32 seconds (2^5)\n- 6th retry: 64 seconds (2^6)\n\n**Load Functions with Retry:**\n- `loadBanner()` - Retries with exponential backoff\n- `loadInterstitial()` - Retries with exponential backoff  \n- `loadRewarderVideo()` - Retries with exponential backoff\n\n**Show Functions (No Retry):**\n- `showInterstitial()` - No retry (immediate success/failure)\n- `showRewarderVideo()` - No retry (immediate success/failure)\n\nBenefits:\n- **Load Reliability**: Automatically recovers from temporary network issues during ad loading\n- **Smart Backoff**: Exponential delays prevent server overload while maximizing success rate\n- **Show Performance**: No delays on show operations for immediate user experience\n\nYou can combine handlers for powerful retry + fallback patterns:\n\n```swift\nlet robustHandler = MultiplexAdsHandler(\n    ExponentialRetryAdsHandler(wrapping: PrimaryAdNetworkHandler()),\n    ExponentialRetryAdsHandler(wrapping: SecondaryAdNetworkHandler()),\n    NOOPAdsHandler()\n)\nAdsSystem.bootstrap(robustHandler)\n```\n\n## Testing\n\nFor testing and development, use the built-in `NOOPAdsHandler`:\n\n```swift\n#if DEBUG\nAdsSystem.bootstrap(NOOPAdsHandler())\n#endif\n```\n\n## Error Handling\n\nSwiftAds methods can throw errors. Handle them appropriately:\n\n```swift\ndo {\n    try await ads.showInterstitial(id: \"ad-id\")\n} catch {\n    print(\"Failed to show ad: \\\\(error)\")\n    // Handle error (e.g., no internet, ad not loaded, etc.)\n}\n```\n\n## Thread Safety\n\nSwiftAds is thread-safe and can be used from any queue. UI-related operations are automatically dispatched to the main actor.\n\n## Related Packages\n\nSwiftAds works great with other Swift ecosystem packages that follow similar interface patterns:\n\n- [**swift-analytics**](https://github.com/dankinsoid/swift-analytics) - Unified analytics interface\n- [**swift-remote-configs**](https://github.com/dankinsoid/swift-remote-configs) - Unified remote configuration interface\n\nExample integration:\n\n```swift\nimport SwiftAds\nimport SwiftAnalytics\nimport SwiftRemoteConfigs\n\n// Track ad events\ntry await ads.showInterstitial(id: \"game-over-ad\")\nAnalytics.track(\"ad_displayed\", properties: [\"placement\": \"game_over\"])\n\n// Use remote config for ad IDs\nlet adId = RemoteConfigs.string(\"interstitial_ad_id\", default: \"default-ad-id\")\ntry await ads.showInterstitial(id: adId)\n\n// Combine with retry handler\nlet handler = MyAdNetworkHandler().withExponentialRetry()\nAdsSystem.bootstrap(handler)\n```\n\n## Requirements\n\n- iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+\n- Swift 5.9+\n- Xcode 15.0+\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdankinsoid%2Fswift-ads","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdankinsoid%2Fswift-ads","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdankinsoid%2Fswift-ads/lists"}