From efa77487c3f128b830ded1cbecb9d5be0f429a42 Mon Sep 17 00:00:00 2001 From: Thomas So <5104410+thomasmso@users.noreply.github.com> Date: Thu, 16 Apr 2020 12:12:38 -0700 Subject: [PATCH] Feat/migrate AppLovin APIs (#17) * Migrating AppLovin ads for interstitials * Deleting mediation debugger icon and adding it under MAX, removing subtitles * interstitial UI changes * Rewarded ad changes * Banner ad changes * Leaders changes * MRECs changes * Native Ads Changes * Event Tracking changes * Support changes * adding mute button and and correcting titles for navigation items * Restructure projects * WIP * Select Info.plist * Organize project * adding Interface Builder to MRECs * Re-name projects * landscape support and support across devices for Interface Builder * correcting project structure for base files * integrating MAX ads and AppLovin Interstitial in Swift * AppLovin Interstitials in Swift * Swift : AppLovin Rewarded Ads * Fix file reference issues * feat/use_exponential_delay_for_fullscreen_ad_load_fail_retries (#16) * feat/use_exponential_delay_for_fullscreen_ad_load_fail_retries * Reset retry attempt on ad load * migration of banner, leaders, native, mrecs, events, support and mute * Adding Mediation Debugger to the table under MAX instead of as an icon at the top toolbar * Updating navigation item title and adding callback table view to banners * Adding interface builder for MRecs in Swift * renaming ObjC Demo App classes (AL -> ALMAX) * renaming Swift Demo App classes (AL -> ALMAX) * Update some copy * Deintegrate CocoaPods Co-authored-by: Varsha Hanji --- .../project.pbxproj | 902 ++++++++++++ .../ALAppDelegate.h | 0 .../ALAppDelegate.m | 0 ...DemoInterfaceBuilderBannerViewController.h | 13 + ...DemoInterfaceBuilderBannerViewController.m | 109 ++ .../ALDemoProgrammaticBannerViewController.h | 13 + .../ALDemoProgrammaticBannerViewController.m | 142 ++ .../ALDemoBannerZoneViewController.h | 13 + .../ALDemoBannerZoneViewController.m | 142 ++ .../ALEventTrackingViewController.h | 13 + .../ALEventTrackingViewController.m | 204 +++ ...terstitialBasicIntegrationViewController.h | 13 + ...terstitialBasicIntegrationViewController.m | 79 ++ ...oInterstitialManualLoadingViewController.h | 13 + ...oInterstitialManualLoadingViewController.m | 80 ++ .../ALDemoInterstitalZoneViewController.h | 13 + .../ALDemoInterstitalZoneViewController.m | 79 ++ ...DemoInterfaceBuilderLeaderViewController.h | 13 + ...DemoInterfaceBuilderLeaderViewController.m | 109 ++ .../ALDemoProgrammaticLeaderViewController.h | 13 + .../ALDemoProgrammaticLeaderViewController.m | 142 ++ ...ALDemoInterfaceBuilderMRECViewController.h | 14 + ...ALDemoInterfaceBuilderMRECViewController.m | 99 ++ .../ALDemoProgrammaticMRECViewController.h | 13 + .../ALDemoProgrammaticMRECViewController.m | 135 ++ .../ALDemoNativeAdFeedTableViewController.h | 13 + .../ALDemoNativeAdFeedTableViewController.m | 98 ++ .../Feed/Carousel UI/ALCarouselCardState.h | 52 + .../Feed/Carousel UI/ALCarouselCardState.m | 34 + .../Carousel UI/ALCarouselRenderingProtocol.h | 26 + .../Feed/Carousel UI/ALCarouselViewModel.h | 32 + .../Feed/Carousel UI/ALCarouselViewModel.m | 174 +++ .../Feed/Carousel UI/ALNativeAdVideoPlayer.h | 30 + .../Feed/Carousel UI/ALNativeAdVideoPlayer.m | 67 + .../Carousel UI/Assets/Star_Sprite_0.5.png | Bin 0 -> 3806 bytes .../Feed/Carousel UI/Assets/Star_Sprite_0.png | Bin 0 -> 3797 bytes .../Carousel UI/Assets/Star_Sprite_1.5.png | Bin 0 -> 3784 bytes .../Feed/Carousel UI/Assets/Star_Sprite_1.png | Bin 0 -> 3738 bytes .../Carousel UI/Assets/Star_Sprite_2.5.png | Bin 0 -> 3734 bytes .../Feed/Carousel UI/Assets/Star_Sprite_2.png | Bin 0 -> 3793 bytes .../Carousel UI/Assets/Star_Sprite_3.5.png | Bin 0 -> 3810 bytes .../Feed/Carousel UI/Assets/Star_Sprite_3.png | Bin 0 -> 3808 bytes .../Carousel UI/Assets/Star_Sprite_4.5.png | Bin 0 -> 3749 bytes .../Feed/Carousel UI/Assets/Star_Sprite_4.png | Bin 0 -> 3749 bytes .../Feed/Carousel UI/Assets/Star_Sprite_5.png | Bin 0 -> 3849 bytes .../Assets/applovin_card_learn_more.png | Bin 0 -> 11806 bytes .../Assets/applovin_card_muted.png | Bin 0 -> 6592 bytes .../Carousel UI/Assets/applovin_card_play.png | Bin 0 -> 10380 bytes .../Assets/applovin_card_replay.png | Bin 0 -> 15399 bytes .../Assets/applovin_card_unmuted.png | Bin 0 -> 5483 bytes .../Categories/ALCarouselView+Internal.h | 20 + .../Categories/UIView+ALActivityIndicator.h | 37 + .../Categories/UIView+ALActivityIndicator.m | 87 ++ .../ALCarouselViewSettings.h | 86 ++ .../Carousel UI/Views/ALCarouselCardView.h | 54 + .../Carousel UI/Views/ALCarouselCardView.m | 353 +++++ .../Carousel UI/Views/ALCarouselMediaView.h | 36 + .../Carousel UI/Views/ALCarouselMediaView.m | 598 ++++++++ .../Views/ALCarouselReplayOverlayView.h | 29 + .../Views/ALCarouselReplayOverlayView.m | 99 ++ .../Feed/Carousel UI/Views/ALCarouselView.h | 34 + .../Feed/Carousel UI/Views/ALCarouselView.m | 642 +++++++++ .../Carousel UI/Views/ALNativeAdVideoView.h | 23 + .../Carousel UI/Views/ALNativeAdVideoView.m | 48 + .../Feed/RSS Feed Parsing/ALDemoArticle.h | 25 + .../Feed/RSS Feed Parsing/ALDemoArticle.m | 13 + .../RSS Feed Parsing/ALDemoRSSFeedRetriever.h | 23 + .../RSS Feed Parsing/ALDemoRSSFeedRetriever.m | 121 ++ ...ALDemoNativeAdProgrammaticViewController.h | 26 + ...ALDemoNativeAdProgrammaticViewController.m | 174 +++ .../ALDemoRewardedVideosViewController.h | 13 + .../ALDemoRewardedVideosViewController.m | 148 ++ .../ALDemoRewardedVideosZoneViewController.h | 13 + .../ALDemoRewardedVideosZoneViewController.m | 156 +++ .../Base Classes}/ALBaseAdViewController.h | 0 .../Base Classes}/ALBaseAdViewController.m | 12 + .../Base Classes}/ALHomeViewController.h | 0 .../Base Classes/ALHomeViewController.m | 173 +++ .../ALMAXAutoLayoutBannerAdViewController.h | 2 +- .../ALMAXAutoLayoutBannerAdViewController.m | 6 +- .../ALMAXFrameLayoutBannerAdViewController.h | 2 +- .../ALMAXFrameLayoutBannerAdViewController.m | 6 +- ...AXInterfaceBuilderBannerAdViewController.h | 2 +- ...AXInterfaceBuilderBannerAdViewController.m | 6 +- .../ALMAXInterstitialAdViewController.h | 2 +- .../ALMAXInterstitialAdViewController.m | 18 +- .../Rewarded/ALMAXRewardedAdViewController.h | 2 +- .../Rewarded/ALMAXRewardedAdViewController.m | 18 +- .../AppIcon.appiconset/1024.png | Bin .../AppIcon.appiconset/120-1.png | Bin .../AppIcon.appiconset/120.png | Bin .../AppIcon.appiconset/152.png | Bin .../AppIcon.appiconset/167.png | Bin .../AppIcon.appiconset/180.png | Bin .../Assets.xcassets/AppIcon.appiconset/20.png | Bin .../Assets.xcassets/AppIcon.appiconset/29.png | Bin .../AppIcon.appiconset/40-1.png | Bin .../AppIcon.appiconset/40-2.png | Bin .../Assets.xcassets/AppIcon.appiconset/40.png | Bin .../AppIcon.appiconset/58-1.png | Bin .../Assets.xcassets/AppIcon.appiconset/58.png | Bin .../Assets.xcassets/AppIcon.appiconset/60.png | Bin .../Assets.xcassets/AppIcon.appiconset/76.png | Bin .../AppIcon.appiconset/80-1.png | Bin .../Assets.xcassets/AppIcon.appiconset/80.png | Bin .../Assets.xcassets/AppIcon.appiconset/87.png | Bin .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 .../bug.imageset/Contents.json | 0 .../Assets.xcassets/bug.imageset/bug.png | Bin .../logo.imageset/Contents.json | 0 .../square_logo_nontransparent.png | Bin .../square_logo_nontransparent@2x.png | Bin .../square_logo_nontransparent@3x.png | Bin .../mute.imageset/Contents.json | 21 + .../Assets.xcassets/mute.imageset/mute.png | Bin 0 -> 3677 bytes .../unmute.imageset/Contents.json | 21 + .../unmute.imageset/unmute.png | Bin 0 -> 3074 bytes .../Supporting Files/Banners.storyboard | 316 +++++ .../Base.lproj/LaunchScreen.storyboard | 0 .../Base.lproj/Main.storyboard | 1185 ++++++++++++++++ .../Supporting Files/Info.plist | 2 +- .../Supporting Files/Interstitials.storyboard | 312 +++++ .../Supporting Files/Leaders.storyboard | 222 +++ .../Supporting Files/MRECs.storyboard | 196 +++ .../Supporting Files/Rewarded.storyboard | 219 +++ .../Supporting Files/main.m | 0 .../Podfile | 2 +- .../project.pbxproj | 792 +++++++++++ .../ALAppDelegate.swift | 0 .../ALDemoBannerZoneViewController.swift | 145 ++ ...InterfaceBuilderBannerViewController.swift | 112 ++ ...DemoProgrammaticBannerViewController.swift | 145 ++ .../ALEventTrackingViewController.swift | 137 ++ ...titialBasicIntegrationViewController.swift | 86 ++ ...erstitialManualLoadingViewController.swift | 87 ++ ...ALDemoInterstitialZoneViewController.swift | 86 ++ ...InterfaceBuilderLeaderViewController.swift | 112 ++ ...DemoProgrammaticLeaderViewController.swift | 144 ++ ...moInterfaceBuilderMRECViewController.swift | 108 ++ ...ALDemoProgrammaticMRecViewController.swift | 116 ++ ...LDemoNativeAdFeedTableViewController.swift | 94 ++ .../Feed /Carousel UI/ALCarouselCardState.h | 52 + .../Feed /Carousel UI/ALCarouselCardState.m | 34 + .../Carousel UI/ALCarouselRenderingProtocol.h | 26 + .../Feed /Carousel UI/ALCarouselViewModel.h | 32 + .../Feed /Carousel UI/ALCarouselViewModel.m | 175 +++ .../Native/Feed /Carousel UI/ALDebugLog.h | 18 + .../Feed /Carousel UI/ALNativeAdVideoPlayer.h | 30 + .../Feed /Carousel UI/ALNativeAdVideoPlayer.m | 67 + .../Carousel UI/Assets/Star_Sprite_0.5.png | Bin 0 -> 3806 bytes .../Carousel UI/Assets/Star_Sprite_0.png | Bin 0 -> 3797 bytes .../Carousel UI/Assets/Star_Sprite_1.5.png | Bin 0 -> 3784 bytes .../Carousel UI/Assets/Star_Sprite_1.png | Bin 0 -> 3738 bytes .../Carousel UI/Assets/Star_Sprite_2.5.png | Bin 0 -> 3734 bytes .../Carousel UI/Assets/Star_Sprite_2.png | Bin 0 -> 3793 bytes .../Carousel UI/Assets/Star_Sprite_3.5.png | Bin 0 -> 3810 bytes .../Carousel UI/Assets/Star_Sprite_3.png | Bin 0 -> 3808 bytes .../Carousel UI/Assets/Star_Sprite_4.5.png | Bin 0 -> 3749 bytes .../Carousel UI/Assets/Star_Sprite_4.png | Bin 0 -> 3749 bytes .../Carousel UI/Assets/Star_Sprite_5.png | Bin 0 -> 3849 bytes .../Assets/applovin_card_learn_more.png | Bin 0 -> 11806 bytes .../Assets/applovin_card_muted.png | Bin 0 -> 6592 bytes .../Carousel UI/Assets/applovin_card_play.png | Bin 0 -> 10380 bytes .../Assets/applovin_card_replay.png | Bin 0 -> 15399 bytes .../Assets/applovin_card_unmuted.png | Bin 0 -> 5483 bytes .../Categories/ALCarouselView+Internal.h | 20 + .../Categories/UIView+ALActivityIndicator.h | 37 + .../Categories/UIView+ALActivityIndicator.m | 88 ++ .../ALCarouselViewSettings.h | 86 ++ .../Carousel UI/Views/ALCarouselCardView.h | 54 + .../Carousel UI/Views/ALCarouselCardView.m | 354 +++++ .../Carousel UI/Views/ALCarouselMediaView.h | 36 + .../Carousel UI/Views/ALCarouselMediaView.m | 599 ++++++++ .../Views/ALCarouselReplayOverlayView.h | 29 + .../Views/ALCarouselReplayOverlayView.m | 99 ++ .../Feed /Carousel UI/Views/ALCarouselView.h | 34 + .../Feed /Carousel UI/Views/ALCarouselView.m | 643 +++++++++ .../Carousel UI/Views/ALNativeAdVideoView.h | 23 + .../Carousel UI/Views/ALNativeAdVideoView.m | 48 + .../Feed /RSS Feed Parsing/ALDemoArticle.h | 25 + .../Feed /RSS Feed Parsing/ALDemoArticle.m | 13 + .../RSS Feed Parsing/ALDemoRSSFeedRetriever.h | 23 + .../RSS Feed Parsing/ALDemoRSSFeedRetriever.m | 121 ++ ...moNativeAdProgrammaticViewController.swift | 193 +++ .../ALDemoRewardedVideosViewController.swift | 141 ++ ...DemoRewardedVideosZoneViewController.swift | 146 ++ .../ALBaseAdViewController.swift | 12 + .../Base Classes/ALHomeViewController.swift | 179 +++ ...LMAXAutoLayoutBannerAdViewController.swift | 2 +- ...MAXFrameLayoutBannerAdViewController.swift | 2 +- ...terfaceBuilderBannerAdViewController.swift | 2 +- .../ALMAXInterstitialAdViewController.swift | 16 +- .../ALMAXRewardedAdViewController.swift | 16 +- .../AppIcon.appiconset/1024.png | Bin .../AppIcon.appiconset/120-1.png | Bin .../AppIcon.appiconset/120.png | Bin .../AppIcon.appiconset/152.png | Bin .../AppIcon.appiconset/167.png | Bin .../AppIcon.appiconset/180.png | Bin .../Assets.xcassets/AppIcon.appiconset/20.png | Bin .../Assets.xcassets/AppIcon.appiconset/29.png | Bin .../AppIcon.appiconset/40-1.png | Bin .../AppIcon.appiconset/40-2.png | Bin .../Assets.xcassets/AppIcon.appiconset/40.png | Bin .../AppIcon.appiconset/58-1.png | Bin .../Assets.xcassets/AppIcon.appiconset/58.png | Bin .../Assets.xcassets/AppIcon.appiconset/60.png | Bin .../Assets.xcassets/AppIcon.appiconset/76.png | Bin .../AppIcon.appiconset/80-1.png | Bin .../Assets.xcassets/AppIcon.appiconset/80.png | Bin .../Assets.xcassets/AppIcon.appiconset/87.png | Bin .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 .../bug.imageset/Contents.json | 0 .../Assets.xcassets/bug.imageset/bug.png | Bin .../logo.imageset/Contents.json | 0 .../square_logo_nontransparent.png | Bin .../square_logo_nontransparent@2x.png | Bin .../square_logo_nontransparent@3x.png | Bin .../mute.imageset/Contents.json | 21 + .../Assets.xcassets/mute.imageset/mute.png | Bin 0 -> 3677 bytes .../unmute.imageset/Contents.json | 21 + .../unmute.imageset/unmute.png | Bin 0 -> 3074 bytes .../Supporting Files/Banners.storyboard | 316 +++++ .../Base.lproj/LaunchScreen.storyboard | 0 .../Base.lproj/Main.storyboard | 1208 +++++++++++++++++ .../Supporting Files/Info.plist | 2 +- .../Supporting Files/Interstitials.storyboard | 313 +++++ .../Supporting Files/Leaders.storyboard | 222 +++ .../Supporting Files/MRECs.storyboard | 196 +++ .../Supporting Files/Rewarded.storyboard | 219 +++ .../Supporting Files/main.swift | 0 .../AppLovin MAX Demo App-Bridging-Header.h | 11 + .../Podfile | 2 +- .../DemoApp-ObjC.xcodeproj/project.pbxproj | 400 ------ .../DemoApp-ObjC/ALHomeViewController.m | 29 - .../Base.lproj/Main.storyboard | 491 ------- .../DemoApp-Swift.xcodeproj/project.pbxproj | 405 ------ .../DemoApp-Swift/ALHomeViewController.swift | 24 - .../Base.lproj/Main.storyboard | 461 ------- 241 files changed, 16626 insertions(+), 1849 deletions(-) create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC.xcodeproj/project.pbxproj rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/ALAppDelegate.h (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/ALAppDelegate.m (100%) create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Interface Builder/ALDemoInterfaceBuilderBannerViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Interface Builder/ALDemoInterfaceBuilderBannerViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Programmatic/ALDemoProgrammaticBannerViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Programmatic/ALDemoProgrammaticBannerViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Zone Integration/ALDemoBannerZoneViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Zone Integration/ALDemoBannerZoneViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Event Tracking/ALEventTrackingViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Event Tracking/ALEventTrackingViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Basic Integration/ALDemoInterstitialBasicIntegrationViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Basic Integration/ALDemoInterstitialBasicIntegrationViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Manual Loading/ALDemoInterstitialManualLoadingViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Manual Loading/ALDemoInterstitialManualLoadingViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Zone Integration/ALDemoInterstitalZoneViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Zone Integration/ALDemoInterstitalZoneViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Interface Builder/ALDemoInterfaceBuilderLeaderViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Interface Builder/ALDemoInterfaceBuilderLeaderViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Programmatic/ALDemoProgrammaticLeaderViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Programmatic/ALDemoProgrammaticLeaderViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Interface Builder/ALDemoInterfaceBuilderMRECViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Interface Builder/ALDemoInterfaceBuilderMRECViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Programmatic/ALDemoProgrammaticMRECViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Programmatic/ALDemoProgrammaticMRECViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/ALDemoNativeAdFeedTableViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/ALDemoNativeAdFeedTableViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselCardState.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselCardState.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselRenderingProtocol.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselViewModel.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselViewModel.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALNativeAdVideoPlayer.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALNativeAdVideoPlayer.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_0.5.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_0.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_1.5.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_1.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_2.5.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_2.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_3.5.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_3.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_4.5.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_4.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_5.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_learn_more.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_muted.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_play.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_replay.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_unmuted.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Categories/ALCarouselView+Internal.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Categories/UIView+ALActivityIndicator.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Categories/UIView+ALActivityIndicator.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Customizable SETTINGS/ALCarouselViewSettings.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselCardView.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselCardView.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselMediaView.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselMediaView.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselReplayOverlayView.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselReplayOverlayView.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselView.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselView.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALNativeAdVideoView.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALNativeAdVideoView.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoArticle.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoArticle.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoRSSFeedRetriever.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoRSSFeedRetriever.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Programmatic/ALDemoNativeAdProgrammaticViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Programmatic/ALDemoNativeAdProgrammaticViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Basic Integration/ALDemoRewardedVideosViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Basic Integration/ALDemoRewardedVideosViewController.m create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Zone Integration/ALDemoRewardedVideosZoneViewController.h create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Zone Integration/ALDemoRewardedVideosZoneViewController.m rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes}/ALBaseAdViewController.h (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes}/ALBaseAdViewController.m (81%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes}/ALHomeViewController.h (100%) create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes/ALHomeViewController.m rename DemoApp-ObjC/DemoApp-ObjC/Ads/ALAutoLayoutBannerAdViewController.h => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.h (77%) rename DemoApp-ObjC/DemoApp-ObjC/Ads/ALAutoLayoutBannerAdViewController.m => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.m (95%) rename DemoApp-ObjC/DemoApp-ObjC/Ads/ALFrameLayoutBannerAdViewController.h => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.h (77%) rename DemoApp-ObjC/DemoApp-ObjC/Ads/ALFrameLayoutBannerAdViewController.m => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.m (91%) rename DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterfaceBuilderBannerAdViewController.h => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.h (77%) rename DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterfaceBuilderBannerAdViewController.m => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.m (88%) rename DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterstitialAdViewController.h => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Interstitials/ALMAXInterstitialAdViewController.h (78%) rename DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterstitialAdViewController.m => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Interstitials/ALMAXInterstitialAdViewController.m (75%) rename DemoApp-ObjC/DemoApp-ObjC/Ads/ALRewardedAdViewController.h => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Rewarded/ALMAXRewardedAdViewController.h (79%) rename DemoApp-ObjC/DemoApp-ObjC/Ads/ALRewardedAdViewController.m => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Rewarded/ALMAXRewardedAdViewController.m (78%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/Contents.json (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/bug.imageset/Contents.json (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/bug.imageset/bug.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/logo.imageset/Contents.json (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@2x.png (100%) rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@3x.png (100%) create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/mute.imageset/Contents.json create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/mute.imageset/mute.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/unmute.imageset/Contents.json create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/unmute.imageset/unmute.png create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Banners.storyboard rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Base.lproj/LaunchScreen.storyboard (100%) create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Base.lproj/Main.storyboard rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/Info.plist (93%) create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Interstitials.storyboard create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Leaders.storyboard create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/MRECs.storyboard create mode 100644 AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Rewarded.storyboard rename {DemoApp-ObjC/DemoApp-ObjC => AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC}/Supporting Files/main.m (100%) rename {DemoApp-ObjC => AppLovin MAX Demo App - ObjC}/Podfile (60%) create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift.xcodeproj/project.pbxproj rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/ALAppDelegate.swift (100%) create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoBannerZoneViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoInterfaceBuilderBannerViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoProgrammaticBannerViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Event Tracking/ALEventTrackingViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialBasicIntegrationViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialManualLoadingViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialZoneViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Leaders/ALDemoInterfaceBuilderLeaderViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Leaders/ALDemoProgrammaticLeaderViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Mrecs/ALDemoInterfaceBuilderMRECViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Mrecs/ALDemoProgrammaticMRecViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /ALDemoNativeAdFeedTableViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselCardState.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselCardState.m create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselRenderingProtocol.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselViewModel.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselViewModel.m create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALDebugLog.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALNativeAdVideoPlayer.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALNativeAdVideoPlayer.m create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_0.5.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_0.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_1.5.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_1.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_2.5.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_2.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_3.5.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_3.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_4.5.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_4.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_5.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_learn_more.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_muted.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_play.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_replay.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_unmuted.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Categories/ALCarouselView+Internal.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Categories/UIView+ALActivityIndicator.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Categories/UIView+ALActivityIndicator.m create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Customizable SETTINGS/ALCarouselViewSettings.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselCardView.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselCardView.m create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselMediaView.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselMediaView.m create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselReplayOverlayView.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselReplayOverlayView.m create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselView.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselView.m create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALNativeAdVideoView.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALNativeAdVideoView.m create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoArticle.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoArticle.m create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoRSSFeedRetriever.h create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoRSSFeedRetriever.m create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Programattic/ALDemoNativeAdProgrammaticViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Rewarded/ALDemoRewardedVideosViewController.swift create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Rewarded/ALDemoRewardedVideosZoneViewController.swift rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Base Classes}/ALBaseAdViewController.swift (73%) create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Base Classes/ALHomeViewController.swift rename DemoApp-Swift/DemoApp-Swift/Ads/ALAutoLayoutBannerAdViewController.swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.swift (96%) rename DemoApp-Swift/DemoApp-Swift/Ads/ALFrameLayoutBannerAdViewController.swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.swift (95%) rename DemoApp-Swift/DemoApp-Swift/Ads/ALInterfaceBuilderBannerAdViewController.swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.swift (92%) rename DemoApp-Swift/DemoApp-Swift/Ads/ALInterstitialAdViewController.swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Interstitials/ALMAXInterstitialAdViewController.swift (78%) rename DemoApp-Swift/DemoApp-Swift/Ads/ALRewardedAdViewController.swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Rewarded/ALMAXRewardedAdViewController.swift (80%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/Contents.json (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/bug.imageset/Contents.json (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/bug.imageset/bug.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/logo.imageset/Contents.json (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@2x.png (100%) rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@3x.png (100%) create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/mute.imageset/Contents.json create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/mute.imageset/mute.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/unmute.imageset/Contents.json create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/unmute.imageset/unmute.png create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Banners.storyboard rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Base.lproj/LaunchScreen.storyboard (100%) create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Base.lproj/Main.storyboard rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/Info.plist (93%) create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Interstitials.storyboard create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Leaders.storyboard create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/MRECs.storyboard create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Rewarded.storyboard rename {DemoApp-Swift/DemoApp-Swift => AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift}/Supporting Files/main.swift (100%) create mode 100644 AppLovin MAX Demo App - Swift/AppLovin MAX Demo App-Bridging-Header.h rename {DemoApp-Swift => AppLovin MAX Demo App - Swift}/Podfile (60%) delete mode 100644 DemoApp-ObjC/DemoApp-ObjC.xcodeproj/project.pbxproj delete mode 100644 DemoApp-ObjC/DemoApp-ObjC/ALHomeViewController.m delete mode 100644 DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Base.lproj/Main.storyboard delete mode 100644 DemoApp-Swift/DemoApp-Swift.xcodeproj/project.pbxproj delete mode 100644 DemoApp-Swift/DemoApp-Swift/ALHomeViewController.swift delete mode 100644 DemoApp-Swift/DemoApp-Swift/Supporting Files/Base.lproj/Main.storyboard diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC.xcodeproj/project.pbxproj b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..7bab87c595 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC.xcodeproj/project.pbxproj @@ -0,0 +1,902 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 1D992FE6231FA1C400C472F8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1D992FE4231FA1C400C472F8 /* Main.storyboard */; }; + 1D992FE8231FA1C500C472F8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1D992FE7231FA1C500C472F8 /* Assets.xcassets */; }; + 1D992FEB231FA1C500C472F8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1D992FE9231FA1C500C472F8 /* LaunchScreen.storyboard */; }; + 37C7E1EB2328904E002165B5 /* ALMAXAutoLayoutBannerAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D0CB33523204D120076AAAA /* ALMAXAutoLayoutBannerAdViewController.m */; }; + 37C7E1EC2328904E002165B5 /* ALMAXFrameLayoutBannerAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 37C7E1E823285E94002165B5 /* ALMAXFrameLayoutBannerAdViewController.m */; }; + 37C7E1ED2328904E002165B5 /* ALMAXInterstitialAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D0CB32F23204CE70076AAAA /* ALMAXInterstitialAdViewController.m */; }; + 37C7E1EE2328904E002165B5 /* ALMAXRewardedAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D0CB33223204CF70076AAAA /* ALMAXRewardedAdViewController.m */; }; + 37C7E1EF2328904E002165B5 /* ALMAXInterfaceBuilderBannerAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 37C7E1E523285B4E002165B5 /* ALMAXInterfaceBuilderBannerAdViewController.m */; }; + 37C7E1F3232892CF002165B5 /* ALAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D992FDF231FA1C400C472F8 /* ALAppDelegate.m */; }; + 37C7E1F523297423002165B5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D992FED231FA1C500C472F8 /* main.m */; }; + 37C7E1F62329742E002165B5 /* ALHomeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D992FE2231FA1C400C472F8 /* ALHomeViewController.m */; }; + C0DE8BB3234E8A86004B0CFC /* ALBaseAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C0DE8BB2234E8A86004B0CFC /* ALBaseAdViewController.m */; }; + E56784A623F22AB300ACA6C1 /* Rewarded.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E56784A523F22AB300ACA6C1 /* Rewarded.storyboard */; }; + E567854523F22E1300ACA6C1 /* ALDemoRewardedVideosViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E567854123F22E1300ACA6C1 /* ALDemoRewardedVideosViewController.m */; }; + E567854623F22E1300ACA6C1 /* ALDemoRewardedVideosZoneViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E567854223F22E1300ACA6C1 /* ALDemoRewardedVideosZoneViewController.m */; }; + E567854B23F3541100ACA6C1 /* Banners.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E567854A23F3541100ACA6C1 /* Banners.storyboard */; }; + E56785E823F3555A00ACA6C1 /* ALDemoProgrammaticBannerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E56785E623F3555A00ACA6C1 /* ALDemoProgrammaticBannerViewController.m */; }; + E56785EB23F3557100ACA6C1 /* ALDemoInterfaceBuilderBannerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E56785E923F3557100ACA6C1 /* ALDemoInterfaceBuilderBannerViewController.m */; }; + E56785EE23F3557B00ACA6C1 /* ALDemoBannerZoneViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E56785EC23F3557B00ACA6C1 /* ALDemoBannerZoneViewController.m */; }; + E56785F423F35B8F00ACA6C1 /* Leaders.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E56785F323F35B8F00ACA6C1 /* Leaders.storyboard */; }; + E56785F923F35BC200ACA6C1 /* ALDemoInterfaceBuilderLeaderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E56785F723F35BC200ACA6C1 /* ALDemoInterfaceBuilderLeaderViewController.m */; }; + E56785FA23F35BC200ACA6C1 /* ALDemoProgrammaticLeaderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E56785F823F35BC200ACA6C1 /* ALDemoProgrammaticLeaderViewController.m */; }; + E56785FF23F4846000ACA6C1 /* MRECs.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E56785FE23F4846000ACA6C1 /* MRECs.storyboard */; }; + E567860223F484C900ACA6C1 /* ALDemoProgrammaticMRECViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E567860023F484C900ACA6C1 /* ALDemoProgrammaticMRECViewController.m */; }; + E567860623F4899600ACA6C1 /* ALDemoNativeAdProgrammaticViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E567860423F4899600ACA6C1 /* ALDemoNativeAdProgrammaticViewController.m */; }; + E56786B823F48B2000ACA6C1 /* ALCarouselCardView.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786AE23F48B1F00ACA6C1 /* ALCarouselCardView.m */; }; + E56786B923F48B2000ACA6C1 /* ALCarouselReplayOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786AF23F48B1F00ACA6C1 /* ALCarouselReplayOverlayView.m */; }; + E56786BA23F48B2000ACA6C1 /* ALCarouselMediaView.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786B323F48B2000ACA6C1 /* ALCarouselMediaView.m */; }; + E56786BB23F48B2000ACA6C1 /* ALCarouselView.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786B523F48B2000ACA6C1 /* ALCarouselView.m */; }; + E56786BC23F48B2000ACA6C1 /* ALNativeAdVideoView.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786B723F48B2000ACA6C1 /* ALNativeAdVideoView.m */; }; + E56786C223F48B7500ACA6C1 /* UIView+ALActivityIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786C023F48B7400ACA6C1 /* UIView+ALActivityIndicator.m */; }; + E56786CA23F48B8A00ACA6C1 /* ALCarouselCardState.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786C323F48B8900ACA6C1 /* ALCarouselCardState.m */; }; + E56786CB23F48B8A00ACA6C1 /* ALNativeAdVideoPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786C523F48B8900ACA6C1 /* ALNativeAdVideoPlayer.m */; }; + E56786CC23F48B8A00ACA6C1 /* ALCarouselViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786C823F48B8A00ACA6C1 /* ALCarouselViewModel.m */; }; + E56786D123F48C1A00ACA6C1 /* ALDemoArticle.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786CE23F48C1A00ACA6C1 /* ALDemoArticle.m */; }; + E56786D223F48C1A00ACA6C1 /* ALDemoRSSFeedRetriever.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786CF23F48C1A00ACA6C1 /* ALDemoRSSFeedRetriever.m */; }; + E56786D723F48C7400ACA6C1 /* ALDemoNativeAdFeedTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786D623F48C7400ACA6C1 /* ALDemoNativeAdFeedTableViewController.m */; }; + E56786E823F4C24500ACA6C1 /* Star_Sprite_4.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786D823F4C24500ACA6C1 /* Star_Sprite_4.png */; }; + E56786E923F4C24500ACA6C1 /* Star_Sprite_3.5.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786D923F4C24500ACA6C1 /* Star_Sprite_3.5.png */; }; + E56786EA23F4C24500ACA6C1 /* Star_Sprite_1.5.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786DA23F4C24500ACA6C1 /* Star_Sprite_1.5.png */; }; + E56786EB23F4C24500ACA6C1 /* Star_Sprite_5.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786DB23F4C24500ACA6C1 /* Star_Sprite_5.png */; }; + E56786EC23F4C24500ACA6C1 /* Star_Sprite_0.5.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786DC23F4C24500ACA6C1 /* Star_Sprite_0.5.png */; }; + E56786ED23F4C24500ACA6C1 /* Star_Sprite_1.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786DD23F4C24500ACA6C1 /* Star_Sprite_1.png */; }; + E56786EE23F4C24500ACA6C1 /* Star_Sprite_2.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786DE23F4C24500ACA6C1 /* Star_Sprite_2.png */; }; + E56786EF23F4C24500ACA6C1 /* Star_Sprite_4.5.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786DF23F4C24500ACA6C1 /* Star_Sprite_4.5.png */; }; + E56786F023F4C24500ACA6C1 /* applovin_card_muted.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786E023F4C24500ACA6C1 /* applovin_card_muted.png */; }; + E56786F123F4C24500ACA6C1 /* applovin_card_play.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786E123F4C24500ACA6C1 /* applovin_card_play.png */; }; + E56786F223F4C24500ACA6C1 /* Star_Sprite_0.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786E223F4C24500ACA6C1 /* Star_Sprite_0.png */; }; + E56786F323F4C24500ACA6C1 /* applovin_card_replay.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786E323F4C24500ACA6C1 /* applovin_card_replay.png */; }; + E56786F423F4C24500ACA6C1 /* Star_Sprite_2.5.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786E423F4C24500ACA6C1 /* Star_Sprite_2.5.png */; }; + E56786F523F4C24500ACA6C1 /* Star_Sprite_3.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786E523F4C24500ACA6C1 /* Star_Sprite_3.png */; }; + E56786F623F4C24500ACA6C1 /* applovin_card_learn_more.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786E623F4C24500ACA6C1 /* applovin_card_learn_more.png */; }; + E56786F723F4C24500ACA6C1 /* applovin_card_unmuted.png in Resources */ = {isa = PBXBuildFile; fileRef = E56786E723F4C24500ACA6C1 /* applovin_card_unmuted.png */; }; + E56786FA23F4CAE100ACA6C1 /* ALEventTrackingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E56786F823F4CAE000ACA6C1 /* ALEventTrackingViewController.m */; }; + E57306F123EB90D500D972F4 /* Interstitials.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E57306F023EB90D500D972F4 /* Interstitials.storyboard */; }; + E573083123EB96E400D972F4 /* ALDemoInterstitialBasicIntegrationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E573083023EB96E400D972F4 /* ALDemoInterstitialBasicIntegrationViewController.m */; }; + E573083423EB970400D972F4 /* ALDemoInterstitialManualLoadingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E573083323EB970400D972F4 /* ALDemoInterstitialManualLoadingViewController.m */; }; + E573083823EB972C00D972F4 /* ALDemoInterstitalZoneViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E573083723EB972C00D972F4 /* ALDemoInterstitalZoneViewController.m */; }; + E5C8F4C724119F0F00592BF8 /* ALDemoInterfaceBuilderMRECViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E5C8F4C624119F0F00592BF8 /* ALDemoInterfaceBuilderMRECViewController.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D0CB32E23204CE70076AAAA /* ALMAXInterstitialAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALMAXInterstitialAdViewController.h; sourceTree = ""; }; + 1D0CB32F23204CE70076AAAA /* ALMAXInterstitialAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALMAXInterstitialAdViewController.m; sourceTree = ""; }; + 1D0CB33123204CF70076AAAA /* ALMAXRewardedAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALMAXRewardedAdViewController.h; sourceTree = ""; }; + 1D0CB33223204CF70076AAAA /* ALMAXRewardedAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALMAXRewardedAdViewController.m; sourceTree = ""; }; + 1D0CB33423204D120076AAAA /* ALMAXAutoLayoutBannerAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALMAXAutoLayoutBannerAdViewController.h; sourceTree = ""; }; + 1D0CB33523204D120076AAAA /* ALMAXAutoLayoutBannerAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALMAXAutoLayoutBannerAdViewController.m; sourceTree = ""; }; + 1D992FDB231FA1C300C472F8 /* AppLovin MAX Demo App - ObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AppLovin MAX Demo App - ObjC.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D992FDE231FA1C300C472F8 /* ALAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALAppDelegate.h; sourceTree = ""; }; + 1D992FDF231FA1C400C472F8 /* ALAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALAppDelegate.m; sourceTree = ""; }; + 1D992FE1231FA1C400C472F8 /* ALHomeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALHomeViewController.h; sourceTree = ""; }; + 1D992FE2231FA1C400C472F8 /* ALHomeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALHomeViewController.m; sourceTree = ""; }; + 1D992FE5231FA1C400C472F8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 1D992FE7231FA1C500C472F8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 1D992FEA231FA1C500C472F8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 1D992FEC231FA1C500C472F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1D992FED231FA1C500C472F8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 37C7E1E523285B4E002165B5 /* ALMAXInterfaceBuilderBannerAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALMAXInterfaceBuilderBannerAdViewController.m; sourceTree = ""; }; + 37C7E1E723285B6A002165B5 /* ALMAXInterfaceBuilderBannerAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALMAXInterfaceBuilderBannerAdViewController.h; sourceTree = ""; }; + 37C7E1E823285E94002165B5 /* ALMAXFrameLayoutBannerAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALMAXFrameLayoutBannerAdViewController.m; sourceTree = ""; }; + 37C7E1EA23285EDF002165B5 /* ALMAXFrameLayoutBannerAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALMAXFrameLayoutBannerAdViewController.h; sourceTree = ""; }; + C0DE8BB1234E8A86004B0CFC /* ALBaseAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALBaseAdViewController.h; sourceTree = ""; }; + C0DE8BB2234E8A86004B0CFC /* ALBaseAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALBaseAdViewController.m; sourceTree = ""; }; + E56784A523F22AB300ACA6C1 /* Rewarded.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Rewarded.storyboard; sourceTree = ""; }; + E567854123F22E1300ACA6C1 /* ALDemoRewardedVideosViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoRewardedVideosViewController.m; sourceTree = ""; }; + E567854223F22E1300ACA6C1 /* ALDemoRewardedVideosZoneViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoRewardedVideosZoneViewController.m; sourceTree = ""; }; + E567854323F22E1300ACA6C1 /* ALDemoRewardedVideosZoneViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoRewardedVideosZoneViewController.h; sourceTree = ""; }; + E567854423F22E1300ACA6C1 /* ALDemoRewardedVideosViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoRewardedVideosViewController.h; sourceTree = ""; }; + E567854A23F3541100ACA6C1 /* Banners.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Banners.storyboard; sourceTree = ""; }; + E56785E623F3555A00ACA6C1 /* ALDemoProgrammaticBannerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoProgrammaticBannerViewController.m; sourceTree = ""; }; + E56785E723F3555A00ACA6C1 /* ALDemoProgrammaticBannerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoProgrammaticBannerViewController.h; sourceTree = ""; }; + E56785E923F3557100ACA6C1 /* ALDemoInterfaceBuilderBannerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoInterfaceBuilderBannerViewController.m; sourceTree = ""; }; + E56785EA23F3557100ACA6C1 /* ALDemoInterfaceBuilderBannerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoInterfaceBuilderBannerViewController.h; sourceTree = ""; }; + E56785EC23F3557B00ACA6C1 /* ALDemoBannerZoneViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoBannerZoneViewController.m; sourceTree = ""; }; + E56785ED23F3557B00ACA6C1 /* ALDemoBannerZoneViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoBannerZoneViewController.h; sourceTree = ""; }; + E56785F323F35B8F00ACA6C1 /* Leaders.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Leaders.storyboard; sourceTree = ""; }; + E56785F523F35BC200ACA6C1 /* ALDemoProgrammaticLeaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoProgrammaticLeaderViewController.h; sourceTree = ""; }; + E56785F623F35BC200ACA6C1 /* ALDemoInterfaceBuilderLeaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoInterfaceBuilderLeaderViewController.h; sourceTree = ""; }; + E56785F723F35BC200ACA6C1 /* ALDemoInterfaceBuilderLeaderViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoInterfaceBuilderLeaderViewController.m; sourceTree = ""; }; + E56785F823F35BC200ACA6C1 /* ALDemoProgrammaticLeaderViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoProgrammaticLeaderViewController.m; sourceTree = ""; }; + E56785FE23F4846000ACA6C1 /* MRECs.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MRECs.storyboard; sourceTree = ""; }; + E567860023F484C900ACA6C1 /* ALDemoProgrammaticMRECViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoProgrammaticMRECViewController.m; sourceTree = ""; }; + E567860123F484C900ACA6C1 /* ALDemoProgrammaticMRECViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoProgrammaticMRECViewController.h; sourceTree = ""; }; + E567860423F4899600ACA6C1 /* ALDemoNativeAdProgrammaticViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoNativeAdProgrammaticViewController.m; sourceTree = ""; }; + E567860523F4899600ACA6C1 /* ALDemoNativeAdProgrammaticViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoNativeAdProgrammaticViewController.h; sourceTree = ""; }; + E567860923F48A1A00ACA6C1 /* ALCarouselViewSettings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALCarouselViewSettings.h; sourceTree = ""; }; + E56786AE23F48B1F00ACA6C1 /* ALCarouselCardView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselCardView.m; sourceTree = ""; }; + E56786AF23F48B1F00ACA6C1 /* ALCarouselReplayOverlayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselReplayOverlayView.m; sourceTree = ""; }; + E56786B023F48B1F00ACA6C1 /* ALCarouselCardView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselCardView.h; sourceTree = ""; }; + E56786B123F48B2000ACA6C1 /* ALNativeAdVideoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALNativeAdVideoView.h; sourceTree = ""; }; + E56786B223F48B2000ACA6C1 /* ALCarouselMediaView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselMediaView.h; sourceTree = ""; }; + E56786B323F48B2000ACA6C1 /* ALCarouselMediaView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselMediaView.m; sourceTree = ""; }; + E56786B423F48B2000ACA6C1 /* ALCarouselView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselView.h; sourceTree = ""; }; + E56786B523F48B2000ACA6C1 /* ALCarouselView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselView.m; sourceTree = ""; }; + E56786B623F48B2000ACA6C1 /* ALCarouselReplayOverlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselReplayOverlayView.h; sourceTree = ""; }; + E56786B723F48B2000ACA6C1 /* ALNativeAdVideoView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALNativeAdVideoView.m; sourceTree = ""; }; + E56786BF23F48B7400ACA6C1 /* UIView+ALActivityIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+ALActivityIndicator.h"; sourceTree = ""; }; + E56786C023F48B7400ACA6C1 /* UIView+ALActivityIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+ALActivityIndicator.m"; sourceTree = ""; }; + E56786C123F48B7500ACA6C1 /* ALCarouselView+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ALCarouselView+Internal.h"; sourceTree = ""; }; + E56786C323F48B8900ACA6C1 /* ALCarouselCardState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselCardState.m; sourceTree = ""; }; + E56786C423F48B8900ACA6C1 /* ALNativeAdVideoPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALNativeAdVideoPlayer.h; sourceTree = ""; }; + E56786C523F48B8900ACA6C1 /* ALNativeAdVideoPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALNativeAdVideoPlayer.m; sourceTree = ""; }; + E56786C623F48B8900ACA6C1 /* ALCarouselCardState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselCardState.h; sourceTree = ""; }; + E56786C723F48B8A00ACA6C1 /* ALCarouselViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselViewModel.h; sourceTree = ""; }; + E56786C823F48B8A00ACA6C1 /* ALCarouselViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselViewModel.m; sourceTree = ""; }; + E56786C923F48B8A00ACA6C1 /* ALCarouselRenderingProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselRenderingProtocol.h; sourceTree = ""; }; + E56786CD23F48C1A00ACA6C1 /* ALDemoRSSFeedRetriever.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoRSSFeedRetriever.h; sourceTree = ""; }; + E56786CE23F48C1A00ACA6C1 /* ALDemoArticle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoArticle.m; sourceTree = ""; }; + E56786CF23F48C1A00ACA6C1 /* ALDemoRSSFeedRetriever.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoRSSFeedRetriever.m; sourceTree = ""; }; + E56786D023F48C1A00ACA6C1 /* ALDemoArticle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoArticle.h; sourceTree = ""; }; + E56786D523F48C7400ACA6C1 /* ALDemoNativeAdFeedTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoNativeAdFeedTableViewController.h; sourceTree = ""; }; + E56786D623F48C7400ACA6C1 /* ALDemoNativeAdFeedTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoNativeAdFeedTableViewController.m; sourceTree = ""; }; + E56786D823F4C24500ACA6C1 /* Star_Sprite_4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_4.png; sourceTree = ""; }; + E56786D923F4C24500ACA6C1 /* Star_Sprite_3.5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_3.5.png; sourceTree = ""; }; + E56786DA23F4C24500ACA6C1 /* Star_Sprite_1.5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_1.5.png; sourceTree = ""; }; + E56786DB23F4C24500ACA6C1 /* Star_Sprite_5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_5.png; sourceTree = ""; }; + E56786DC23F4C24500ACA6C1 /* Star_Sprite_0.5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_0.5.png; sourceTree = ""; }; + E56786DD23F4C24500ACA6C1 /* Star_Sprite_1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_1.png; sourceTree = ""; }; + E56786DE23F4C24500ACA6C1 /* Star_Sprite_2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_2.png; sourceTree = ""; }; + E56786DF23F4C24500ACA6C1 /* Star_Sprite_4.5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_4.5.png; sourceTree = ""; }; + E56786E023F4C24500ACA6C1 /* applovin_card_muted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = applovin_card_muted.png; sourceTree = ""; }; + E56786E123F4C24500ACA6C1 /* applovin_card_play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = applovin_card_play.png; sourceTree = ""; }; + E56786E223F4C24500ACA6C1 /* Star_Sprite_0.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_0.png; sourceTree = ""; }; + E56786E323F4C24500ACA6C1 /* applovin_card_replay.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = applovin_card_replay.png; sourceTree = ""; }; + E56786E423F4C24500ACA6C1 /* Star_Sprite_2.5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_2.5.png; sourceTree = ""; }; + E56786E523F4C24500ACA6C1 /* Star_Sprite_3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_3.png; sourceTree = ""; }; + E56786E623F4C24500ACA6C1 /* applovin_card_learn_more.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = applovin_card_learn_more.png; sourceTree = ""; }; + E56786E723F4C24500ACA6C1 /* applovin_card_unmuted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = applovin_card_unmuted.png; sourceTree = ""; }; + E56786F823F4CAE000ACA6C1 /* ALEventTrackingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALEventTrackingViewController.m; sourceTree = ""; }; + E56786F923F4CAE000ACA6C1 /* ALEventTrackingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALEventTrackingViewController.h; sourceTree = ""; }; + E57306F023EB90D500D972F4 /* Interstitials.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Interstitials.storyboard; sourceTree = ""; }; + E573082F23EB96E400D972F4 /* ALDemoInterstitialBasicIntegrationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoInterstitialBasicIntegrationViewController.h; sourceTree = ""; }; + E573083023EB96E400D972F4 /* ALDemoInterstitialBasicIntegrationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoInterstitialBasicIntegrationViewController.m; sourceTree = ""; }; + E573083223EB970400D972F4 /* ALDemoInterstitialManualLoadingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoInterstitialManualLoadingViewController.h; sourceTree = ""; }; + E573083323EB970400D972F4 /* ALDemoInterstitialManualLoadingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoInterstitialManualLoadingViewController.m; sourceTree = ""; }; + E573083623EB972C00D972F4 /* ALDemoInterstitalZoneViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoInterstitalZoneViewController.h; sourceTree = ""; }; + E573083723EB972C00D972F4 /* ALDemoInterstitalZoneViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoInterstitalZoneViewController.m; sourceTree = ""; }; + E5C8F4C524119F0F00592BF8 /* ALDemoInterfaceBuilderMRECViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALDemoInterfaceBuilderMRECViewController.h; sourceTree = ""; }; + E5C8F4C624119F0F00592BF8 /* ALDemoInterfaceBuilderMRECViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALDemoInterfaceBuilderMRECViewController.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D992FD8231FA1C300C472F8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1D0CB33823204EBD0076AAAA /* MAX */ = { + isa = PBXGroup; + children = ( + 1D629D5D240F4E6600FE6F5F /* Banners */, + 1D629D5E240F4E7200FE6F5F /* Interstitials */, + 1D629D5F240F4E7500FE6F5F /* Rewarded */, + ); + path = MAX; + sourceTree = ""; + }; + 1D629D5D240F4E6600FE6F5F /* Banners */ = { + isa = PBXGroup; + children = ( + 1D0CB33423204D120076AAAA /* ALMAXAutoLayoutBannerAdViewController.h */, + 1D0CB33523204D120076AAAA /* ALMAXAutoLayoutBannerAdViewController.m */, + 37C7E1EA23285EDF002165B5 /* ALMAXFrameLayoutBannerAdViewController.h */, + 37C7E1E823285E94002165B5 /* ALMAXFrameLayoutBannerAdViewController.m */, + 37C7E1E723285B6A002165B5 /* ALMAXInterfaceBuilderBannerAdViewController.h */, + 37C7E1E523285B4E002165B5 /* ALMAXInterfaceBuilderBannerAdViewController.m */, + ); + path = Banners; + sourceTree = ""; + }; + 1D629D5E240F4E7200FE6F5F /* Interstitials */ = { + isa = PBXGroup; + children = ( + 1D0CB32E23204CE70076AAAA /* ALMAXInterstitialAdViewController.h */, + 1D0CB32F23204CE70076AAAA /* ALMAXInterstitialAdViewController.m */, + ); + path = Interstitials; + sourceTree = ""; + }; + 1D629D5F240F4E7500FE6F5F /* Rewarded */ = { + isa = PBXGroup; + children = ( + 1D0CB33123204CF70076AAAA /* ALMAXRewardedAdViewController.h */, + 1D0CB33223204CF70076AAAA /* ALMAXRewardedAdViewController.m */, + ); + path = Rewarded; + sourceTree = ""; + }; + 1D629D60240F4E8A00FE6F5F /* Base Classes */ = { + isa = PBXGroup; + children = ( + C0DE8BB1234E8A86004B0CFC /* ALBaseAdViewController.h */, + C0DE8BB2234E8A86004B0CFC /* ALBaseAdViewController.m */, + 1D992FE1231FA1C400C472F8 /* ALHomeViewController.h */, + 1D992FE2231FA1C400C472F8 /* ALHomeViewController.m */, + ); + path = "Base Classes"; + sourceTree = ""; + }; + 1D992FD2231FA1C300C472F8 = { + isa = PBXGroup; + children = ( + 1D992FDD231FA1C300C472F8 /* AppLovin MAX Demo App - ObjC */, + 1D992FDC231FA1C300C472F8 /* Products */, + ); + sourceTree = ""; + }; + 1D992FDC231FA1C300C472F8 /* Products */ = { + isa = PBXGroup; + children = ( + 1D992FDB231FA1C300C472F8 /* AppLovin MAX Demo App - ObjC.app */, + ); + name = Products; + sourceTree = ""; + }; + 1D992FDD231FA1C300C472F8 /* AppLovin MAX Demo App - ObjC */ = { + isa = PBXGroup; + children = ( + 1D992FDE231FA1C300C472F8 /* ALAppDelegate.h */, + 1D992FDF231FA1C400C472F8 /* ALAppDelegate.m */, + 1D629D60240F4E8A00FE6F5F /* Base Classes */, + E57306F223EB91B400D972F4 /* AppLovin */, + 1D0CB33823204EBD0076AAAA /* MAX */, + 1D993015231FA39000C472F8 /* Supporting Files */, + ); + path = "AppLovin MAX Demo App - ObjC"; + sourceTree = ""; + }; + 1D993015231FA39000C472F8 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 1D992FED231FA1C500C472F8 /* main.m */, + 1D992FEC231FA1C500C472F8 /* Info.plist */, + 1D992FE7231FA1C500C472F8 /* Assets.xcassets */, + 1D992FE9231FA1C500C472F8 /* LaunchScreen.storyboard */, + 1D992FE4231FA1C400C472F8 /* Main.storyboard */, + E57306F023EB90D500D972F4 /* Interstitials.storyboard */, + E56784A523F22AB300ACA6C1 /* Rewarded.storyboard */, + E567854A23F3541100ACA6C1 /* Banners.storyboard */, + E56785F323F35B8F00ACA6C1 /* Leaders.storyboard */, + E56785FE23F4846000ACA6C1 /* MRECs.storyboard */, + ); + path = "Supporting Files"; + sourceTree = ""; + }; + E567854723F22E1E00ACA6C1 /* Rewarded */ = { + isa = PBXGroup; + children = ( + E567854823F3401700ACA6C1 /* Basic Integration */, + E567854923F3403500ACA6C1 /* Zone Integration */, + ); + path = Rewarded; + sourceTree = ""; + }; + E567854823F3401700ACA6C1 /* Basic Integration */ = { + isa = PBXGroup; + children = ( + E567854423F22E1300ACA6C1 /* ALDemoRewardedVideosViewController.h */, + E567854123F22E1300ACA6C1 /* ALDemoRewardedVideosViewController.m */, + ); + path = "Basic Integration"; + sourceTree = ""; + }; + E567854923F3403500ACA6C1 /* Zone Integration */ = { + isa = PBXGroup; + children = ( + E567854323F22E1300ACA6C1 /* ALDemoRewardedVideosZoneViewController.h */, + E567854223F22E1300ACA6C1 /* ALDemoRewardedVideosZoneViewController.m */, + ); + path = "Zone Integration"; + sourceTree = ""; + }; + E56785EF23F3558500ACA6C1 /* Banners */ = { + isa = PBXGroup; + children = ( + E56785F123F355BD00ACA6C1 /* Programmatic */, + E56785F023F355A800ACA6C1 /* Interface Builder */, + E56785F223F355CD00ACA6C1 /* Zone Integration */, + ); + path = Banners; + sourceTree = ""; + }; + E56785F023F355A800ACA6C1 /* Interface Builder */ = { + isa = PBXGroup; + children = ( + E56785EA23F3557100ACA6C1 /* ALDemoInterfaceBuilderBannerViewController.h */, + E56785E923F3557100ACA6C1 /* ALDemoInterfaceBuilderBannerViewController.m */, + ); + path = "Interface Builder"; + sourceTree = ""; + }; + E56785F123F355BD00ACA6C1 /* Programmatic */ = { + isa = PBXGroup; + children = ( + E56785E723F3555A00ACA6C1 /* ALDemoProgrammaticBannerViewController.h */, + E56785E623F3555A00ACA6C1 /* ALDemoProgrammaticBannerViewController.m */, + ); + path = Programmatic; + sourceTree = ""; + }; + E56785F223F355CD00ACA6C1 /* Zone Integration */ = { + isa = PBXGroup; + children = ( + E56785ED23F3557B00ACA6C1 /* ALDemoBannerZoneViewController.h */, + E56785EC23F3557B00ACA6C1 /* ALDemoBannerZoneViewController.m */, + ); + path = "Zone Integration"; + sourceTree = ""; + }; + E56785FB23F35BCA00ACA6C1 /* Leaders */ = { + isa = PBXGroup; + children = ( + E56785FC23F35BD700ACA6C1 /* Interface Builder */, + E56785FD23F35BEA00ACA6C1 /* Programmatic */, + ); + path = Leaders; + sourceTree = ""; + }; + E56785FC23F35BD700ACA6C1 /* Interface Builder */ = { + isa = PBXGroup; + children = ( + E56785F623F35BC200ACA6C1 /* ALDemoInterfaceBuilderLeaderViewController.h */, + E56785F723F35BC200ACA6C1 /* ALDemoInterfaceBuilderLeaderViewController.m */, + ); + path = "Interface Builder"; + sourceTree = ""; + }; + E56785FD23F35BEA00ACA6C1 /* Programmatic */ = { + isa = PBXGroup; + children = ( + E56785F523F35BC200ACA6C1 /* ALDemoProgrammaticLeaderViewController.h */, + E56785F823F35BC200ACA6C1 /* ALDemoProgrammaticLeaderViewController.m */, + ); + path = Programmatic; + sourceTree = ""; + }; + E567860323F484D200ACA6C1 /* MRECs */ = { + isa = PBXGroup; + children = ( + E5C8F4C82411B17B00592BF8 /* Programmatic */, + E5C8F4C92411B18B00592BF8 /* Interface Builder */, + ); + path = MRECs; + sourceTree = ""; + }; + E567860723F489C100ACA6C1 /* Native Ads */ = { + isa = PBXGroup; + children = ( + E567860823F489DF00ACA6C1 /* Programmatic */, + E56786D423F48C5E00ACA6C1 /* Feed */, + ); + path = "Native Ads"; + sourceTree = ""; + }; + E567860823F489DF00ACA6C1 /* Programmatic */ = { + isa = PBXGroup; + children = ( + E567860523F4899600ACA6C1 /* ALDemoNativeAdProgrammaticViewController.h */, + E567860423F4899600ACA6C1 /* ALDemoNativeAdProgrammaticViewController.m */, + ); + path = Programmatic; + sourceTree = ""; + }; + E567860A23F48A2B00ACA6C1 /* Carousel UI */ = { + isa = PBXGroup; + children = ( + E56786BD23F48B2A00ACA6C1 /* Views */, + E56786BE23F48B6600ACA6C1 /* Categories */, + E567866D23F48AC800ACA6C1 /* Assets */, + E567860B23F48A3400ACA6C1 /* Customizable SETTINGS */, + E56786C623F48B8900ACA6C1 /* ALCarouselCardState.h */, + E56786C323F48B8900ACA6C1 /* ALCarouselCardState.m */, + E56786C923F48B8A00ACA6C1 /* ALCarouselRenderingProtocol.h */, + E56786C723F48B8A00ACA6C1 /* ALCarouselViewModel.h */, + E56786C823F48B8A00ACA6C1 /* ALCarouselViewModel.m */, + E56786C423F48B8900ACA6C1 /* ALNativeAdVideoPlayer.h */, + E56786C523F48B8900ACA6C1 /* ALNativeAdVideoPlayer.m */, + ); + path = "Carousel UI"; + sourceTree = ""; + }; + E567860B23F48A3400ACA6C1 /* Customizable SETTINGS */ = { + isa = PBXGroup; + children = ( + E567860923F48A1A00ACA6C1 /* ALCarouselViewSettings.h */, + ); + path = "Customizable SETTINGS"; + sourceTree = ""; + }; + E567866D23F48AC800ACA6C1 /* Assets */ = { + isa = PBXGroup; + children = ( + E56786E623F4C24500ACA6C1 /* applovin_card_learn_more.png */, + E56786E023F4C24500ACA6C1 /* applovin_card_muted.png */, + E56786E123F4C24500ACA6C1 /* applovin_card_play.png */, + E56786E323F4C24500ACA6C1 /* applovin_card_replay.png */, + E56786E723F4C24500ACA6C1 /* applovin_card_unmuted.png */, + E56786DC23F4C24500ACA6C1 /* Star_Sprite_0.5.png */, + E56786E223F4C24500ACA6C1 /* Star_Sprite_0.png */, + E56786DA23F4C24500ACA6C1 /* Star_Sprite_1.5.png */, + E56786DD23F4C24500ACA6C1 /* Star_Sprite_1.png */, + E56786E423F4C24500ACA6C1 /* Star_Sprite_2.5.png */, + E56786DE23F4C24500ACA6C1 /* Star_Sprite_2.png */, + E56786D923F4C24500ACA6C1 /* Star_Sprite_3.5.png */, + E56786E523F4C24500ACA6C1 /* Star_Sprite_3.png */, + E56786DF23F4C24500ACA6C1 /* Star_Sprite_4.5.png */, + E56786D823F4C24500ACA6C1 /* Star_Sprite_4.png */, + E56786DB23F4C24500ACA6C1 /* Star_Sprite_5.png */, + ); + path = Assets; + sourceTree = ""; + }; + E56786BD23F48B2A00ACA6C1 /* Views */ = { + isa = PBXGroup; + children = ( + E56786B023F48B1F00ACA6C1 /* ALCarouselCardView.h */, + E56786AE23F48B1F00ACA6C1 /* ALCarouselCardView.m */, + E56786B223F48B2000ACA6C1 /* ALCarouselMediaView.h */, + E56786B323F48B2000ACA6C1 /* ALCarouselMediaView.m */, + E56786B623F48B2000ACA6C1 /* ALCarouselReplayOverlayView.h */, + E56786AF23F48B1F00ACA6C1 /* ALCarouselReplayOverlayView.m */, + E56786B423F48B2000ACA6C1 /* ALCarouselView.h */, + E56786B523F48B2000ACA6C1 /* ALCarouselView.m */, + E56786B123F48B2000ACA6C1 /* ALNativeAdVideoView.h */, + E56786B723F48B2000ACA6C1 /* ALNativeAdVideoView.m */, + ); + path = Views; + sourceTree = ""; + }; + E56786BE23F48B6600ACA6C1 /* Categories */ = { + isa = PBXGroup; + children = ( + E56786C123F48B7500ACA6C1 /* ALCarouselView+Internal.h */, + E56786BF23F48B7400ACA6C1 /* UIView+ALActivityIndicator.h */, + E56786C023F48B7400ACA6C1 /* UIView+ALActivityIndicator.m */, + ); + path = Categories; + sourceTree = ""; + }; + E56786D323F48C2200ACA6C1 /* RSS Feed Parsing */ = { + isa = PBXGroup; + children = ( + E56786D023F48C1A00ACA6C1 /* ALDemoArticle.h */, + E56786CE23F48C1A00ACA6C1 /* ALDemoArticle.m */, + E56786CD23F48C1A00ACA6C1 /* ALDemoRSSFeedRetriever.h */, + E56786CF23F48C1A00ACA6C1 /* ALDemoRSSFeedRetriever.m */, + ); + path = "RSS Feed Parsing"; + sourceTree = ""; + }; + E56786D423F48C5E00ACA6C1 /* Feed */ = { + isa = PBXGroup; + children = ( + E567860A23F48A2B00ACA6C1 /* Carousel UI */, + E56786D323F48C2200ACA6C1 /* RSS Feed Parsing */, + E56786D523F48C7400ACA6C1 /* ALDemoNativeAdFeedTableViewController.h */, + E56786D623F48C7400ACA6C1 /* ALDemoNativeAdFeedTableViewController.m */, + ); + path = Feed; + sourceTree = ""; + }; + E56786FB23F4CAE700ACA6C1 /* Event Tracking */ = { + isa = PBXGroup; + children = ( + E56786F923F4CAE000ACA6C1 /* ALEventTrackingViewController.h */, + E56786F823F4CAE000ACA6C1 /* ALEventTrackingViewController.m */, + ); + path = "Event Tracking"; + sourceTree = ""; + }; + E57306F223EB91B400D972F4 /* AppLovin */ = { + isa = PBXGroup; + children = ( + E56785EF23F3558500ACA6C1 /* Banners */, + E56786FB23F4CAE700ACA6C1 /* Event Tracking */, + E573083A23EB975500D972F4 /* Interstitials */, + E56785FB23F35BCA00ACA6C1 /* Leaders */, + E567860323F484D200ACA6C1 /* MRECs */, + E567860723F489C100ACA6C1 /* Native Ads */, + E567854723F22E1E00ACA6C1 /* Rewarded */, + ); + path = AppLovin; + sourceTree = ""; + }; + E573082C23EB967600D972F4 /* Basic Integration */ = { + isa = PBXGroup; + children = ( + E573082F23EB96E400D972F4 /* ALDemoInterstitialBasicIntegrationViewController.h */, + E573083023EB96E400D972F4 /* ALDemoInterstitialBasicIntegrationViewController.m */, + ); + path = "Basic Integration"; + sourceTree = ""; + }; + E573083523EB970A00D972F4 /* Manual Loading */ = { + isa = PBXGroup; + children = ( + E573083223EB970400D972F4 /* ALDemoInterstitialManualLoadingViewController.h */, + E573083323EB970400D972F4 /* ALDemoInterstitialManualLoadingViewController.m */, + ); + path = "Manual Loading"; + sourceTree = ""; + }; + E573083923EB973200D972F4 /* Zone Integration */ = { + isa = PBXGroup; + children = ( + E573083623EB972C00D972F4 /* ALDemoInterstitalZoneViewController.h */, + E573083723EB972C00D972F4 /* ALDemoInterstitalZoneViewController.m */, + ); + path = "Zone Integration"; + sourceTree = ""; + }; + E573083A23EB975500D972F4 /* Interstitials */ = { + isa = PBXGroup; + children = ( + E573082C23EB967600D972F4 /* Basic Integration */, + E573083523EB970A00D972F4 /* Manual Loading */, + E573083923EB973200D972F4 /* Zone Integration */, + ); + path = Interstitials; + sourceTree = ""; + }; + E5C8F4C82411B17B00592BF8 /* Programmatic */ = { + isa = PBXGroup; + children = ( + E567860123F484C900ACA6C1 /* ALDemoProgrammaticMRECViewController.h */, + E567860023F484C900ACA6C1 /* ALDemoProgrammaticMRECViewController.m */, + ); + path = Programmatic; + sourceTree = ""; + }; + E5C8F4C92411B18B00592BF8 /* Interface Builder */ = { + isa = PBXGroup; + children = ( + E5C8F4C524119F0F00592BF8 /* ALDemoInterfaceBuilderMRECViewController.h */, + E5C8F4C624119F0F00592BF8 /* ALDemoInterfaceBuilderMRECViewController.m */, + ); + path = "Interface Builder"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D992FDA231FA1C300C472F8 /* AppLovin MAX Demo App - ObjC */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D992FF1231FA1C500C472F8 /* Build configuration list for PBXNativeTarget "AppLovin MAX Demo App - ObjC" */; + buildPhases = ( + 1D992FD7231FA1C300C472F8 /* Sources */, + 1D992FD8231FA1C300C472F8 /* Frameworks */, + 1D992FD9231FA1C300C472F8 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "AppLovin MAX Demo App - ObjC"; + productName = "DemoApp-ObjC"; + productReference = 1D992FDB231FA1C300C472F8 /* AppLovin MAX Demo App - ObjC.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1D992FD3231FA1C300C472F8 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1030; + ORGANIZATIONNAME = "AppLovin Corporation"; + TargetAttributes = { + 1D992FDA231FA1C300C472F8 = { + CreatedOnToolsVersion = 10.3; + }; + }; + }; + buildConfigurationList = 1D992FD6231FA1C300C472F8 /* Build configuration list for PBXProject "AppLovin MAX Demo App - ObjC" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 1D992FD2231FA1C300C472F8; + productRefGroup = 1D992FDC231FA1C300C472F8 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D992FDA231FA1C300C472F8 /* AppLovin MAX Demo App - ObjC */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D992FD9231FA1C300C472F8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E56786EC23F4C24500ACA6C1 /* Star_Sprite_0.5.png in Resources */, + E56786F623F4C24500ACA6C1 /* applovin_card_learn_more.png in Resources */, + 1D992FEB231FA1C500C472F8 /* LaunchScreen.storyboard in Resources */, + E56785FF23F4846000ACA6C1 /* MRECs.storyboard in Resources */, + E56786F223F4C24500ACA6C1 /* Star_Sprite_0.png in Resources */, + E56785F423F35B8F00ACA6C1 /* Leaders.storyboard in Resources */, + E56786F123F4C24500ACA6C1 /* applovin_card_play.png in Resources */, + 1D992FE8231FA1C500C472F8 /* Assets.xcassets in Resources */, + E56786EB23F4C24500ACA6C1 /* Star_Sprite_5.png in Resources */, + E56784A623F22AB300ACA6C1 /* Rewarded.storyboard in Resources */, + E56786F323F4C24500ACA6C1 /* applovin_card_replay.png in Resources */, + E56786F523F4C24500ACA6C1 /* Star_Sprite_3.png in Resources */, + E56786EA23F4C24500ACA6C1 /* Star_Sprite_1.5.png in Resources */, + E56786ED23F4C24500ACA6C1 /* Star_Sprite_1.png in Resources */, + E57306F123EB90D500D972F4 /* Interstitials.storyboard in Resources */, + E56786EE23F4C24500ACA6C1 /* Star_Sprite_2.png in Resources */, + E567854B23F3541100ACA6C1 /* Banners.storyboard in Resources */, + E56786F423F4C24500ACA6C1 /* Star_Sprite_2.5.png in Resources */, + E56786EF23F4C24500ACA6C1 /* Star_Sprite_4.5.png in Resources */, + E56786E823F4C24500ACA6C1 /* Star_Sprite_4.png in Resources */, + 1D992FE6231FA1C400C472F8 /* Main.storyboard in Resources */, + E56786F023F4C24500ACA6C1 /* applovin_card_muted.png in Resources */, + E56786E923F4C24500ACA6C1 /* Star_Sprite_3.5.png in Resources */, + E56786F723F4C24500ACA6C1 /* applovin_card_unmuted.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D992FD7231FA1C300C472F8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C0DE8BB3234E8A86004B0CFC /* ALBaseAdViewController.m in Sources */, + E56786B823F48B2000ACA6C1 /* ALCarouselCardView.m in Sources */, + 37C7E1F62329742E002165B5 /* ALHomeViewController.m in Sources */, + E567854523F22E1300ACA6C1 /* ALDemoRewardedVideosViewController.m in Sources */, + 37C7E1F523297423002165B5 /* main.m in Sources */, + E56785EE23F3557B00ACA6C1 /* ALDemoBannerZoneViewController.m in Sources */, + E56786CA23F48B8A00ACA6C1 /* ALCarouselCardState.m in Sources */, + E56785E823F3555A00ACA6C1 /* ALDemoProgrammaticBannerViewController.m in Sources */, + E567854623F22E1300ACA6C1 /* ALDemoRewardedVideosZoneViewController.m in Sources */, + E56786D223F48C1A00ACA6C1 /* ALDemoRSSFeedRetriever.m in Sources */, + E56785EB23F3557100ACA6C1 /* ALDemoInterfaceBuilderBannerViewController.m in Sources */, + 37C7E1F3232892CF002165B5 /* ALAppDelegate.m in Sources */, + E56786D123F48C1A00ACA6C1 /* ALDemoArticle.m in Sources */, + E56786FA23F4CAE100ACA6C1 /* ALEventTrackingViewController.m in Sources */, + E56786B923F48B2000ACA6C1 /* ALCarouselReplayOverlayView.m in Sources */, + E573083823EB972C00D972F4 /* ALDemoInterstitalZoneViewController.m in Sources */, + E56786BB23F48B2000ACA6C1 /* ALCarouselView.m in Sources */, + E56786CB23F48B8A00ACA6C1 /* ALNativeAdVideoPlayer.m in Sources */, + 37C7E1EB2328904E002165B5 /* ALMAXAutoLayoutBannerAdViewController.m in Sources */, + E567860623F4899600ACA6C1 /* ALDemoNativeAdProgrammaticViewController.m in Sources */, + E5C8F4C724119F0F00592BF8 /* ALDemoInterfaceBuilderMRECViewController.m in Sources */, + E56785FA23F35BC200ACA6C1 /* ALDemoProgrammaticLeaderViewController.m in Sources */, + 37C7E1EC2328904E002165B5 /* ALMAXFrameLayoutBannerAdViewController.m in Sources */, + E56786BC23F48B2000ACA6C1 /* ALNativeAdVideoView.m in Sources */, + E56786C223F48B7500ACA6C1 /* UIView+ALActivityIndicator.m in Sources */, + E573083423EB970400D972F4 /* ALDemoInterstitialManualLoadingViewController.m in Sources */, + E56786D723F48C7400ACA6C1 /* ALDemoNativeAdFeedTableViewController.m in Sources */, + 37C7E1ED2328904E002165B5 /* ALMAXInterstitialAdViewController.m in Sources */, + 37C7E1EE2328904E002165B5 /* ALMAXRewardedAdViewController.m in Sources */, + E56785F923F35BC200ACA6C1 /* ALDemoInterfaceBuilderLeaderViewController.m in Sources */, + E56786CC23F48B8A00ACA6C1 /* ALCarouselViewModel.m in Sources */, + E573083123EB96E400D972F4 /* ALDemoInterstitialBasicIntegrationViewController.m in Sources */, + 37C7E1EF2328904E002165B5 /* ALMAXInterfaceBuilderBannerAdViewController.m in Sources */, + E56786BA23F48B2000ACA6C1 /* ALCarouselMediaView.m in Sources */, + E567860223F484C900ACA6C1 /* ALDemoProgrammaticMRECViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 1D992FE4231FA1C400C472F8 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 1D992FE5231FA1C400C472F8 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 1D992FE9231FA1C500C472F8 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 1D992FEA231FA1C500C472F8 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1D992FEF231FA1C500C472F8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 1D992FF0231FA1C500C472F8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-ObjC"; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 1D992FF2231FA1C500C472F8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7WXN7X228H; + INFOPLIST_FILE = "$(SRCROOT)/AppLovin MAX Demo App - ObjC/Supporting Files/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = com.applovin.enterprise.apps.demoapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1D992FF3231FA1C500C472F8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 7WXN7X228H; + INFOPLIST_FILE = "$(SRCROOT)/AppLovin MAX Demo App - ObjC/Supporting Files/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = com.applovin.enterprise.apps.demoapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D992FD6231FA1C300C472F8 /* Build configuration list for PBXProject "AppLovin MAX Demo App - ObjC" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D992FEF231FA1C500C472F8 /* Debug */, + 1D992FF0231FA1C500C472F8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1D992FF1231FA1C500C472F8 /* Build configuration list for PBXNativeTarget "AppLovin MAX Demo App - ObjC" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D992FF2231FA1C500C472F8 /* Debug */, + 1D992FF3231FA1C500C472F8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1D992FD3231FA1C300C472F8 /* Project object */; +} diff --git a/DemoApp-ObjC/DemoApp-ObjC/ALAppDelegate.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/ALAppDelegate.h similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/ALAppDelegate.h rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/ALAppDelegate.h diff --git a/DemoApp-ObjC/DemoApp-ObjC/ALAppDelegate.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/ALAppDelegate.m similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/ALAppDelegate.m rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/ALAppDelegate.m diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Interface Builder/ALDemoInterfaceBuilderBannerViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Interface Builder/ALDemoInterfaceBuilderBannerViewController.h new file mode 100644 index 0000000000..1b3939a881 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Interface Builder/ALDemoInterfaceBuilderBannerViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoInterfaceBuilderBannerViewController.h +// iOS-SDK-Demo-ObjC +// +// Created by Thomas So on 3/6/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoInterfaceBuilderBannerViewController : ALBaseAdViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Interface Builder/ALDemoInterfaceBuilderBannerViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Interface Builder/ALDemoInterfaceBuilderBannerViewController.m new file mode 100644 index 0000000000..ace0a181ce --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Interface Builder/ALDemoInterfaceBuilderBannerViewController.m @@ -0,0 +1,109 @@ +// +// ALDemoInterfaceBuilderBannerViewController.m +// iOS-SDK-Demo-ObjC +// +// Created by Thomas So on 3/6/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +#import "ALDemoInterfaceBuilderBannerViewController.h" +#import + +@interface ALDemoInterfaceBuilderBannerViewController() +@property (nonatomic, weak) IBOutlet ALAdView *adView; +@property (nonatomic, weak) IBOutlet UIBarButtonItem *loadButton; +@end + +@implementation ALDemoInterfaceBuilderBannerViewController + +#pragma mark - View Lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.adView.adLoadDelegate = self; + self.adView.adDisplayDelegate = self; + self.adView.adEventDelegate = self; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear: animated]; + + self.adView.adLoadDelegate = nil; + self.adView.adDisplayDelegate = nil; + self.adView.adEventDelegate = nil; +} + +#pragma mark - IB Action + +- (IBAction)loadNextAd +{ + [self.adView loadNextAd]; + + self.loadButton.enabled = NO; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + // Look at ALErrorCodes.h for list of error codes + [self logCallback: __PRETTY_FUNCTION__]; + + self.loadButton.enabled = YES; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; + + self.loadButton.enabled = YES; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad View Event Delegate + +- (void)ad:(ALAd *)ad didPresentFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willLeaveApplicationForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didFailToDisplayInAdView:(ALAdView *)adView withError:(ALAdViewDisplayErrorCode)code +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Programmatic/ALDemoProgrammaticBannerViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Programmatic/ALDemoProgrammaticBannerViewController.h new file mode 100644 index 0000000000..82ef8d8982 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Programmatic/ALDemoProgrammaticBannerViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoProgrammaticBannerViewController.h +// iOS-SDK-Demo-ObjC +// +// Created by Thomas So on 3/5/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoProgrammaticBannerViewController : ALBaseAdViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Programmatic/ALDemoProgrammaticBannerViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Programmatic/ALDemoProgrammaticBannerViewController.m new file mode 100644 index 0000000000..dac9fc2f34 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Programmatic/ALDemoProgrammaticBannerViewController.m @@ -0,0 +1,142 @@ +// +// ALDemoProgrammaticBannerViewController.m +// iOS-SDK-Demo-ObjC +// +// Created by Thomas So on 3/5/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +#import "ALDemoProgrammaticBannerViewController.h" +#import + +@interface ALDemoProgrammaticBannerViewController() +@property (nonatomic, strong) ALAdView *adView; +@property (nonatomic, weak) IBOutlet UIBarButtonItem *loadButton; +@end + +@implementation ALDemoProgrammaticBannerViewController +static const CGFloat kBannerHeight = 50.0f; + +#pragma mark - View Lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + // Create the banner view + self.adView = [[ALAdView alloc] initWithSize: ALAdSize.banner]; + + // Optional: Implement the ad delegates to receive ad events. + self.adView.adLoadDelegate = self; + self.adView.adDisplayDelegate = self; + self.adView.adEventDelegate = self; + self.adView.translatesAutoresizingMaskIntoConstraints = NO; + + // Call loadNextAd() to start showing ads + [self.adView loadNextAd]; + + // Center the banner and anchor it to the bottom of the screen. + [self.view addSubview: self.adView]; + [self.view addConstraints: @[[self constraintWithAdView: self.adView andAttribute: NSLayoutAttributeLeading], + [self constraintWithAdView: self.adView andAttribute: NSLayoutAttributeTrailing], + [self constraintWithAdView: self.adView andAttribute: NSLayoutAttributeBottom], + [NSLayoutConstraint constraintWithItem: self.adView + attribute: NSLayoutAttributeHeight + relatedBy: NSLayoutRelationEqual + toItem: nil + attribute: NSLayoutAttributeNotAnAttribute + multiplier: 1.0 + constant: kBannerHeight]]]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear: animated]; + + self.adView.adLoadDelegate = nil; + self.adView.adDisplayDelegate = nil; + self.adView.adEventDelegate = nil; +} + +- (NSLayoutConstraint *)constraintWithAdView:(ALAdView *)adView andAttribute:(NSLayoutAttribute)attribute +{ + return [NSLayoutConstraint constraintWithItem: self.adView + attribute: attribute + relatedBy: NSLayoutRelationEqual + toItem: self.view + attribute: attribute + multiplier: 1.0 + constant: 0.0]; +} + +#pragma mark - IB Action + +- (IBAction)loadNextAd +{ + [self.adView loadNextAd]; + + self.loadButton.enabled = NO; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + // Look at ALErrorCodes.h for list of error codes + [self logCallback: __PRETTY_FUNCTION__]; + + self.loadButton.enabled = YES; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; + + self.loadButton.enabled = YES; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad View Event Delegate + +- (void)ad:(ALAd *)ad didPresentFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willLeaveApplicationForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didFailToDisplayInAdView:(ALAdView *)adView withError:(ALAdViewDisplayErrorCode)code +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Zone Integration/ALDemoBannerZoneViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Zone Integration/ALDemoBannerZoneViewController.h new file mode 100644 index 0000000000..671b1c3f80 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Zone Integration/ALDemoBannerZoneViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoBannerZoneViewController.h +// iOS-SDK-Demo-ObjC +// +// Created by Suyash Saxena on 6/19/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoBannerZoneViewController : ALBaseAdViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Zone Integration/ALDemoBannerZoneViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Zone Integration/ALDemoBannerZoneViewController.m new file mode 100644 index 0000000000..942312e087 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Banners/Zone Integration/ALDemoBannerZoneViewController.m @@ -0,0 +1,142 @@ +// +// ALDemoBannerZoneViewController.m +// iOS-SDK-Demo-ObjC +// +// Created by Suyash Saxena on 6/19/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +#import "ALDemoBannerZoneViewController.h" +#import + +@interface ALDemoBannerZoneViewController() +@property (nonatomic, strong) ALAdView *adView; +@property (nonatomic, weak) IBOutlet UIBarButtonItem *loadButton; +@end + +@implementation ALDemoBannerZoneViewController +static const CGFloat kBannerHeight = 50.0f; + +#pragma mark - View Lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + // Create the banner view + self.adView = [[ALAdView alloc] initWithSize: ALAdSize.banner zoneIdentifier: @"YOUR_ZONE_ID"]; + + // Optional: Implement the ad delegates to receive ad events. + self.adView.adLoadDelegate = self; + self.adView.adDisplayDelegate = self; + self.adView.adEventDelegate = self; + self.adView.translatesAutoresizingMaskIntoConstraints = NO; + + // Call loadNextAd() to start showing ads + [self.adView loadNextAd]; + + // Center the banner and anchor it to the bottom of the screen. + [self.view addSubview: self.adView]; + [self.view addConstraints: @[[self constraintWithAdView: self.adView andAttribute: NSLayoutAttributeLeading], + [self constraintWithAdView: self.adView andAttribute: NSLayoutAttributeTrailing], + [self constraintWithAdView: self.adView andAttribute: NSLayoutAttributeBottom], + [NSLayoutConstraint constraintWithItem: self.adView + attribute: NSLayoutAttributeHeight + relatedBy: NSLayoutRelationEqual + toItem: nil + attribute: NSLayoutAttributeNotAnAttribute + multiplier: 1.0 + constant: kBannerHeight]]]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear: animated]; + + self.adView.adLoadDelegate = nil; + self.adView.adDisplayDelegate = nil; + self.adView.adEventDelegate = nil; +} + +- (NSLayoutConstraint *)constraintWithAdView:(ALAdView *)adView andAttribute:(NSLayoutAttribute)attribute +{ + return [NSLayoutConstraint constraintWithItem: self.adView + attribute: attribute + relatedBy: NSLayoutRelationEqual + toItem: self.view + attribute: attribute + multiplier: 1.0 + constant: 0.0]; +} + +#pragma mark - IB Action + +- (IBAction)loadNextAd +{ + [self.adView loadNextAd]; + + self.loadButton.enabled = NO; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + // Look at ALErrorCodes.h for list of error codes + [self logCallback: __PRETTY_FUNCTION__]; + + self.loadButton.enabled = YES; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; + + self.loadButton.enabled = YES; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad View Event Delegate + +- (void)ad:(ALAd *)ad didPresentFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willLeaveApplicationForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didFailToDisplayInAdView:(ALAdView *)adView withError:(ALAdViewDisplayErrorCode)code +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Event Tracking/ALEventTrackingViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Event Tracking/ALEventTrackingViewController.h new file mode 100644 index 0000000000..2dab374de2 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Event Tracking/ALEventTrackingViewController.h @@ -0,0 +1,13 @@ +// +// ALEventTrackingViewController.h +// iOS-SDK-Demo-ObjC +// +// Created by Monica on 6/5/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALEventTrackingViewController : UITableViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Event Tracking/ALEventTrackingViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Event Tracking/ALEventTrackingViewController.m new file mode 100644 index 0000000000..dbbadd665e --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Event Tracking/ALEventTrackingViewController.m @@ -0,0 +1,204 @@ +// +// ALEventTrackingViewController.m +// iOS-SDK-Demo-ObjC +// +// Created by Monica Ong on 6/5/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +#import "ALEventTrackingViewController.h" +#import +#import + +@interface ALDemoEvent : NSObject + +@property (nonatomic, copy) NSString *name; +@property (nonatomic, copy) NSString *purpose; + +- (instancetype)initWithName:(NSString *)name purpose:(NSString *)purpose; + +@end + +@interface ALEventTrackingViewController() +@property (nonatomic, strong) NSArray *events; +@end + +@implementation ALEventTrackingViewController + +#pragma mark - View Lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.events = @[[[ALDemoEvent alloc] initWithName: @"Began Checkout Event" purpose: @"Track when user begins checkout procedure"], + [[ALDemoEvent alloc] initWithName: @"Cart Event" purpose: @"Track when user adds an item to cart"], + [[ALDemoEvent alloc] initWithName: @"Completed Achievement Event" purpose: @"Track when user completed an achievement"], + [[ALDemoEvent alloc] initWithName: @"Completed Checkout Event" purpose: @"Track when user completed checkout"], + [[ALDemoEvent alloc] initWithName: @"Completed Level Event" purpose: @"Track when user completed level"], + [[ALDemoEvent alloc] initWithName: @"Created Reservation Event" purpose: @"Track when user created a reservation"], + [[ALDemoEvent alloc] initWithName: @"In-App Purchase Event" purpose: @"Track when user makes an in-app purchase"], + [[ALDemoEvent alloc] initWithName: @"Login Event" purpose: @"Track when user logs in"], + [[ALDemoEvent alloc] initWithName: @"Payment Info Event" purpose: @"Tracks when user inputs their payment information"], + [[ALDemoEvent alloc] initWithName: @"Registration Event" purpose: @"Track when user registers"], + [[ALDemoEvent alloc] initWithName: @"Search Event" purpose: @"Track when user makes a search"], + [[ALDemoEvent alloc] initWithName: @"Sent Invitation Event" purpose: @"Track when user sends invitation"], + [[ALDemoEvent alloc] initWithName: @"Shared Link Event" purpose: @"Track when user shares a link"], + [[ALDemoEvent alloc] initWithName: @"Spent Virtual Currency Event" purpose: @"Track when users spends virtual currency"], + [[ALDemoEvent alloc] initWithName: @"Tutorial Event" purpose: @"Track when users does a tutorial"], + [[ALDemoEvent alloc] initWithName: @"Viewed Content Event" purpose: @"Track when user views content"], + [[ALDemoEvent alloc] initWithName: @"Viewed Product Event" purpose: @"Track when user views product"], + [[ALDemoEvent alloc] initWithName: @"Wishlist Event" purpose: @"Track when user adds an item to their wishlist"]]; +} + +#pragma mark - Table View Data Source + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return self.events.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + static NSString *const kCellIdentifier = @"rootPrototype"; + + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: kCellIdentifier]; + if ( !cell ) + { + cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: kCellIdentifier]; + } + + cell.textLabel.text = self.events[indexPath.row].name; + cell.detailTextLabel.text = self.events[indexPath.row].purpose; + + return cell; +} + +#pragma mark - Table View Delegate + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + [tableView deselectRowAtIndexPath: indexPath animated: YES]; + + ALEventService *eventService = [ALSdk shared].eventService; + if ( indexPath.row == 0 ) + { + [eventService trackEvent: kALEventTypeUserBeganCheckOut + parameters: @{kALEventParameterProductIdentifierKey : @"PRODUCT SKU OR ID", + kALEventParameterRevenueAmountKey : @"PRICE OF ITEM", + kALEventParameterRevenueCurrencyKey : @"3-LETTER CURRENCY CODE"}]; + } + else if ( indexPath.row == 1 ) + { + [eventService trackEvent: kALEventTypeUserAddedItemToCart + parameters: @{kALEventParameterProductIdentifierKey : @"PRODUCT SKU OR ID"}]; + } + else if ( indexPath.row == 2 ) + { + [eventService trackEvent: kALEventTypeUserCompletedAchievement + parameters: @{kALEventParameterCompletedAchievementKey : @"ACHIEVEMENT NAME OR ID"}]; + } + else if ( indexPath.row == 3 ) + { + [eventService trackEvent: kALEventTypeUserCompletedCheckOut + parameters: @{kALEventParameterCheckoutTransactionIdentifierKey : @"UNIQUE TRANSACTION ID", + kALEventParameterProductIdentifierKey : @"PRODUCT SKU OR ID", + kALEventParameterRevenueAmountKey : @"AMOUNT OF MONEY SPENT", + kALEventParameterRevenueCurrencyKey : @"3-LETTER CURRENCY CODE"}]; + } + else if ( indexPath.row == 4 ) + { + [eventService trackEvent: kALEventTypeUserCompletedLevel + parameters: @{kALEventParameterCompletedLevelKey : @"LEVEL NAME OR NUMBER"}]; + } + else if ( indexPath.row == 5 ) + { + [eventService trackEvent: kALEventTypeUserCreatedReservation + parameters: @{kALEventParameterProductIdentifierKey : @"PRODUCT SKU OR ID", + kALEventParameterReservationStartDateKey : @"START DATE", + kALEventParameterReservationEndDateKey : @"END DATE"}]; + } + else if ( indexPath.row == 6 ) + { + SKPaymentTransaction *transaction = [[SKPaymentTransaction alloc] init]; // from paymentQueue:updatedTransactions: + //SKProduct* product = ...; // Appropriate product (matching productIdentifier property to SKPaymentTransaction); + [eventService trackInAppPurchaseWithTransactionIdentifier: transaction.transactionIdentifier + parameters: @{kALEventParameterRevenueAmountKey : @"AMOUNT OF MONEY SPENT", + kALEventParameterRevenueCurrencyKey : @"3-LETTER CURRENCY CODE", + kALEventParameterProductIdentifierKey : @"product.productIdentifier"}]; //product.productIdentifier + } + else if ( indexPath.row == 7 ) + { + [eventService trackEvent: kALEventTypeUserLoggedIn + parameters: @{kALEventParameterUserAccountIdentifierKey : @"USERNAME"}]; + } + else if ( indexPath.row == 8 ) + { + [eventService trackEvent: kALEventTypeUserProvidedPaymentInformation]; + } + else if ( indexPath.row == 9 ) + { + [eventService trackEvent: kALEventTypeUserCreatedAccount + parameters: @{kALEventParameterUserAccountIdentifierKey : @"USERNAME"}]; + } + else if ( indexPath.row == 10 ) + { + [eventService trackEvent: kALEventTypeUserExecutedSearch + parameters: @{kALEventParameterSearchQueryKey : @"USER'S SEARCH STRING"}]; + } + else if ( indexPath.row == 11 ) + { + [eventService trackEvent: kALEventTypeUserSentInvitation]; + } + else if ( indexPath.row == 12 ) + { + [eventService trackEvent: kALEventTypeUserSharedLink]; + } + else if ( indexPath.row == 13 ) + { + [eventService trackEvent: kALEventTypeUserSpentVirtualCurrency + parameters: @{kALEventParameterVirtualCurrencyAmountKey : @"NUMBER OF COINS SPENT", + kALEventParameterVirtualCurrencyNameKey : @"CURRENCY NAME"}]; + } + else if ( indexPath.row == 14 ) + { + [eventService trackEvent: kALEventTypeUserCompletedTutorial]; + } + else if ( indexPath.row == 15 ) + { + [eventService trackEvent: kALEventTypeUserViewedContent + parameters: @{kALEventParameterContentIdentifierKey : @"SOME ID DESCRIBING CONTENT"}]; + + } + else if ( indexPath.row == 16 ) + { + [eventService trackEvent: kALEventTypeUserViewedProduct + parameters: @{kALEventParameterProductIdentifierKey : @"PRODUCT SKU OR ID"}]; + } + else if ( indexPath.row == 17 ) + { + [eventService trackEvent: kALEventTypeUserAddedItemToWishlist + parameters: @{kALEventParameterProductIdentifierKey : @"PRODUCT SKU OR ID"}]; + } + else + { + [self setTitle: @"Default event tracking initiated"]; + } +} + +@end + +@implementation ALDemoEvent + +- (instancetype)initWithName:(NSString *)name purpose:(NSString *)purpose +{ + self = [super init]; + if ( self ) + { + self.name = name; + self.purpose = purpose; + } + return self; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Basic Integration/ALDemoInterstitialBasicIntegrationViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Basic Integration/ALDemoInterstitialBasicIntegrationViewController.h new file mode 100644 index 0000000000..7a4988ec76 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Basic Integration/ALDemoInterstitialBasicIntegrationViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoInterstitialBasicIntegrationViewController.h +// iOS-SDK-Demo +// +// Created by Thomas So on 9/23/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoInterstitialBasicIntegrationViewController : ALBaseAdViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Basic Integration/ALDemoInterstitialBasicIntegrationViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Basic Integration/ALDemoInterstitialBasicIntegrationViewController.m new file mode 100644 index 0000000000..e8826dc731 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Basic Integration/ALDemoInterstitialBasicIntegrationViewController.m @@ -0,0 +1,79 @@ +// +// ALDemoInterstitialBasicIntegrationViewController.m +// iOS-SDK-Demo +// +// Created by Thomas So on 9/23/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALDemoInterstitialBasicIntegrationViewController.h" +#import + +@interface ALDemoInterstitialBasicIntegrationViewController() +@property (nonatomic, strong) ALInterstitialAd *interstitialAd; +@end + +@implementation ALDemoInterstitialBasicIntegrationViewController + +#pragma mark - View Controller Lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.interstitialAd = [ALInterstitialAd shared]; + self.interstitialAd.adLoadDelegate = self; + self.interstitialAd.adDisplayDelegate = self; + self.interstitialAd.adVideoPlaybackDelegate = self; +} + +#pragma mark - IB Actions + +- (IBAction)showAd:(id)sender +{ + [self.interstitialAd show]; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void) adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + // Look at ALErrorCodes.h for list of error codes + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Video Playback Delegate + +- (void)videoPlaybackBeganInAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)videoPlaybackEndedInAd:(ALAd *)ad atPlaybackPercent:(NSNumber *)percentPlayed fullyWatched:(BOOL)wasFullyWatched +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Manual Loading/ALDemoInterstitialManualLoadingViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Manual Loading/ALDemoInterstitialManualLoadingViewController.h new file mode 100644 index 0000000000..91c8ebb87d --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Manual Loading/ALDemoInterstitialManualLoadingViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoInterstitialManualLoadingViewController.h +// iOS-SDK-Demo +// +// Created by Thomas So on 9/25/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoInterstitialManualLoadingViewController : ALBaseAdViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Manual Loading/ALDemoInterstitialManualLoadingViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Manual Loading/ALDemoInterstitialManualLoadingViewController.m new file mode 100644 index 0000000000..52bb47d9cc --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Manual Loading/ALDemoInterstitialManualLoadingViewController.m @@ -0,0 +1,80 @@ +// +// ALDemoInterstitialManualLoadingViewController.m +// iOS-SDK-Demo +// +// Created by Thomas So on 9/25/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALDemoInterstitialManualLoadingViewController.h" +#import + +@interface ALDemoInterstitialManualLoadingViewController() +@property (nonatomic, strong) ALAd *ad; +@property (nonatomic, strong) ALInterstitialAd *interstitialAd; +@property (nonatomic, weak) IBOutlet UIBarButtonItem *showButton; +@end + +@implementation ALDemoInterstitialManualLoadingViewController + +- (IBAction)loadInterstitial:(id)sender +{ + [[ALSdk shared].adService loadNextAd: ALAdSize.interstitial andNotify: self]; +} + +- (IBAction)showInterstitial:(id)sender +{ + // Optional: Assign delegates + self.interstitialAd = [ALInterstitialAd shared]; + self.interstitialAd.adDisplayDelegate = self; + self.interstitialAd.adVideoPlaybackDelegate = self; + + [self.interstitialAd showAd: self.ad]; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; + + self.ad = ad; + self.showButton.enabled = YES; +} + +- (void) adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + // Look at ALErrorCodes.h for list of error codes + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Video Playback Delegate + +- (void)videoPlaybackBeganInAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)videoPlaybackEndedInAd:(ALAd *)ad atPlaybackPercent:(NSNumber *)percentPlayed fullyWatched:(BOOL)wasFullyWatched +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Zone Integration/ALDemoInterstitalZoneViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Zone Integration/ALDemoInterstitalZoneViewController.h new file mode 100644 index 0000000000..403476301a --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Zone Integration/ALDemoInterstitalZoneViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoInterstitalZoneViewController.h +// iOS-SDK-Demo-ObjC +// +// Created by Suyash Saxena on 6/19/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoInterstitalZoneViewController : ALBaseAdViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Zone Integration/ALDemoInterstitalZoneViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Zone Integration/ALDemoInterstitalZoneViewController.m new file mode 100644 index 0000000000..6f3fd4bb6a --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Interstitials/Zone Integration/ALDemoInterstitalZoneViewController.m @@ -0,0 +1,79 @@ +// +// ALDemoInterstitalZoneViewController.m +// iOS-SDK-Demo-ObjC +// +// Created by Suyash Saxena on 6/19/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +#import "ALDemoInterstitalZoneViewController.h" +#import + +@interface ALDemoInterstitalZoneViewController() +@property (nonatomic, strong) ALAd *ad; +@property (nonatomic, strong) ALInterstitialAd *interstitialAd; +@property (nonatomic, weak) IBOutlet UIBarButtonItem *showButton; +@end + +@implementation ALDemoInterstitalZoneViewController + +- (IBAction)loadInterstitial:(id)sender +{ + [[ALSdk shared].adService loadNextAdForZoneIdentifier: @"YOUR_ZONE_ID" andNotify: self]; +} + +- (IBAction)showInterstitial:(id)sender +{ + // Optional: Assign delegates + self.interstitialAd = [ALInterstitialAd shared]; + self.interstitialAd.adDisplayDelegate = self; + self.interstitialAd.adVideoPlaybackDelegate = self; + + [self.interstitialAd showAd: self.ad]; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + self.ad = ad; + self.showButton.enabled = YES; + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void) adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + // Look at ALErrorCodes.h for list of error codes + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Video Playback Delegate + +- (void)videoPlaybackBeganInAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)videoPlaybackEndedInAd:(ALAd *)ad atPlaybackPercent:(NSNumber *)percentPlayed fullyWatched:(BOOL)wasFullyWatched +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Interface Builder/ALDemoInterfaceBuilderLeaderViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Interface Builder/ALDemoInterfaceBuilderLeaderViewController.h new file mode 100644 index 0000000000..0b89de727b --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Interface Builder/ALDemoInterfaceBuilderLeaderViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoInterfaceBuilderLeaderViewController.h +// iOS-SDK-Demo-ObjC +// +// Created by Santosh Bagadi on 4/4/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoInterfaceBuilderLeaderViewController : ALBaseAdViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Interface Builder/ALDemoInterfaceBuilderLeaderViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Interface Builder/ALDemoInterfaceBuilderLeaderViewController.m new file mode 100644 index 0000000000..3302e944f9 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Interface Builder/ALDemoInterfaceBuilderLeaderViewController.m @@ -0,0 +1,109 @@ +// +// ALDemoInterfaceBuilderLeaderViewController.m +// iOS-SDK-Demo-ObjC +// +// Created by Santosh Bagadi on 4/4/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +#import "ALDemoInterfaceBuilderLeaderViewController.h" +#import + +@interface ALDemoInterfaceBuilderLeaderViewController() +@property (nonatomic, weak) IBOutlet ALAdView *adView; +@property (nonatomic, weak) IBOutlet UIBarButtonItem *loadButton; +@end + +@implementation ALDemoInterfaceBuilderLeaderViewController + +#pragma mark - View Lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.adView.adLoadDelegate = self; + self.adView.adDisplayDelegate = self; + self.adView.adEventDelegate = self; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear: animated]; + + self.adView.adLoadDelegate = nil; + self.adView.adDisplayDelegate = nil; + self.adView.adEventDelegate = nil; +} + +#pragma mark - IB Action + +- (IBAction)loadNextAd +{ + [self.adView loadNextAd]; + + self.loadButton.enabled = NO; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + // Look at ALErrorCodes.h for list of error codes + [self logCallback: __PRETTY_FUNCTION__]; + + self.loadButton.enabled = YES; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; + + self.loadButton.enabled = YES; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad View Event Delegate + +- (void)ad:(ALAd *)ad didPresentFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willLeaveApplicationForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didFailToDisplayInAdView:(ALAdView *)adView withError:(ALAdViewDisplayErrorCode)code +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Programmatic/ALDemoProgrammaticLeaderViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Programmatic/ALDemoProgrammaticLeaderViewController.h new file mode 100644 index 0000000000..b969177df0 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Programmatic/ALDemoProgrammaticLeaderViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoProgrammaticLeaderViewController.h +// iOS-SDK-Demo-ObjC +// +// Created by Santosh Bagadi on 4/4/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoProgrammaticLeaderViewController : ALBaseAdViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Programmatic/ALDemoProgrammaticLeaderViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Programmatic/ALDemoProgrammaticLeaderViewController.m new file mode 100644 index 0000000000..9d6679b1d3 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Leaders/Programmatic/ALDemoProgrammaticLeaderViewController.m @@ -0,0 +1,142 @@ +// +// ALDemoProgrammaticLeaderViewController.m +// iOS-SDK-Demo-ObjC +// +// Created by Santosh Bagadi on 4/4/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +#import "ALDemoProgrammaticLeaderViewController.h" +#import + +@interface ALDemoProgrammaticLeaderViewController() +@property (nonatomic, strong) ALAdView *adView; +@property (nonatomic, weak) IBOutlet UIBarButtonItem *loadButton; +@end + +@implementation ALDemoProgrammaticLeaderViewController +static const CGFloat kLeaderHeight = 90.0f; + +#pragma mark - View Lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + // Create the banner view + self.adView = [[ALAdView alloc] initWithSize: ALAdSize.leader]; + + // Optional: Implement the ad delegates to receive ad events. + self.adView.adLoadDelegate = self; + self.adView.adDisplayDelegate = self; + self.adView.adEventDelegate = self; + self.adView.translatesAutoresizingMaskIntoConstraints = false; + + // Call loadNextAd() to start showing ads + [self.adView loadNextAd]; + + // Center the banner and anchor it to the bottom of the screen. + [self.view addSubview: self.adView]; + [self.view addConstraints: @[[self constraintWithAdView: self.adView andAttribute: NSLayoutAttributeLeading], + [self constraintWithAdView: self.adView andAttribute: NSLayoutAttributeTrailing], + [self constraintWithAdView: self.adView andAttribute: NSLayoutAttributeBottom], + [NSLayoutConstraint constraintWithItem: self.adView + attribute: NSLayoutAttributeHeight + relatedBy: NSLayoutRelationEqual + toItem: nil + attribute: NSLayoutAttributeNotAnAttribute + multiplier: 1.0 + constant: kLeaderHeight]]]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear: animated]; + + self.adView.adLoadDelegate = nil; + self.adView.adDisplayDelegate = nil; + self.adView.adEventDelegate = nil; +} + +- (NSLayoutConstraint *)constraintWithAdView:(ALAdView *)adView andAttribute:(NSLayoutAttribute)attribute +{ + return [NSLayoutConstraint constraintWithItem: self.adView + attribute: attribute + relatedBy: NSLayoutRelationEqual + toItem: self.view + attribute: attribute + multiplier: 1.0 + constant: 0.0]; +} + +#pragma mark - IB Action + +- (IBAction)loadNextAd +{ + [self.adView loadNextAd]; + + self.loadButton.enabled = NO; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + // Look at ALErrorCodes.h for list of error codes + [self logCallback: __PRETTY_FUNCTION__]; + + self.loadButton.enabled = YES; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; + + self.loadButton.enabled = YES; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad View Event Delegate + +- (void)ad:(ALAd *)ad didPresentFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willLeaveApplicationForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didFailToDisplayInAdView:(ALAdView *)adView withError:(ALAdViewDisplayErrorCode)code +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Interface Builder/ALDemoInterfaceBuilderMRECViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Interface Builder/ALDemoInterfaceBuilderMRECViewController.h new file mode 100644 index 0000000000..b03f0fa026 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Interface Builder/ALDemoInterfaceBuilderMRECViewController.h @@ -0,0 +1,14 @@ +// +// ALDemoInterfaceBuilderMRECViewController.h +// AppLovin Demo App - ObjC +// +// Created by Varsha Hanji on 3/5/20. +// Copyright © 2020 AppLovin Corporation. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoInterfaceBuilderMRECViewController : ALBaseAdViewController + +@end + diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Interface Builder/ALDemoInterfaceBuilderMRECViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Interface Builder/ALDemoInterfaceBuilderMRECViewController.m new file mode 100644 index 0000000000..0173277dde --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Interface Builder/ALDemoInterfaceBuilderMRECViewController.m @@ -0,0 +1,99 @@ +// +// ALDemoInterfaceBuilderMRECViewController.m +// AppLovin Demo App - ObjC +// +// Created by Varsha Hanji on 3/5/20. +// Copyright © 2020 AppLovin Corporation. All rights reserved. +// + +#import "ALDemoInterfaceBuilderMRECViewController.h" +#import + +@interface ALDemoInterfaceBuilderMRECViewController () +@property (weak, nonatomic) IBOutlet ALAdView *adView; +@end + +@implementation ALDemoInterfaceBuilderMRECViewController + +#pragma mark - View Lifecycle + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear: animated]; + + self.adView.adLoadDelegate = self; + self.adView.adDisplayDelegate = self; + self.adView.adEventDelegate = self; + + // Call loadNextAd() to start showing ads + self.adView.adSize = ALAdSize.mrec; + [self.adView loadNextAd]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear: animated]; + + self.adView.adLoadDelegate = nil; + self.adView.adDisplayDelegate = nil; + self.adView.adEventDelegate = nil; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + // Look at ALErrorCodes.h for list of error codes + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad View Event Delegate + +- (void)ad:(ALAd *)ad didPresentFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willLeaveApplicationForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didFailToDisplayInAdView:(ALAdView *)adView withError:(ALAdViewDisplayErrorCode)code +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Programmatic/ALDemoProgrammaticMRECViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Programmatic/ALDemoProgrammaticMRECViewController.h new file mode 100644 index 0000000000..1eb979fed0 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Programmatic/ALDemoProgrammaticMRECViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoProgrammaticMRECViewController.h +// iOS-SDK-Demo-ObjC +// +// Created by Thomas So on 3/6/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoProgrammaticMRECViewController : ALBaseAdViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Programmatic/ALDemoProgrammaticMRECViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Programmatic/ALDemoProgrammaticMRECViewController.m new file mode 100644 index 0000000000..c2acaba4c5 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/MRECs/Programmatic/ALDemoProgrammaticMRECViewController.m @@ -0,0 +1,135 @@ +// +// ALDemoProgrammaticMRECViewController.m +// iOS-SDK-Demo-ObjC +// +// Created by Thomas So on 3/6/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +#import "ALDemoProgrammaticMRECViewController.h" +#import + +@interface ALDemoProgrammaticMRECViewController() +@property (nonatomic, strong) ALAdView *adView; +@end + +@implementation ALDemoProgrammaticMRECViewController +static const CGFloat kMRECHeight = 250.0f; +static const CGFloat kMRECWidth = 300.0f; + +#pragma mark - View Lifecycle + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear: animated]; + + // Create the MREC view + self.adView = [[ALAdView alloc] initWithSize: ALAdSize.mrec]; + + // Optional: Implement the ad delegates to receive ad events. + self.adView.adLoadDelegate = self; + self.adView.adDisplayDelegate = self; + self.adView.adEventDelegate = self; + self.adView.translatesAutoresizingMaskIntoConstraints = false; + + // Call loadNextAd() to start showing ads + [self.adView loadNextAd]; + + [self.view addSubview: self.adView]; + [self.view addConstraints: @[[NSLayoutConstraint constraintWithItem: self.adView + attribute: NSLayoutAttributeCenterX + relatedBy: NSLayoutRelationEqual + toItem: self.view + attribute: NSLayoutAttributeCenterX + multiplier: 1.0 + constant: 0.0], + [NSLayoutConstraint constraintWithItem: self.adView + attribute: NSLayoutAttributeCenterY + relatedBy: NSLayoutRelationEqual + toItem: self.view + attribute: NSLayoutAttributeCenterY + multiplier: 1.0 + constant: 0.0], + [NSLayoutConstraint constraintWithItem: self.adView + attribute: NSLayoutAttributeHeight + relatedBy: NSLayoutRelationEqual + toItem: nil + attribute: NSLayoutAttributeNotAnAttribute + multiplier: 1.0 + constant: kMRECHeight], + [NSLayoutConstraint constraintWithItem: self.adView + attribute: NSLayoutAttributeWidth + relatedBy: NSLayoutRelationEqual + toItem: nil + attribute: NSLayoutAttributeNotAnAttribute + multiplier: 1.0 + constant: kMRECWidth]]]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear: animated]; + + self.adView.adLoadDelegate = nil; + self.adView.adDisplayDelegate = nil; + self.adView.adEventDelegate = nil; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + // Look at ALErrorCodes.h for list of error codes + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad View Event Delegate + +- (void)ad:(ALAd *)ad didPresentFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didDismissFullscreenForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad willLeaveApplicationForAdView:(ALAdView *)adView +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad didFailToDisplayInAdView:(ALAdView *)adView withError:(ALAdViewDisplayErrorCode)code +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/ALDemoNativeAdFeedTableViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/ALDemoNativeAdFeedTableViewController.h new file mode 100644 index 0000000000..a51abfcfdd --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/ALDemoNativeAdFeedTableViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoNativeAdFeedTableViewController.h +// iOS-SDK-Demo +// +// Created by Thomas So on 9/24/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import + +@interface ALDemoNativeAdFeedTableViewController : UITableViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/ALDemoNativeAdFeedTableViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/ALDemoNativeAdFeedTableViewController.m new file mode 100644 index 0000000000..df23aed98d --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/ALDemoNativeAdFeedTableViewController.m @@ -0,0 +1,98 @@ +// +// ALDemoNativeAdFeedTableViewController.m +// iOS-SDK-Demo +// +// Created by Thomas So on 9/24/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALDemoNativeAdFeedTableViewController.h" +#import "ALCarouselView.h" +#import "ALDemoRSSFeedRetriever.h" + +// +// This view controller demonstrates how to display native ads using our open-source carousel views. +// + +@interface ALDemoNativeAdFeedTableViewController() +@property (nonatomic, strong) NSArray *articles; +@end + +@implementation ALDemoNativeAdFeedTableViewController +static NSString *const kArticleCellIdentifier = @"articleCell"; +static NSString *const kAdCellIdentifier = @"adCell"; + +static NSUInteger const kCellTagTitleLabel = 2; +static NSUInteger const kCellTagSubtitleLabel = 3; +static NSUInteger const kCellTagDescriptionLabel = 4; + +#pragma mark - View Lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + [[ALDemoRSSFeedRetriever sharedRetriever] startParsingWithCompletion:^(NSError * _Nullable error, NSArray * _Nonnull articles) { + dispatch_async(dispatch_get_main_queue(), ^{ + + if ( error || articles.count == 0 ) + { + UIAlertController *alert = [UIAlertController alertControllerWithTitle: @"ERROR" message: error.localizedDescription preferredStyle: UIAlertControllerStyleAlert]; + [alert addAction: [UIAlertAction actionWithTitle: @"OK" style: UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self.navigationController popViewControllerAnimated: YES]; + }]]; + [self presentViewController: alert animated: YES completion: nil]; + } + else + { + self.articles = articles; + [self.tableView reloadData]; + } + }); + }]; +} + +#pragma mark - Table View Data Source + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return self.articles.count; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + return self.articles[indexPath.row].isAd ? 360.0f : 280.0f; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + UITableViewCell *cell; + ALDemoArticle *article = self.articles[indexPath.row]; + + if ( article.isAd ) + { + // You can configure carousels in ALCarouselViewSettings.h + cell = [tableView dequeueReusableCellWithIdentifier: kAdCellIdentifier forIndexPath:indexPath]; + } + else + { + cell = [tableView dequeueReusableCellWithIdentifier: kArticleCellIdentifier forIndexPath:indexPath]; + ((UILabel *)[cell viewWithTag: kCellTagTitleLabel]).text = article.title; + ((UILabel *)[cell viewWithTag: kCellTagSubtitleLabel]).text = [NSString stringWithFormat: @"%@ - %@", article.creator, article.pubDate]; + ((UILabel *)[cell viewWithTag: kCellTagDescriptionLabel]).text = article.articleDescription; + } + + return cell; +} + +#pragma mark - Table View Delegate + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + [tableView deselectRowAtIndexPath: indexPath animated: YES]; + + ALDemoArticle *article = self.articles[indexPath.row]; + [[UIApplication sharedApplication] openURL: article.link]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselCardState.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselCardState.h new file mode 100644 index 0000000000..3c6061c88e --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselCardState.h @@ -0,0 +1,52 @@ +// +// ALCarouselCardState.h +// sdk +// +// Created by Matt Szaro on 4/17/15. +// +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Tracks the state of a card within an ALCarouselView. + */ +@interface ALCarouselCardState : NSObject + +/** + Retrieve an instance with appropriate default settings for use within a carousel view. + */ ++(instancetype) cardStateForCarousel; + +/** + Retrieve an instance with appropriate default settings for use within a single ALCarouselCardView outside a carousel. + */ ++(instancetype) cardStateForSingleCard; + +typedef NS_ENUM(NSUInteger, ALMuteState) +{ + ALMuteStateUnspecified, + ALMuteStateUnmuted, + ALMuteStateMuted +}; + +@property (assign, atomic, getter=wasVideoStarted) BOOL videoStarted; +@property (assign, atomic, getter=wasVideoCompleted) BOOL videoCompleted; +@property (assign, atomic, getter=wasVideoStartTracked) BOOL videoStartTracked; +@property (assign, atomic, getter=isFirstPlayback) BOOL firstPlayback; +@property (assign, atomic, getter=wasImpressionTracked) BOOL impressionTracked; +@property (assign, atomic, getter=isCurrentlyActive) BOOL currentlyActive; +@property (assign, atomic, getter=isReplayOverlayVisible) BOOL replayOverlayVisible; +@property (assign, atomic, getter=isPrecaching) BOOL precaching; // To prvent a slot from being redundantly pre-caching + +@property (assign, atomic) Float64 lastMediaPlayerPosition; +@property (assign, atomic) ALMuteState muteState; +@property (strong, atomic) UIImage *screenshot; +@property (assign, atomic) CGRect videoRect; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselCardState.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselCardState.m new file mode 100644 index 0000000000..d8350f8831 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselCardState.m @@ -0,0 +1,34 @@ +// +// ALCarouselCardState.m +// sdk +// +// Created by Matt Szaro on 4/17/15. +// +// + +#import "ALCarouselCardState.h" + +@implementation ALCarouselCardState + ++(instancetype) cardStateForCarousel +{ + ALCarouselCardState* state = [[[self class] alloc] init]; + + state.muteState = ALMuteStateUnspecified; + state.firstPlayback = YES; + + return state; +} + ++(instancetype) cardStateForSingleCard +{ + ALCarouselCardState* state = [[[self class] alloc] init]; + + state.muteState = ALMuteStateUnspecified; + state.currentlyActive = YES; + state.firstPlayback = YES; + + return state; +} + +@end \ No newline at end of file diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselRenderingProtocol.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselRenderingProtocol.h new file mode 100644 index 0000000000..30454997ed --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselRenderingProtocol.h @@ -0,0 +1,26 @@ +// +// ALCarouselRenderingProtocol.h +// sdk +// +// Created by Matt Szaro on 5/7/15. +// +// + +@import AppLovinSDK; +#import +#import "ALCarouselCardState.h" + +@protocol ALCarouselRenderingProtocol + +@optional +- (void)renderViewForNativeAd:(ALNativeAd *)ad; + +@required +- (void)renderViewForNativeAd:(ALNativeAd *)ad cardState:(ALCarouselCardState *)cardState; + +/** + * Resets the current card's view properties. + */ +- (void)clearView; + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselViewModel.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselViewModel.h new file mode 100644 index 0000000000..4c51732b59 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselViewModel.h @@ -0,0 +1,32 @@ +// +// ALCarouselModel.h +// sdk +// +// Created by Matt Szaro on 4/20/15. +// +// + +#import +#import + +@class ALNativeAd; +@class ALCarouselCardState; +@class ALSdk; + +NS_ASSUME_NONNULL_BEGIN + +@interface ALCarouselViewModel : NSObject + +@property (strong, nonatomic, readonly) NSArray *nativeAds; +@property (assign, nonatomic, readonly) NSUInteger nativeAdsCount; + +- (instancetype)initWithNativeAds:(NSArray *)ads; +- (nullable ALCarouselCardState *)cardStateForNativeAd:(ALNativeAd *)ad; +- (nullable ALCarouselCardState *)cardStateAtNativeAdIndex:(NSUInteger)index; +- (nullable ALNativeAd *)nativeAdAtIndex:(NSUInteger)index; + +- (void)removeAllObjects; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselViewModel.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselViewModel.m new file mode 100644 index 0000000000..e22b46d780 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALCarouselViewModel.m @@ -0,0 +1,174 @@ +// +// ALCarouselModel.m +// sdk +// +// Created by Matt Szaro on 4/20/15. +// +// + +#import "ALCarouselViewModel.h" +#import "ALCarouselCardState.h" + +@interface ALCarouselViewModel () + +@property (strong, nonatomic) ALSdk* sdk; + +@property (strong, nonatomic) NSArray* nativeAds; +@property (strong, nonatomic) NSMutableDictionary* cardStates; + +@end + +@implementation ALCarouselViewModel +@dynamic nativeAdsCount; + +static void* ALCarouselViewModelKVOContext = &ALCarouselViewModelKVOContext; +static NSString* kKeypathMuteState = @"muteState"; + +-(instancetype) initWithNativeAds: (NSArray *)ads +{ + self = [super init]; + if ( self ) + { + self.nativeAds = ads; + self.cardStates = [NSMutableDictionary dictionary]; + } + return self; +} + +- (ALCarouselCardState *)cardStateAtNativeAdIndex:(NSUInteger)index +{ + if ( index >= [self nativeAdsCount]) + { + NSLog(@"Requested card state at native ad index %lu is out-of-bounds", index); + return nil; + } + else + { + ALCarouselCardState* cardState = self.cardStates[ @(index) ]; + if ( cardState ) + { + NSLog(@"Requested card state at native ad index %lu", index); + return cardState; + } + else + { + NSLog(@"Requested card state at native ad index %lu does not exist yet. Creating new card state", index); + + ALCarouselCardState* newState = [ALCarouselCardState cardStateForCarousel]; + [self beginObservingCardState: newState]; + [self.cardStates setObject: newState forKey: @(index)]; + + return newState; + } + } +} + +-(void) beginObservingCardState: (ALCarouselCardState*) cardState +{ + // Observe any properties which need to be synchronized among all card states. + // E.g., propogate mute settings among cards while within a carousel view. + // This allows us to add syncronization features on top of the card state without adding complexity for single card integrations. + + [cardState addObserver: self + forKeyPath: kKeypathMuteState + options: NSKeyValueObservingOptionNew + context: ALCarouselViewModelKVOContext]; +} + +-(void) endObservingCardState: (ALCarouselCardState*) cardState +{ + // Observe any properties which need to be synchronized among all card states. + // E.g., propogate mute settings among cards while within a carousel view. + // This allows us to add syncronization features on top of the card state without adding complexity for single card integrations. + + @try { + [cardState removeObserver: self + forKeyPath: kKeypathMuteState + context: ALCarouselViewModelKVOContext]; + } + @catch (NSException* __unused ignore) + { + } +} + +-(void) observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context +{ + if (context == ALCarouselViewModelKVOContext) + { + if ([keyPath isEqual: kKeypathMuteState]) + { + NSNumber* newValue = change[NSKeyValueChangeNewKey]; + ALMuteState muteState = (ALMuteState) [newValue unsignedIntegerValue]; + + if (muteState != ALMuteStateUnspecified) + { + [self updateMuteStates: muteState]; + } + } + } + else + { + [super observeValueForKeyPath: keyPath ofObject: object change: change context: context]; + } +} + +-(void) updateMuteStates: (ALMuteState) newState +{ + [self.cardStates enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + ALCarouselCardState* cardState = (ALCarouselCardState*) obj; + [self endObservingCardState: cardState]; + cardState.muteState = newState; + [self beginObservingCardState: cardState]; + }]; +} + +- (ALCarouselCardState *)cardStateForNativeAd:(ALNativeAd *)ad +{ + NSUInteger index = [self.nativeAds indexOfObject: ad]; + if ( index != NSNotFound ) + { + return [self cardStateAtNativeAdIndex: index]; + } + else + { + return nil; + } +} + +- (ALNativeAd *)nativeAdAtIndex:(NSUInteger)index +{ + if ( index < self.nativeAds.count ) + { + NSLog(@"Requested native ad index %lu", index); + return [self.nativeAds objectAtIndex: index]; + } + else + { + NSLog(@"Requested native ad index %lu is out of bounds", index); + return nil; + } +} + +- (NSUInteger)nativeAdsCount +{ + return self.nativeAds.count; +} + +-(void) removeAllObjects; +{ + self.nativeAds = nil; + + [self.cardStates enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + ALCarouselCardState* cardState = (ALCarouselCardState*) obj; + [self endObservingCardState: cardState]; + }]; + + [self.cardStates removeAllObjects]; +} + +-(void) dealloc +{ + [self removeAllObjects]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALNativeAdVideoPlayer.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALNativeAdVideoPlayer.h new file mode 100644 index 0000000000..c48d014873 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALNativeAdVideoPlayer.h @@ -0,0 +1,30 @@ +// +// ALVideoPlayer.h +// sdk +// +// Created by Matt Szaro on 6/23/14. +// +// + +@import AppLovinSDK; +@import AVFoundation; +@import CoreMedia; +#import "ALNativeAdVideoView.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ALNativeAdVideoPlayer : NSObject + +@property (strong, nonatomic, readonly) ALNativeAdVideoView* videoView; +@property (strong, nonatomic, readonly) AVPlayerItem* playerItem; +@property (strong, nonatomic, readonly) AVAsset* playerAsset; +@property (strong, nonatomic, readwrite) NSURL* mediaSource; + +-(instancetype) initWithMediaSource: (nullable NSURL*) aMediaSource; + +-(void) playVideo; +-(void) stopVideo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALNativeAdVideoPlayer.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALNativeAdVideoPlayer.m new file mode 100644 index 0000000000..c52389bc76 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/ALNativeAdVideoPlayer.m @@ -0,0 +1,67 @@ +// +// ALVideoPlayer.m +// sdk +// +// Created by Matt Szaro on 6/23/14. +// +// + +#import "ALNativeAdVideoPlayer.h" + +@interface ALNativeAdVideoPlayer() + +@property (strong, nonatomic, readwrite) ALNativeAdVideoView* videoView; +@property (strong, nonatomic, readwrite) AVPlayerItem* playerItem; +@property (strong, nonatomic, readwrite) AVAsset* playerAsset; +@property (strong, nonatomic, readwrite) AVPlayer* player; + +@end + +@implementation ALNativeAdVideoPlayer + +-(instancetype) initWithMediaSource:(NSURL *)aMediaSource +{ + self = [super init]; + if(self) + { + self.mediaSource = aMediaSource; + self.videoView = [self createVideoView]; + } + return self; +} + +-(ALNativeAdVideoView*) createVideoView +{ + self.playerAsset = [AVAsset assetWithURL: self.mediaSource]; + self.playerItem = [AVPlayerItem playerItemWithAsset: self.playerAsset]; + self.player = [AVPlayer playerWithPlayerItem: self.playerItem]; + + ALNativeAdVideoView* videoView = [[ALNativeAdVideoView alloc] initWithPlayer: self.player]; + videoView.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; + + return videoView; +} + +-(void) playVideo +{ + [self.videoView.player play]; +} + +-(void) stopVideo +{ + [self.videoView.player pause]; +} + +-(void) setMediaSource:(NSURL *)mediaSource +{ + if (![_mediaSource isEqual: mediaSource]) { + + _mediaSource = mediaSource; + + self.playerAsset = [AVAsset assetWithURL: self.mediaSource]; + self.playerItem = [AVPlayerItem playerItemWithAsset: self.playerAsset]; + + [self.player replaceCurrentItemWithPlayerItem: self.playerItem]; + } +} +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_0.5.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_0.5.png new file mode 100644 index 0000000000000000000000000000000000000000..2e0f5341136a11ec50e5b5521e1dcb5ae0f20bb3 GIT binary patch literal 3806 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 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_0.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_0.png new file mode 100644 index 0000000000000000000000000000000000000000..9283ab8508ffa14fe7cda1f439268ada3c714271 GIT binary patch literal 3797 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 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_1.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_1.png new file mode 100644 index 0000000000000000000000000000000000000000..e14864d3cc55b5dd3485856bb71b032d8ce24829 GIT binary patch literal 3738 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 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_3.5.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_3.5.png new file mode 100644 index 0000000000000000000000000000000000000000..b96d07a0534cc80dab0cb9b37479244b4f0fca6c GIT binary patch literal 3810 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 AX8-^I literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_3.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_3.png new file mode 100644 index 0000000000000000000000000000000000000000..7ff58caeec1bf31eaee58e321edf5a2f37fe6ca9 GIT binary patch literal 3808 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 D?_8?D literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_4.5.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_4.5.png new file mode 100644 index 0000000000000000000000000000000000000000..8023c19deeeed82610f1fcca4144bc4d0f9c7368 GIT binary patch literal 3749 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 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_5.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/Star_Sprite_5.png new file mode 100644 index 0000000000000000000000000000000000000000..ad40f083079246df9d1c7822ef2b7444b67e7387 GIT binary patch literal 3849 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 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_learn_more.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_learn_more.png new file mode 100644 index 0000000000000000000000000000000000000000..21b8472308809bf8e881e6c8ae90ab14e36f2b9f GIT binary patch literal 11806 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 k!GXc@q+qV)8;AN&;02><{9 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_play.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_play.png new file mode 100644 index 0000000000000000000000000000000000000000..2acb854f1827a12957418f1e22aa868459f37d2a GIT binary patch literal 10380 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 KtXZ!Pi}^narUn53 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_replay.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_replay.png new file mode 100644 index 0000000000000000000000000000000000000000..2ed3bd3bfef802b7e2c14abd2c16186ae5f67744 GIT binary patch literal 15399 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 ZmADu$o@n+~T)(=jEUzy2?xAVe{{yXJ(ii{$ literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_unmuted.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Assets/applovin_card_unmuted.png new file mode 100644 index 0000000000000000000000000000000000000000..773cdb07fe2297e3cc7993e849795b1c80a44c4f GIT binary patch literal 5483 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface UIView (ALActivityIndicator) + +/** + * Show an activity indicator with a semi-transparent black overlay underneath without fade. + */ +- (void)al_showActivityIndicator; + +/** + * Show an activity indicator with a semi-transparent black overlay underneath with a fade animation option. + */ +- (void)al_showActivityIndicatorAnimated:(BOOL)animated; + +/** + * Hides the activity indicator view without fade. + */ +- (void)al_hideActivityIndicator; + +/** + * Hides the activity indicator view with a fade animation option. + */ +- (void)al_hideActivityIndicatorAnimated:(BOOL)animated; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Categories/UIView+ALActivityIndicator.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Categories/UIView+ALActivityIndicator.m new file mode 100644 index 0000000000..92e3121a1a --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Categories/UIView+ALActivityIndicator.m @@ -0,0 +1,87 @@ +// +// UIView+ALActivityIndicator.m +// sdk +// +// Created by Thomas So on 5/15/15. +// +// + +#import "UIView+ALActivityIndicator.h" +#import "ALCarouselView+Internal.h" + +@implementation UIView(ALActivityIndicator) +static NSString *const kActivityIndicatorKey = @"activityIndicator"; +static NSString *const kActivityIndicatorOverlayKey = @"activityIndicatorOverlay"; + +static const CGFloat kTargetOverlayAlpha = 1.0f; +static const CGFloat kAnimationDuration = 0.35f; + +- (void)al_showActivityIndicator +{ + [self al_showActivityIndicatorAnimated: NO]; +} + +- (void)al_showActivityIndicatorAnimated:(BOOL)animated +{ + UIView *overlay = [self valueForKey: kActivityIndicatorOverlayKey]; + if ( !overlay ) + { + overlay = [[UIView alloc] init]; + overlay.backgroundColor = [UIColor whiteColor]; + overlay.frame = self.bounds; + + NSLog(@"Created overlay with frame: %@", NSStringFromCGRect(overlay.frame)); + + [self setValue: overlay forKey: kActivityIndicatorOverlayKey]; + } + + UIActivityIndicatorView *activityIndicator = [self valueForKey: kActivityIndicatorKey]; + if ( !activityIndicator ) + { + activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleGray]; + activityIndicator.hidesWhenStopped = YES; + activityIndicator.center = self.center; + + [overlay addSubview: activityIndicator]; + [overlay bringSubviewToFront: activityIndicator]; + + [self setValue: activityIndicator forKey: kActivityIndicatorKey]; + } + + [activityIndicator startAnimating]; + overlay.alpha = animated ? 0.0f : kTargetOverlayAlpha; + + [self addSubview: overlay]; + [self bringSubviewToFront: overlay]; + + if ( animated ) + { + [UIView animateWithDuration: kAnimationDuration animations:^{ + overlay.alpha = kTargetOverlayAlpha; + }]; + } +} + +- (void)al_hideActivityIndicator +{ + [self al_hideActivityIndicatorAnimated: NO]; +} + +- (void)al_hideActivityIndicatorAnimated:(BOOL)animated +{ + UIView *overlay = [self valueForKey: kActivityIndicatorOverlayKey]; + if ( !overlay.superview ) + { + return; + } + + UIActivityIndicatorView *activityIndicator = [self valueForKey: kActivityIndicatorKey]; + [UIView animateWithDuration: animated ? kAnimationDuration : 0.0f animations:^{ + overlay.alpha = 0.0f; + } completion:^(BOOL finished) { + [activityIndicator stopAnimating]; + [overlay removeFromSuperview]; + }]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Customizable SETTINGS/ALCarouselViewSettings.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Customizable SETTINGS/ALCarouselViewSettings.h new file mode 100644 index 0000000000..84cad4d4fd --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Customizable SETTINGS/ALCarouselViewSettings.h @@ -0,0 +1,86 @@ +// +// ALCarouselViewSettings.h +// iOS Test App NG +// +// Created by Thomas So on 5/27/15. +// Copyright (c) 2015 AppLovin. All rights reserved. +// + +/** + * This file contains advanced UI configuration for ALCarouselView. + * Sizing and colors can be easily tweaked to your liking here. + */ + +#ifndef iOS_Test_App_NG_ALCarouselViewSettings_h +#define iOS_Test_App_NG_ALCarouselViewSettings_h + +#define kTextColor [UIColor darkTextColor] +#define kReplayTextColor [UIColor whiteColor] +#define kCarouselBackgroundColor [UIColor whiteColor] +#define kCardBackgroundColor [UIColor whiteColor] +#define kCardButtonColor [UIColor colorWithWhite: 0.84f alpha: 1.0f] +#define kVideoViewBackgroundColor [UIColor blackColor] +#define kVideoViewBackgroundWhilePlayingColor [UIColor blackColor] +#define kReplayOverlayBackgroundColor [UIColor blackColor] +#define kButtonHighlightTint [UIColor colorWithWhite: 0.87f alpha: 1.0f] + +// Media controls +static BOOL const kIsAutoplay = YES; +static BOOL const kVideoClicksThrough = YES; +static BOOL const kConfigIsMuted = YES; +static BOOL const kRenderVideoScreenshotAsFallbackImage = YES; + +// Replay overlay controls +static NSString *const kTextReplayVideo = @"Replay Video"; +static NSString *const kTextLearnMore = @"Learn More"; +static CGFloat const kConfigReplayOverlayAlpha = 0.75f; + + + +// Carousel constants +static NSUInteger const kNativeAdsToLoadCount = 3; +static NSString *const kFontFamily = @""; + +// Carousel layout constants +static CGFloat const kCardWidthPercentage = 0.90f; // As percentage of width of screen +static CGFloat const kCardMargin = 5.0f; // The margin on the side of each card. So a margin of 5px would result in a total of 10px separation card-to-card + +// Spring animation constants +static CGFloat const kSpringDuration = 0.3f; +static CGFloat const kDelay = 0.0f; +static CGFloat const kSpringDampeningGoNextCard = 0.9f; +static CGFloat const kSpringDampeningReturnSameCard = 0.7f; +static CGFloat const kInitialSpringVelocity = 0.0f; +static CGFloat const kConfigSwipeThreshold = 40.0f; // The number of pixels that will trigger a swipe event + +// Card layout constants +static CGFloat const kCardPadding = 10.0f; +static CGFloat const kTopMargin = 5.0f; +static CGFloat const kAppIconSize = 40.0f; +static CGFloat const kRatingHeight = 15.0f; +static CGFloat const kStarRatingTopPadding = 0.0f; +static CGFloat const kMaxStarRatingHeight = 15.0f; +static NSUInteger const kStarWidthToHeightMultiplier = 5; +static CGFloat const kDescriptionVerticalMargin = 16.0f; +static CGFloat const kVideoAspectRatio = 1.0f/1.78f; +static CGFloat const kDescriptionTextHeight = 36.0f; +static CGFloat const kCtaMaxHeight = 40.0f; +static CGFloat const kCtaCornerRadius = 3.0f; + +// Card configurations +static BOOL const kConfigEntireCardClickable = YES; +static CGFloat const kFontSizeTitle = 14.0f; +static CGFloat const kFontSizeDescription = 14.0f; +static CGFloat const kFontSizeButton = 18.0f; +static NSUInteger const kDescriptionMaxLines = 2; + +// Media layout constants +static CGFloat const kPadding = 10.0f; +static CGFloat const kMuteButtonPadding = 12.0f; +static CGFloat const kMuteWidth = 20.0f; +static CGFloat const kMuteHeight = 20.0f; +static CGFloat const kMuteButtonMargin = 8.0f; +static CGFloat const kPlayReplayWidth = 40.0f; +static CGFloat const kPlayReplayHeight = 40.0f; + +#endif \ No newline at end of file diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselCardView.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselCardView.h new file mode 100644 index 0000000000..399ed62aea --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselCardView.h @@ -0,0 +1,54 @@ +// +// ALCarouselCardView.h +// sdk +// +// Created by Thomas So on 4/20/15. +// +// + +#import + +#import "ALCarouselMediaView.h" +#import "ALCarouselViewModel.h" +#import "ALCarouselRenderingProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@class ALCarouselView; + +/** + * This view is used for paging of the carousel. + */ +@interface ALCarouselCardView : UIView + +/** + * The view containing the ad video or image. + */ +@property (strong, nonatomic) ALCarouselMediaView *mediaView; + +/** + * Initializes a newly allocated card view object with the specified sdk + */ +- (instancetype) initWithSdk:(ALSdk *)sdk; + +/** + * Redirects to the CTA for the given ad. + * + * @param ad The ad with the CTA URL to redirect to. + */ +- (void)handleClickForAd:(ALNativeAd *)ad; + +/** + Call this method when your view is displayed to the user. + Will track an impression. + */ +- (void)trackImpression; + +@property (strong, nonatomic, nullable) UIActivityIndicatorView *activityIndicator; +@property (strong, nonatomic, nullable) UIView *activityIndicatorOverlay; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselCardView.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselCardView.m new file mode 100644 index 0000000000..1e278814e3 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselCardView.m @@ -0,0 +1,353 @@ +// +// ALCarouselCardView.m +// sdk +// +// Created by Thomas So on 4/20/15. +// +// + +@import AppLovinSDK; +#import "ALCarouselCardView.h" +#import "ALCarouselCardState.h" +#import "UIView+ALActivityIndicator.h" +#import "ALCarouselViewSettings.h" + +@interface ALCarouselCardView() + +@property (weak, nonatomic) ALSdk *sdk; + +@property (strong, nonatomic) ALCarouselCardState *cardState; +@property (strong, nonatomic) ALNativeAd *ad; + +@property (strong, nonatomic) UITapGestureRecognizer *cardTapGesture; +@property (strong, nonatomic) UIView *contentView; +@property (strong, nonatomic) UIView *topBarContainer; +@property (strong, nonatomic) UIImageView *appIcon; +@property (strong, nonatomic) UILabel *titleLabel; +@property (strong, nonatomic) UIImageView *ratingImageView; +@property (strong, nonatomic) UILabel *descriptionLabel; +@property (strong, nonatomic) UIButton *ctaButton; + +@end + +@implementation ALCarouselCardView +static NSString *const TAG = @"ALCarouselCardView"; + +#pragma mark - Initialization + +- (instancetype)initWithSdk:(ALSdk *)sdk +{ + self = [super init]; + if ( self ) + { + [self baseInitWithSdk: sdk]; + } + return self; +} + +- (instancetype)initWithFrame: (CGRect) frame +{ + self = [super initWithFrame: frame]; + if ( self ) + { + [self baseInitWithSdk: [ALSdk shared]]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder: aDecoder]; + if ( self ) + { + [self baseInitWithSdk: [ALSdk shared]]; + } + return self; +} + +- (void)baseInitWithSdk:(ALSdk*) sdk +{ + self.sdk = sdk; + + self.backgroundColor = [UIColor clearColor]; + + self.cardTapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(didTapCard:)]; + self.cardTapGesture.enabled = kConfigEntireCardClickable; + [self addGestureRecognizer: self.cardTapGesture]; + + self.contentView = [[UIView alloc] init]; + self.contentView.backgroundColor = kCardBackgroundColor; + [self addSubview: self.contentView]; + + self.topBarContainer = [[UIView alloc] init]; + [self.contentView addSubview: self.topBarContainer]; + + self.appIcon = [[UIImageView alloc] init]; + self.appIcon.userInteractionEnabled = YES; + [self.contentView addSubview: self.appIcon]; + + UITapGestureRecognizer *appIconTapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(didTapAppIcon:)]; + [self.appIcon addGestureRecognizer: appIconTapGesture]; + + self.titleLabel = [[UILabel alloc] init]; + self.titleLabel.textColor = kTextColor; + [self.topBarContainer addSubview: self.titleLabel]; + + self.ratingImageView = [[UIImageView alloc] init]; + self.ratingImageView.contentMode = UIViewContentModeScaleAspectFit; + [self.topBarContainer addSubview: self.ratingImageView]; + + self.descriptionLabel = [[UILabel alloc] init]; + self.descriptionLabel.textColor = kTextColor; + self.descriptionLabel.numberOfLines = kDescriptionMaxLines; + [self.contentView addSubview: self.descriptionLabel]; + + self.mediaView = [[ALCarouselMediaView alloc] initWithSdk: self.sdk parentView: self]; + [self.contentView addSubview: self.mediaView]; + + self.ctaButton = [[UIButton alloc] init]; + self.ctaButton.layer.masksToBounds = YES; + self.ctaButton.layer.cornerRadius = kCtaCornerRadius; + self.ctaButton.backgroundColor = kCardButtonColor; + [self.ctaButton setTitleColor: kTextColor forState: UIControlStateNormal]; + [self.ctaButton addTarget: self action: @selector(didTapCTAButton:) forControlEvents: UIControlEventTouchUpInside]; + [self.contentView addSubview: self.ctaButton]; + + // Determine label fonts + if ( [[UIFont familyNames] containsObject: kFontFamily] ) + { + self.titleLabel.font = [UIFont fontWithName: kFontFamily size: kFontSizeTitle]; + self.descriptionLabel.font = [UIFont fontWithName: kFontFamily size: kFontSizeDescription]; + self.ctaButton.titleLabel.font = [UIFont fontWithName: kFontFamily size: kFontSizeButton]; + } + else + { + self.titleLabel.font = [UIFont systemFontOfSize: kFontSizeTitle]; + self.descriptionLabel.font = [UIFont systemFontOfSize: kFontSizeDescription]; + self.ctaButton.titleLabel.font = [UIFont systemFontOfSize: kFontSizeButton]; + } +} + +#pragma mark - Action Methods + +- (void)didTapCTAButton:(UIButton *)sender +{ + NSLog(@"Redirecting from cta button click"); + [self handleClickForAd: self.ad]; +} + +- (void)didTapAppIcon:(UITapGestureRecognizer *)tapGesture +{ + NSLog(@"Redirecting from app icon click"); + [self handleClickForAd: self.ad]; +} + +- (void)didTapCard:(UITapGestureRecognizer *)tapGesture +{ + NSLog(@"Redirecting from card click"); + [self handleClickForAd: self.ad]; +} + +#pragma mark - View Management + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + // Layout content view + CGRect contentFrame = CGRectZero; + contentFrame.origin.x = kCardMargin; + contentFrame.size.width = CGRectGetWidth(self.bounds) - (2*kCardMargin); + contentFrame.size.height = CGRectGetHeight(self.bounds); + self.contentView.frame = contentFrame; + + // Layout app icon + self.appIcon.frame = CGRectMake(kCardPadding, kTopMargin, kAppIconSize, kAppIconSize); + + // Layout title + CGRect titleFrame = CGRectZero; + titleFrame.size.width = (CGRectGetWidth(self.contentView.frame) - CGRectGetMaxX(self.appIcon.frame)) - (2*kCardPadding); + titleFrame.size.height = [self.titleLabel sizeThatFits: CGSizeMake(CGRectGetWidth(titleFrame), CGFLOAT_MAX)].height; + self.titleLabel.frame = titleFrame; + + // Layout star rating + const CGFloat ratingWidth = kMaxStarRatingHeight*kStarWidthToHeightMultiplier; + self.ratingImageView.frame = CGRectMake(CGRectGetMinX(titleFrame), CGRectGetMaxY(titleFrame) + kStarRatingTopPadding, ratingWidth, kRatingHeight); + + // Layout top bar + CGRect topBarFrame = CGRectZero; + topBarFrame.origin.x = CGRectGetMaxX(self.appIcon.frame) + kCardPadding; + topBarFrame.size.width = CGRectGetWidth(self.contentView.frame) - (3*kCardPadding) - kAppIconSize; + topBarFrame.size.height = CGRectGetHeight(self.titleLabel.frame) + kRatingHeight; + self.topBarContainer.frame = topBarFrame; + self.topBarContainer.center = CGPointMake(self.topBarContainer.center.x, self.appIcon.center.y); + + // Layout description + CGRect descriptionFrame = CGRectZero; + descriptionFrame.origin.x = kCardPadding; + descriptionFrame.origin.y = CGRectGetMaxY(self.topBarContainer.frame) + kDescriptionVerticalMargin; + descriptionFrame.size.width = CGRectGetWidth(self.contentView.frame) - (2*kCardPadding); + descriptionFrame.size.height = [self.descriptionLabel sizeThatFits: CGSizeMake(CGRectGetWidth(descriptionFrame), CGFLOAT_MAX)].height; + self.descriptionLabel.frame = descriptionFrame; + + // Layout media view + const CGFloat maxHeightPossible = (CGRectGetMaxY(self.frame) - kCardPadding) - (CGRectGetMaxY(self.descriptionLabel.frame) + kCardPadding); + const CGFloat fittedHeight = (CGRectGetWidth(self.frame)) * kVideoAspectRatio; + + CGRect mediaFrame = CGRectZero; + mediaFrame.origin.y = CGRectGetMaxY(self.topBarContainer.frame) + (2*kDescriptionVerticalMargin) + kDescriptionTextHeight; + mediaFrame.size.width = CGRectGetWidth(self.contentView.frame); + mediaFrame.size.height = MIN(maxHeightPossible, fittedHeight); + self.mediaView.frame = mediaFrame; + + // Layout CTA button (center it between bottom of video and bottom of card) + const CGFloat ctaOriginY = CGRectGetMaxY(self.mediaView.frame) + (CGRectGetMaxY(self.contentView.frame) - CGRectGetMaxY(self.mediaView.frame))/2 - kCtaMaxHeight/2; + + // If button will overflow bottom, hide it + BOOL willOverflow = (ctaOriginY + kCtaMaxHeight) > CGRectGetMaxY(self.contentView.frame); + if ( willOverflow ) + { + self.ctaButton.frame = CGRectZero; + self.ctaButton.hidden = YES; + } + else + { + CGRect ctaFrame = CGRectZero; + ctaFrame.origin.y = ctaOriginY; + ctaFrame.size.height = kCtaMaxHeight; + ctaFrame.size.width = (2*kCardPadding) + [self.ctaButton sizeThatFits: CGSizeMake(CGFLOAT_MAX, 0.0f)].width; + ctaFrame.origin.x = CGRectGetWidth(self.contentView.frame) - kCardPadding - CGRectGetWidth(ctaFrame); + + self.ctaButton.frame = ctaFrame; + } + + // Activity Views from category + self.activityIndicatorOverlay.frame = self.bounds; + self.activityIndicator.center = self.activityIndicatorOverlay.center; +} + +#pragma mark - View Rendering + +- (void)renderViewForNativeAd:(ALNativeAd *)ad cardState:(ALCarouselCardState *)cardState +{ + if ( ad ) + { + self.ad = ad; + self.cardState = cardState; + + [self refresh]; + } + else + { + [self clearView]; + } +} + +- (void)refresh +{ + NSLog(@"----------Begin refreshing carousel card view----------"); + + ALNativeAd *ad = self.ad; + + // If there is ad, render + if ( ad ) + { + NSLog(@"Refreshing ad (%@) for card view", ad.adIdNumber); + + self.titleLabel.text = ad.title; + self.descriptionLabel.text = ad.descriptionText; + self.mediaView.hidden = NO; + self.ctaButton.hidden = NO; + [self.ctaButton setTitle: ad.ctaText forState: UIControlStateNormal]; + [self populateStarRating: ad]; + + // If the images are pre-cached, just render them + if ( [ad isImagePrecached] ) + { + NSLog(@"Native ad (%@) is pre-cached. Refreshing image resources", ad.adIdNumber); + +#pragma mark - Populate with appropriate stars asset depending on starRating + self.appIcon.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: ad.iconURL]]; // Local URL + + [self.mediaView renderViewForNativeAd: self.ad cardState: self.cardState]; + + [self al_hideActivityIndicatorAnimated: YES]; + [self setNeedsLayout]; + } + else + { + [self clearView]; + [self al_showActivityIndicator]; + } + } + // There is no slot to render, so clear view + else + { + NSLog(@"Refreshing nil native ad for card view. Clearing view..."); + + [self clearView]; + [self al_hideActivityIndicatorAnimated: YES]; + } + + NSLog(@"----------Finish refreshing carousel card view----------"); +} + +#pragma mark - Utility + +- (void)handleClickForAd:(ALNativeAd *)ad +{ + if ( ad ) + { + [ad launchClickTarget]; + } + else + { + // Something is wrong, trying to redirect when card view does not have a native ad (like when ad is still loading) + NSLog(@"Attempting to open CTA URL with a nil native ad"); + } +} + +- (void)trackImpression +{ + if ( self.ad && self.cardState ) + { + NSLog(@"Handling displaying of native ad (%@)", self.ad.adIdNumber); + + if ( !self.cardState.impressionTracked ) + { + self.cardState.impressionTracked = YES; + NSLog(@"Tracking impression for ad (%@)", self.ad.adIdNumber); + [self.ad trackImpression]; + } + } + else + { + NSLog(@"Attempting to handle a nil slot or nil card state being displayed"); + } +} + +- (void)populateStarRating:(ALNativeAd*) ad +{ + NSString* filename = [NSString stringWithFormat: @"Star_Sprite_%@", ad.starRating.stringValue]; + UIImage* starRating = [UIImage imageNamed: filename]; + self.ratingImageView.image = starRating; +} + +- (void)clearView +{ + self.titleLabel.text = @""; + self.descriptionLabel.text = @""; + self.ratingImageView.image = nil; + self.appIcon.image = nil; + + self.ctaButton.hidden = YES; + [self.ctaButton setTitle: @"" forState: UIControlStateNormal]; + + self.mediaView.hidden = YES; + + // Reset State + self.cardState = nil; + self.ad = nil; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselMediaView.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselMediaView.h new file mode 100644 index 0000000000..bfb300920e --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselMediaView.h @@ -0,0 +1,36 @@ +// +// ALCarouselMediaView.h +// sdk +// +// Created by Thomas So on 4/20/15. +// +// + +#import + +#import "ALCarouselViewModel.h" +#import "ALCarouselRenderingProtocol.h" + +@class ALCarouselCardView; + +NS_ASSUME_NONNULL_BEGIN + +/** + * This view is used to store the ad's media. + */ +@interface ALCarouselMediaView : UIView + +/** + * Saves the current video's states and clears it. This is for when moving a slot out of the middle card. + */ +- (void)setInactive; + +/** + * Initializes a newly allocated media view object with the specified sdk and parent card view. + */ +- (instancetype)initWithSdk:(ALSdk *)sdk parentView:(ALCarouselCardView *)parentView; +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselMediaView.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselMediaView.m new file mode 100644 index 0000000000..8832a1d844 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselMediaView.m @@ -0,0 +1,598 @@ +// +// ALCarouselMediaView.m +// sdk +// +// Created by Thomas So on 4/20/15. +// +// + +@import AppLovinSDK; +#import "ALCarouselMediaView.h" +#import "ALCarouselCardState.h" +#import "ALCarouselReplayOverlayView.h" +#import "ALCarouselCardView.h" +#import "ALCarouselViewSettings.h" +#import "ALNativeAdVideoPlayer.h" +#import "ALNativeAdVideoView.h" + +@interface ALCarouselMediaView() + +@property (weak, nonatomic) ALSdk *sdk; + +@property (strong, nonatomic) ALCarouselCardView *cardView; +@property (strong, nonatomic) ALCarouselCardState *cardState; +@property (strong, nonatomic) ALNativeAd *ad; + +@property (strong, nonatomic) UIImageView *adImageView; + +@property (strong, nonatomic) ALNativeAdVideoPlayer *videoPlayer; +@property (weak, nonatomic) ALNativeAdVideoView *videoView; +@property (strong, nonatomic) UIButton *muteButton; +@property (strong, nonatomic) UIButton *playButton; +@property (strong, nonatomic) ALCarouselReplayOverlayView *replayOverlayView; + +@end + +@implementation ALCarouselMediaView +static NSString *const TAG = @"ALCarouselMediaView"; + +#pragma mark - Initialization Methods + +- (instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder:coder]; + if (self) + { + self.sdk = [ALSdk shared]; + [self setup]; + } + return self; +} + +- (instancetype)initWithSdk:(ALSdk *)sdk parentView:(ALCarouselCardView *)parentView; +{ + self = [super init]; + if ( self ) + { + self.sdk = sdk; + self.cardView = parentView; + [self setup]; + } + return self; +} + +- (void)setup +{ + self.backgroundColor = kVideoViewBackgroundColor; + + self.adImageView = [[UIImageView alloc] init]; + self.adImageView.userInteractionEnabled = NO; + self.adImageView.backgroundColor = [UIColor clearColor]; + + UITapGestureRecognizer *adImageTapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(didTapAdImage:)]; + [self.adImageView addGestureRecognizer: adImageTapGesture]; + + if ( self.replayOverlayView ) + { + [self.replayOverlayView removeFromSuperview]; + } + + self.replayOverlayView = [[ALCarouselReplayOverlayView alloc] initWithParentView: self]; + self.replayOverlayView.alpha = 0.0f; + self.replayOverlayView.userInteractionEnabled = YES; + [self.replayOverlayView.replayIconButton addTarget: self action: @selector(didTapReplayButton:) forControlEvents: UIControlEventTouchUpInside]; + [self.replayOverlayView.replayButton addTarget: self action: @selector(didTapReplayButton:) forControlEvents: UIControlEventTouchUpInside]; + [self.replayOverlayView.learnMoreIconButton addTarget: self action: @selector(didTapLearnMoreButton:) forControlEvents: UIControlEventTouchUpInside]; + [self.replayOverlayView.learnMoreButton addTarget: self action: @selector(didTapLearnMoreButton:) forControlEvents: UIControlEventTouchUpInside]; + + [self addSubview: self.adImageView]; + [self addSubview: self.replayOverlayView]; +} + +#pragma mark - App Notifications + +- (void)appPaused:(NSNotification *)notification +{ + [self deactivateIfNeeded]; +} + +- (void)appResumed:(NSNotification *)notification +{ + [self reactivateIfNeeded]; +} + +#pragma mark - View Management + +- (void)willMoveToWindow:(UIWindow *)newWindow +{ + [super willMoveToWindow: newWindow]; + + // Will be moved into a window + if ( newWindow ) + { + [self reactivateIfNeeded]; + + [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(appPaused:) name: UIApplicationDidEnterBackgroundNotification object: nil]; + [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(appResumed:) name: UIApplicationDidBecomeActiveNotification object: nil]; + } + // Will be removed from window + else + { + [self deactivateIfNeeded]; + + [[NSNotificationCenter defaultCenter] removeObserver: self]; + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + self.videoView.frame = self.bounds; + + const CGFloat muteButtonWidth = kMuteWidth + kMuteButtonPadding; + const CGFloat muteButtonHeight = kMuteHeight + kMuteButtonPadding; + + CGRect muteFrame = CGRectZero; + muteFrame.origin.x = kMuteButtonMargin; + muteFrame.origin.y = CGRectGetMaxY(self.videoView.frame) - muteButtonHeight - kMuteButtonMargin; + muteFrame.size.width = muteButtonWidth; + muteFrame.size.height = muteButtonHeight; + self.muteButton.frame = muteFrame; + self.muteButton.imageEdgeInsets = UIEdgeInsetsMake(kMuteButtonPadding, 0.0f, 0.0f, kMuteButtonPadding); + + self.playButton.frame = CGRectMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds), kPlayReplayWidth, kPlayReplayHeight); + self.playButton.center = self.videoView.center; + + self.replayOverlayView.frame = self.bounds; + self.adImageView.frame = self.cardState.screenshot ? self.cardState.videoRect : self.bounds; +} + +// Entry points +- (void)renderViewForNativeAd:(ALNativeAd *)ad +{ + ALCarouselCardState *cardState = [ALCarouselCardState cardStateForSingleCard]; + [self renderViewForNativeAd: ad cardState: cardState]; +} + +- (void)renderViewForNativeAd:(ALNativeAd *)ad cardState:(ALCarouselCardState *)cardState +{ + if ( ad ) + { + self.ad = ad; + self.cardState = cardState; + [self createVideoPlayerIfNeeded]; + [self refresh]; + } + else + { + [self clearView]; + } +} + +- (void)refresh +{ + const ALNativeAd *ad = self.ad; + if ( ad ) + { + // If we get to this point, ad image is pre-cached + NSLog(@"Begin refresh media view for slot ID: %@ %@", ad.adIdNumber, ad.title); + + self.adImageView.userInteractionEnabled = YES; + + // Populate ad image behind replay overlay + if ( self.cardState.screenshot && !CGRectIsEmpty(self.cardState.videoRect) ) + { + // If this card has a video screenshot rendered, set that as the ad image + self.adImageView.image = self.cardState.screenshot; + self.adImageView.frame = self.cardState.videoRect; + } + else + { + // Else, just set the ad image to the default one + self.adImageView.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: ad.imageURL]]; + self.adImageView.frame = self.bounds; + } + + // If the replay overlay is supposed to be visible, show it and hide the video controls + if ( self.cardState.replayOverlayVisible ) + { + self.replayOverlayView.alpha = 1.0f; + self.playButton.alpha = 0.0f; + self.muteButton.alpha = 0.0f; + } + // Else, hide the replay overlay. Visibility of video controls will be determined if autoplay is on/off + else + { + self.replayOverlayView.alpha = 0.0f; + } + + // If this is the middle/single card, we give it special treatment + if ( self.cardState.currentlyActive ) + { + // If the video is pre-cached or we should stream + if ( [ad isVideoPrecached] ) + { + // Autoplay if required (if config says so, and replay overlay is not currently showing) + [self autoplayIfRequired]; + } + // Video is still pre-caching. Will show on top of ad image when pre-cached from carousel view + else + { + NSLog(@"Video still waiting to be pre-cached for slot (%@)...", ad.adIdNumber); + } + } + else + { + // No special treatment for non-middle cards + } + } + else + { + NSLog(@"Begin refresh for nil slot. Clearing view..."); + [self clearView]; + } +} + +#pragma mark - Video Action Methods + +- (void)autoplayIfRequired +{ + self.adImageView.userInteractionEnabled = NO; + + // Update the mute button regardless if we're autoplaying since we'll have to animate it regardless + [self updateMuteState]; + + // If the video is not waiting to be replayed + if ( !self.cardState.replayOverlayVisible ) + { + // If configuration says we should autoplay + if ( kIsAutoplay ) + { + [self playVideoIfInactive]; + } + else + { + // If we're not going to autoplay the video, animate in the video controls + [UIView animateWithDuration: 0.5f animations:^{ + self.playButton.alpha = 1.0f; + }]; + } + } +} + +- (void)playVideoIfInactive +{ + if ( [self isCurrentlyPlayingVideo] ) + { + NSLog(@"Attempting to play a video that's already playing"); + } + else + { + NSLog(@"Video play requested..."); + + self.videoView.playerLayer.backgroundColor = kVideoViewBackgroundWhilePlayingColor.CGColor; + self.videoView.backgroundColor = kVideoViewBackgroundWhilePlayingColor; + + // Prepare the view to play video + [UIView animateWithDuration: 1.0f animations:^{ + + self.muteButton.alpha = 1.0f; + self.playButton.alpha = 0.0f; + + // Crossfade the video in and the ad image out then autoplay the video if needed + self.adImageView.alpha = 0.0f; + self.videoView.alpha = 1.0f; + }]; + + self.adImageView.userInteractionEnabled = NO; + + // When replaying a video we do not fade out the replay overlay + self.replayOverlayView.alpha = 0.0f; + self.cardState.replayOverlayVisible = NO; + + // Attach new observer to get notified when video ends + [[NSNotificationCenter defaultCenter] addObserver: self + selector: @selector(playerItemDidReachEnd:) + name: AVPlayerItemDidPlayToEndTimeNotification + object: self.videoView.player.currentItem]; + + // Prepare the video + self.videoPlayer.mediaSource = self.ad.videoURL; + [self seekToPosition: self.cardState.lastMediaPlayerPosition]; + self.cardState.videoStarted = YES; + [self.videoPlayer playVideo]; + + + // Track the video start if we didn't track it yet + if ( !self.cardState.wasVideoStartTracked ) + { + NSLog(@"Tracking video start for native ad (%@)", self.ad.adIdNumber); + [self.sdk.postbackService dispatchPostbackAsync: self.ad.videoStartTrackingURL andNotify: nil]; + + self.cardState.videoStartTracked = YES; + } + } +} + +- (void)setInactive +{ + self.adImageView.alpha = 1.0f; + self.adImageView.userInteractionEnabled = YES; + self.videoView.alpha = 0.0f; + self.muteButton.alpha = 0.0f; + self.playButton.alpha = 0.0f; + + // Reset background colors + self.backgroundColor = kVideoViewBackgroundColor; + self.videoView.playerLayer.backgroundColor = kVideoViewBackgroundColor.CGColor; + self.videoView.backgroundColor = kVideoViewBackgroundColor; + + [self deactivateIfNeeded]; +} + +- (void)playerItemDidReachEnd:(NSNotification *)notification +{ + NSLog(@"Video finished playing for native ad (%@)", self.ad.adIdNumber); + + self.cardState.lastMediaPlayerPosition = 0.0f; + self.cardState.videoCompleted = YES; + self.cardState.replayOverlayVisible = YES; + + [UIView animateWithDuration: 0.5f animations:^{ + self.muteButton.alpha = 0.0f; + self.replayOverlayView.alpha = 1.0f; + }]; + + [self handleVideoStopPlaying]; +} + +#pragma mark - Action Methods + +- (void)didTapMuteButton:(UIButton *)muteButton +{ + self.cardState.muteState = self.cardState.muteState == ALMuteStateMuted ? ALMuteStateUnmuted : ALMuteStateMuted; + [self updateMuteState]; +} + +- (void)didTapPlayButton:(UIButton *)sender +{ + [self playVideoIfInactive]; +} + +- (void)didTapReplayButton:(UIButton *)sender +{ + [self playVideoIfInactive]; +} + +- (void)didTapLearnMoreButton:(UIButton *)sender +{ + NSLog(@"Redirecting from Learn More button"); + [self handleClick]; +} + +- (void)didTapVideo:(UITapGestureRecognizer *)tapGesture +{ + if ( kVideoClicksThrough ) + { + NSLog(@"Redirecting from video click"); + [self handleClick]; + [self setInactive]; + } + else + { + if ( [self isCurrentlyPlayingVideo] ) + { + [self setInactive]; + [UIView animateWithDuration: 0.5f animations:^{ + self.playButton.alpha = 1.0f; + }]; + } + else + { + [self playVideoIfInactive]; + } + } +} + +- (void)didTapAdImage:(UITapGestureRecognizer *)tapGesture +{ + NSLog(@"Redirecting from ad image click"); + [self handleClick]; +} + +- (void)handleClick +{ + if ( self.cardView ) + { + [self.cardView handleClickForAd: self.ad]; + } + else + { + [self.ad launchClickTarget]; + } +} + +#pragma mark - Video Utility Methods + +- (Float64)currentVideoPosition +{ + return CMTimeGetSeconds(self.videoView.player.currentTime); +} + +- (Float64)videoDuration +{ + return CMTimeGetSeconds(self.videoPlayer.playerAsset.duration); +} + +- (NSNumber *)percentViewed +{ + Float64 viewedRatio = [self currentVideoPosition] / [self videoDuration]; + return @( viewedRatio * 100 ); +} + +- (UIImage *)frameForVideoAtCurrentPosition +{ + AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset: self.videoPlayer.playerAsset]; + + // Set the tolerance so we have a precise screenshot + if ( [imageGenerator respondsToSelector: @selector(setRequestedTimeToleranceBefore:)] && + [imageGenerator respondsToSelector: @selector(setRequestedTimeToleranceAfter:)] ) + { + [imageGenerator setRequestedTimeToleranceBefore: kCMTimeZero]; + [imageGenerator setRequestedTimeToleranceAfter: kCMTimeZero]; + } + + return [UIImage imageWithCGImage: [imageGenerator copyCGImageAtTime: self.videoView.player.currentItem.currentTime + actualTime: nil + error: nil]]; +} + +- (void)handleVideoStopPlaying +{ + self.videoView.playerLayer.backgroundColor = kVideoViewBackgroundColor.CGColor; + + if ( kRenderVideoScreenshotAsFallbackImage ) + { + self.cardState.screenshot = [self frameForVideoAtCurrentPosition]; + self.adImageView.image = self.cardState.screenshot; + + // Save the video rect, so at layoutSubviews: in an inactive card, we know what aspect ratio size the screenshot + // should be rendered in. + self.cardState.videoRect = self.videoView.playerLayer.videoRect; + } + + [[NSNotificationCenter defaultCenter] removeObserver: self name: AVPlayerItemDidPlayToEndTimeNotification object: nil]; + + // Track video completion if we havn't already + if ( self.cardState.videoStarted ) + { + NSURL* postbackUrl = [self.ad videoEndTrackingURL: [[self percentViewed] unsignedIntegerValue] firstPlay: self.cardState.firstPlayback]; + [self.sdk.postbackService dispatchPostbackAsync: postbackUrl andNotify: nil]; + } + + self.cardState.firstPlayback = NO; +} + +- (BOOL)isCurrentlyPlayingVideo +{ + return self.videoView.player.rate > 0.0f; +} + +- (void)seekToPosition:(Float64)position +{ + CMTimeScale timeScale = self.videoView.player.currentItem.asset.duration.timescale; + CMTime time = CMTimeMakeWithSeconds(position, timeScale); + [self.videoView.player seekToTime: time toleranceBefore: kCMTimeZero toleranceAfter: kCMTimeZero]; +} + +- (void)updateMuteState +{ + ALMuteState currentState = self.cardState.muteState; + + // If the current state is unspecify, determine if it should be muted or unmuted according to settings + if ( currentState == ALMuteStateUnspecified ) + { + currentState = kConfigIsMuted ? ALMuteStateMuted : ALMuteStateUnmuted; + self.cardState.muteState = currentState; + } + + if ( currentState == ALMuteStateMuted ) + { + self.muteButton.selected = YES; + self.videoView.player.muted = YES; + } + else if ( currentState == ALMuteStateUnmuted ) + { + self.muteButton.selected = NO; + self.videoView.player.muted = NO; + } +} + +#pragma mark - Utility Methods + +- (void)clearView +{ + self.adImageView.image = nil; + self.adImageView.alpha = 0.0f; + self.adImageView.userInteractionEnabled = NO; + self.videoView.alpha = 0.0f; + self.replayOverlayView.alpha = 0.0f; + self.cardState = nil; +} + +// Called when resuming app or going back into the containing VC +- (void)reactivateIfNeeded +{ + if ( self.cardState.currentlyActive && [self.ad isVideoPrecached] ) + { + [self autoplayIfRequired]; + } +} + +// Called when pausing app or leaving VC *OR* when setting a card inactive +- (void)deactivateIfNeeded +{ + if ( [self isCurrentlyPlayingVideo] ) + { + self.cardState.lastMediaPlayerPosition = [self currentVideoPosition]; + + [self.videoPlayer stopVideo]; + [self handleVideoStopPlaying]; + } +} + +- (void)destroyVideoPlayer +{ + // We don't need a video player; remove any one left behind. + [self.videoPlayer stopVideo]; + self.videoPlayer = nil; + + [self.videoView removeFromSuperview]; + self.videoView = nil; + + [self.muteButton removeFromSuperview]; + [self.playButton removeFromSuperview]; + + self.playButton = nil; + self.muteButton = nil; +} + +- (void)createVideoPlayerIfNeeded +{ + [self destroyVideoPlayer]; + + // Create video player only for middle card + if ( self.cardState.currentlyActive && [self.ad isVideoPrecached] ) + { + self.videoPlayer = [[ALNativeAdVideoPlayer alloc] initWithMediaSource: nil]; + self.videoView.player.actionAtItemEnd = AVPlayerActionAtItemEndPause; + self.videoView.playerLayer.backgroundColor = [UIColor clearColor].CGColor; + + self.videoView = self.videoPlayer.videoView; + [self.videoView.playerLayer setNeedsDisplay]; + self.videoView.backgroundColor = kVideoViewBackgroundColor; + + self.muteButton = [[UIButton alloc] init]; + self.muteButton.alpha = 0.0f; + [self.muteButton addTarget: self action: @selector(didTapMuteButton:) forControlEvents: UIControlEventTouchUpInside]; + + self.playButton = [[UIButton alloc] init]; + self.playButton.alpha = 0.0f; + [self.playButton addTarget: self action: @selector(didTapPlayButton:) forControlEvents: UIControlEventTouchUpInside]; + + UITapGestureRecognizer *videoTapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(didTapVideo:)]; + [self.videoView addGestureRecognizer: videoTapGesture]; + + [self insertSubview: self.videoView belowSubview: self.adImageView]; + [self insertSubview: self.playButton aboveSubview: self.adImageView]; + [self insertSubview: self.muteButton aboveSubview: self.videoView]; + + // Popualte the assets + [self.playButton setImage: [UIImage imageNamed: @"applovin_card_play"] forState: UIControlStateNormal]; + [self.muteButton setImage: [UIImage imageNamed: @"applovin_card_unmuted"] forState: UIControlStateNormal]; + [self.muteButton setImage: [UIImage imageNamed: @"applovin_card_muted"] forState: UIControlStateSelected]; + } +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselReplayOverlayView.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselReplayOverlayView.h new file mode 100644 index 0000000000..7c14e79d75 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselReplayOverlayView.h @@ -0,0 +1,29 @@ +// +// ALCarouselReplayOverlayView.h +// sdk +// +// Created by Thomas So on 4/22/15. +// +// + +@import UIKit; +#import "ALCarouselMediaView.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * This view provides a few buttons to allow the user to replay or click through an ad. + */ +@interface ALCarouselReplayOverlayView : UIView + +@property (strong, nonatomic) UIView *overlay; +@property (strong, nonatomic) UIButton *replayButton; +@property (strong, nonatomic) UIButton *replayIconButton; +@property (strong, nonatomic) UIButton *learnMoreButton; +@property (strong, nonatomic) UIButton *learnMoreIconButton; + +- (instancetype)initWithParentView:(ALCarouselMediaView *)parentView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselReplayOverlayView.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselReplayOverlayView.m new file mode 100644 index 0000000000..02a4d8768b --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselReplayOverlayView.m @@ -0,0 +1,99 @@ +// +// ALCarouselReplayOverlayView.m +// sdk +// +// Created by Thomas So on 4/22/15. +// +// + +#import "ALCarouselReplayOverlayView.h" +#import "ALCarouselViewSettings.h" + +@interface ALCarouselReplayOverlayView() +@property (weak, nonatomic) ALCarouselMediaView *mediaView; +@end + +@implementation ALCarouselReplayOverlayView + +#pragma mark - Initialization + +- (instancetype)initWithParentView:(ALCarouselMediaView *)parentView +{ + self = [super init]; + if ( self ) + { + self.mediaView = parentView; + + self.backgroundColor = [UIColor clearColor]; + + self.overlay = [[UIView alloc] init]; + + self.overlay.backgroundColor = kReplayOverlayBackgroundColor; + self.overlay.alpha = kConfigReplayOverlayAlpha; + [self addSubview: self.overlay]; + + self.replayIconButton = [[UIButton alloc] init]; + [self addSubview: self.replayIconButton]; + + self.replayButton = [[UIButton alloc] init]; + [self addSubview: self.replayButton]; + + self.learnMoreIconButton = [[UIButton alloc] init]; + [self addSubview: self.learnMoreIconButton]; + + self.learnMoreButton = [[UIButton alloc] init]; + [self addSubview: self.learnMoreButton]; + + [self.replayIconButton setTintColor: kButtonHighlightTint]; + [self.replayButton setTitleColor: kReplayTextColor forState: UIControlStateNormal]; + [self.replayButton setTitleColor: kButtonHighlightTint forState: UIControlStateHighlighted]; + [self.learnMoreButton setTitleColor: kReplayTextColor forState: UIControlStateNormal]; + [self.learnMoreButton setTitleColor: kButtonHighlightTint forState: UIControlStateHighlighted]; + [self.learnMoreIconButton setTintColor: kButtonHighlightTint]; + + [self.replayIconButton setImage: [UIImage imageNamed: @"applovin_card_replay"] forState: UIControlStateNormal]; + [self.learnMoreIconButton setImage: [UIImage imageNamed: @"applovin_card_learn_more"] forState: UIControlStateNormal]; + + [self.replayButton setTitle: kTextReplayVideo forState: UIControlStateNormal]; + [self.learnMoreButton setTitle: kTextLearnMore forState: UIControlStateNormal]; + } + return self; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + self.overlay.frame = self.bounds; + + const CGFloat replayButtonWidth = [self.replayButton sizeThatFits: CGSizeMake(CGFLOAT_MAX, 0.0f)].width; + const CGFloat totalReplayWidth = kPlayReplayWidth + kPadding + replayButtonWidth; + + const CGFloat learnMoreButtonWidth = [self.learnMoreButton sizeThatFits: CGSizeMake(CGFLOAT_MAX, 0.0f)].width; + const CGFloat totalLearnMoreWidth = kPlayReplayWidth + kPadding + learnMoreButtonWidth; + + const CGFloat totalContentHeight = (2*kPlayReplayHeight) + kPadding; + + // We will center and align depending on which button has the longer total width + const CGFloat longerWidth = totalReplayWidth >= totalLearnMoreWidth ? totalReplayWidth : totalLearnMoreWidth; + + self.replayIconButton.frame = CGRectMake(CGRectGetMidX(self.frame) - longerWidth/2.0f, + CGRectGetMidY(self.frame) - totalContentHeight/2.0f, + kPlayReplayWidth, + kPlayReplayHeight); + self.replayButton.frame = CGRectMake(CGRectGetMaxX(self.replayIconButton.frame) + kPadding, + CGRectGetMinY(self.replayIconButton.frame), + replayButtonWidth, + kPlayReplayHeight); + + self.learnMoreIconButton.frame = CGRectMake(CGRectGetMidX(self.frame) - longerWidth/2.0f, + CGRectGetMaxY(self.replayIconButton.frame) + kPadding, + kPlayReplayWidth, + kPlayReplayHeight); + self.learnMoreButton.frame = CGRectMake(CGRectGetMaxX(self.learnMoreIconButton.frame) + kPadding, + CGRectGetMaxY(self.replayIconButton.frame) + kPadding, + learnMoreButtonWidth, + kPlayReplayHeight); +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselView.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselView.h new file mode 100644 index 0000000000..ba438deb10 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselView.h @@ -0,0 +1,34 @@ +// +// ALCarouselView.h +// +// Created by Thomas So on 3/30/15. +// Copyright (c) 2015, AppLovin Corporation. All rights reserved. +// + +@import AppLovinSDK; + +NS_ASSUME_NONNULL_BEGIN + +/** + * This class is used to display native ads to the user. + */ +@interface ALCarouselView : UIView + +/** + * An object conforming to the ALNativeAdGroupLoadDelegate protocol, which, if set, will be notified of ad load events. + */ +@property (weak, nonatomic, nullable) id loadDelegate; + +/** + * The current native ad(s) being displayed. + */ +@property (strong, nonatomic, readonly) NSArray *nativeAds; + +- (instancetype)initWithFrame:(CGRect)frame; +- (instancetype)initWithFrame:(CGRect)frame sdk:(ALSdk *)sdk; +- (instancetype)initWithFrame:(CGRect)frame sdk:(ALSdk *)sdk nativeAds:(NSArray *)nativeAds; +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselView.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselView.m new file mode 100644 index 0000000000..de4b17b0b3 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALCarouselView.m @@ -0,0 +1,642 @@ +// +// ALCarouselView.m +// +// Created by Thomas So on 3/30/15. +// Copyright (c) 2015, AppLovin Corporation. All rights reserved. +// + +@import AppLovinSDK; +#import "ALCarouselView.h" +#import "ALCarouselView+Internal.h" +#import "UIView+ALActivityIndicator.h" +#import "ALCarouselCardView.h" +#import "ALCarouselCardState.h" +#import "ALCarouselViewModel.h" +#import "ALCarouselViewSettings.h" + +@class ALCarouselView; + +/** + * This class acts as an intermediary between pre-cache events of slots and which card to notify. + */ +@interface ALCarouselPrecacheRouter : NSObject + +@property (copy, nonatomic) NSString *tag; +@property (weak, nonatomic) ALCarouselView *carouselView; + +- (instancetype)initWithCarouselView:(ALCarouselView *)carouselView; + +@end + +@interface ALCarouselView() + +@property (strong, nonatomic) ALSdk *sdk; +@property (weak, nonatomic) ALPostbackService *postbackService; + +@property (weak, atomic) NSArray *previousNativeAdsRendered; +@property (strong, nonatomic) ALCarouselViewModel *carouselModel; +@property (assign, nonatomic) NSInteger currentAdIndex; + +@property (strong, nonatomic) ALCarouselPrecacheRouter *precacheRouter; + +// This array has 5 card objets that are used to render the ad +@property (strong, nonatomic) NSArray *cardViews; +@property (strong, nonatomic) UIView *contentView; +@property (strong, nonatomic) UIPanGestureRecognizer *panGesture; + +@end + +#pragma mark ALCarouselView + +@implementation ALCarouselView +static NSString * const TAG = @"ALCarouselView"; + +// Card constants +static NSInteger const kNumSideCards = 2; +static NSInteger const kNumCards = 5; +static NSInteger const kMidCardIndex = 2; + +#pragma mark - Initialization + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder: aDecoder]; + if ( self ) + { + [self baseInitWithSdk: [ALSdk shared]]; + } + return self; +} + +- (instancetype)init +{ + self = [super init]; + if (self) + { + self.sdk = [ALSdk shared]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + return [self initWithFrame: frame sdk: [ALSdk shared]]; +} + +- (instancetype)initWithFrame:(CGRect)frame sdk:(ALSdk *)sdk +{ + return [self initWithFrame: frame sdk: sdk nativeAds: @[]]; +} + +- (instancetype)initWithFrame:(CGRect)frame sdk:(ALSdk *)sdk nativeAds:(NSArray *)nativeAds +{ + self = [super initWithFrame: frame]; + if ( self ) + { + [self baseInitWithSdk: sdk]; + self.nativeAds = nativeAds; + } + return self; +} + +- (void)baseInitWithSdk:(ALSdk *)sdk +{ + self.sdk = sdk; + self.postbackService = sdk.postbackService; + + self.currentAdIndex = 0; + + self.precacheRouter = [[ALCarouselPrecacheRouter alloc] initWithCarouselView: self]; + + // Setup View + self.userInteractionEnabled = YES; + self.clipsToBounds = YES; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.backgroundColor = kCarouselBackgroundColor; + + self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; + self.panGesture.delegate = self; + self.panGesture.delaysTouchesBegan = NO; + self.panGesture.delaysTouchesEnded = NO; + [self addGestureRecognizer: self.panGesture]; + + self.contentView = [[UIView alloc] init]; + self.contentView.backgroundColor = kCarouselBackgroundColor; + [self addSubview: self.contentView]; + + NSMutableArray* tempCards = [NSMutableArray arrayWithCapacity: kNumCards]; + for ( NSInteger i = 0; i < kNumCards; ++i ) + { + ALCarouselCardView *cardView = [[ALCarouselCardView alloc] initWithSdk: sdk]; + cardView.backgroundColor = [UIColor clearColor]; + cardView.hidden = YES; + + + [self.contentView addSubview: cardView]; + + [tempCards addObject: cardView]; + } + + self.cardViews = [NSArray arrayWithArray: tempCards]; +} + +-(void) didMoveToSuperview +{ + [super didMoveToSuperview]; + + // If there are attached from initialization + if ( self.nativeAds.count > 0 ) + { + // All of the objects in the array are checked to be of proper type in setter. Render. + [self renderAdsIfNeeded]; + } + // If there isn't currently any native ad(s) attached, load one. + else + { + [self al_showActivityIndicator]; + + // AppLovin SDK has deprecated loading of multiple native ads + [self.sdk.nativeAdService loadNextAdAndNotify: self]; + //[self.sdk.nativeAdService loadNativeAdGroupOfCount: kNativeAdsToLoadCount andNotify: self]; + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + const CGFloat cardWidth = (CGRectGetWidth(self.bounds) * kCardWidthPercentage) - (2 * kCardMargin); + const CGFloat contentWidth = kNumCards * cardWidth; + + CGRect contentFrame = CGRectZero; + contentFrame.origin.x = CGRectGetMidX(self.bounds) - (contentWidth/2); + contentFrame.size.width = contentWidth; + contentFrame.size.height = CGRectGetHeight(self.bounds); + self.contentView.frame = contentFrame; + + CGFloat currentX = 0.0f; + for ( ALCarouselCardView *cardView in self.cardViews ) + { + cardView.frame = CGRectMake(currentX, 0.0f, cardWidth, CGRectGetHeight(self.bounds)); + currentX += cardWidth; + } + + // Activity Views from category + self.activityIndicatorOverlay.frame = self.bounds; + self.activityIndicator.center = self.activityIndicatorOverlay.center; +} + +#pragma mark - Native Ad Load Delegate + +- (void)nativeAdService:(ALNativeAdService *)service didLoadAds:(NSArray *)ads +{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + self.nativeAds = ads; + [self renderAdsIfNeeded]; + + @try + { + if ( [(id)self.loadDelegate respondsToSelector: @selector(nativeAdService:didLoadAds:)] ) + { + [self.loadDelegate nativeAdService: service didLoadAds: ads]; + } + } + @catch (NSException *exception) + { + NSLog(@"Unable to notify native ad load delegate because of exception: %@", exception); + } + }]; +} + +- (void)nativeAdService:(ALNativeAdService *)service didFailToLoadAdsWithError:(NSInteger)code +{ + NSLog(@"Native ad service did fail to load native ad with error: %ld", code); + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + @try + { + if ( [(id)self.loadDelegate respondsToSelector: @selector(nativeAdService:didFailToLoadAdsWithError:)] ) + { + [self.loadDelegate nativeAdService: service didFailToLoadAdsWithError: code]; + } + } + @catch (NSException *exception) + { + NSLog(@"Unable to notify native ad load delegate about failing to load because of exception: %@", exception); + } + }]; +} + +#pragma mark - View Rendering + +- (void)renderAdsIfNeeded +{ + if ( [self.nativeAds isEqual: self.previousNativeAdsRendered] ) + { + NSLog(@"Attempting to re-render native ad(s)"); + } + else + { + // Keep track of native ad(s) rendered to prevent re-rendering + self.previousNativeAdsRendered = self.nativeAds; + + self.carouselModel = [[ALCarouselViewModel alloc] initWithNativeAds: self.nativeAds]; + + self.panGesture.enabled = self.nativeAds.count > 1; // Don't allow swiping if only one card + + self.currentAdIndex = 0; + + [self refreshView]; + } +} + +- (void)refreshView +{ + NSLog(@"Begin refreshing carousel view for number of native ads: %lu", self.nativeAds.count); + + // Update/save states before refreshing each card + for ( ALCarouselCardView *cardView in self.cardViews ) + { + [cardView.mediaView setInactive]; + } + + for ( NSInteger cardIndex = 0; cardIndex < kNumCards ; ++cardIndex ) + { + NSLog(@"Begin refreshing for card at index %ld", cardIndex); + + ALCarouselCardView *cardView = self.cardViews[cardIndex]; + + // Determine what slot should be displaying in the card with index 'cardIndex' now + // Please Note: Will return an out-of-bounds if we're not supposed to render ad for card at this index + NSInteger adIndex = [self adIndexForCardIndex: cardIndex]; + + // If ad exists for this card view, render + if ( (adIndex >= 0) && (adIndex < self.nativeAds.count) ) + { + NSLog(@"Refreshing card view at index: %ld with slot at index: %ld", cardIndex, adIndex); + ALNativeAd *ad = self.nativeAds[adIndex]; + + ALCarouselCardState *cardState = [self.carouselModel cardStateAtNativeAdIndex: adIndex]; + cardState.currentlyActive = (cardIndex == kMidCardIndex); + + if ( [ad isImagePrecached] ) + { + NSLog(@"Card at index: %ld currently has its images pre-cached", cardIndex); + + [cardView renderViewForNativeAd: ad cardState: cardState]; + + // Only middle (active) card can be clicked on + cardView.userInteractionEnabled = cardState.currentlyActive; + cardView.hidden = NO; + + if ( cardState.currentlyActive ) + { + // Handle events when middle card is displayed + [cardView trackImpression]; + } + } + // If images are not pre-cached, then videos are not pre-cached as well + else + { + NSLog(@"Card at index: %ld is not pre-cached", cardIndex); + + [cardView clearView]; + [cardView al_showActivityIndicator]; + cardView.hidden = NO; + + if ( cardState.precaching ) + { + NSLog(@"Card at index: %ld is already currently pre-caching", cardIndex); + } + else + { + cardState.precaching = YES; + + NSLog(@"Begin pre-caching for card at index: %ld", cardIndex); + [self.sdk.nativeAdService precacheResourcesForNativeAd: ad andNotify: self.precacheRouter]; + } + } + } + // Slot does not exist for this card view, hide + else + { + NSLog(@"Hiding card at card index: %ld", cardIndex); + + [cardView clearView]; + cardView.hidden = YES; + + if ( cardIndex == kMidCardIndex ) + { + NSLog(@"Hiding middle card because of nil ad."); + } + } + } + + [self al_hideActivityIndicatorAnimated: YES]; + + NSLog(@"Finish refreshing carousel view"); +} + +- (void)clearView +{ + for ( ALCarouselCardView *cardView in self.cardViews ) + { + [cardView clearView]; + } + [self.carouselModel removeAllObjects]; +} + +#pragma mark - Overridden Getters/Setters + +- (void)setNativeAds:(NSArray * __nullable)nativeAds +{ + if ( self.nativeAds.count > 0 ) + { + // Check if array contains objects of the proper ad type + for ( id obj in self.nativeAds ) + { + if ( ![obj isKindOfClass: [ALNativeAd class]] ) + { + // Found an object of invalid type + NSLog(@"Found an object of invalid type (%@) in nativeAds", NSStringFromClass([obj class])); + + return; + } + } + } + else + { + // We clear the view if native ads is set to an empty array + NSLog(@"Setting native ads of count 0. Clearing view..."); + [self clearView]; + } + + _nativeAds = nativeAds; +} + +- (void)setCurrentAdIndex:(NSInteger)currentAdIndex +{ + if ( currentAdIndex < self.nativeAds.count ) + { + if ( self.currentAdIndex == currentAdIndex ) + { + NSLog(@"Setting same current ad index of %ld", currentAdIndex); + } + else + { + NSLog(@"Setting new current ad index of %ld", currentAdIndex); + + _currentAdIndex = currentAdIndex; + + [self refreshView]; + } + } + else + { + NSLog(@"Setting out-of-bounds index of %ld", currentAdIndex); + } +} + +#pragma mark - Utility + +- (NSInteger)adIndexForCardIndex:(NSUInteger)cardIndex +{ + return self.currentAdIndex + cardIndex - kNumSideCards; +} + +#pragma mark - Gesture Recognizers + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + if ( [gestureRecognizer isEqual: self.panGesture] ) + { + // Our pan gesture should only recognize horizontal pans + CGPoint translation = [self.panGesture velocityInView: self]; + return fabs(translation.y) < fabs(translation.x); + } + + return YES; +} + +- (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer +{ + switch ( recognizer.state ) + { + case UIGestureRecognizerStateChanged: + { + CGFloat xOffset = [recognizer translationInView: self].x; + self.contentView.frame = CGRectOffset(self.contentView.frame, xOffset, 0.0f); + + [recognizer setTranslation: CGPointZero inView: self]; + + break; + } + case UIGestureRecognizerStateEnded: + { + const CGFloat cardWidth = (CGRectGetWidth(self.bounds) * kCardWidthPercentage) - (2 * kCardMargin); + const CGFloat contentOffset = fabs(self.center.x - self.contentView.center.x); + const CGFloat percentageSwiped = 1.0f - (contentOffset/CGRectGetWidth(self.bounds)); + const CGFloat springDuration = percentageSwiped * kSpringDuration; + + const BOOL exceedsThreshold = contentOffset > kConfigSwipeThreshold; + const BOOL leftToRight = [recognizer velocityInView: self].x > 0.0f; + + // If user is swiping left to right + if ( leftToRight ) + { + // If the middle card has a slot to the left of it, then execute the swipe + if ( self.currentAdIndex >= 1 && exceedsThreshold ) + { + [UIView animateWithDuration: springDuration + delay: kDelay + usingSpringWithDamping: kSpringDampeningGoNextCard + initialSpringVelocity: kInitialSpringVelocity + options: UIViewAnimationOptionCurveEaseInOut + animations: ^{ + + self.contentView.center = CGPointMake( (CGRectGetWidth(self.frame)/2.0f) + cardWidth, self.contentView.center.y); + } + completion: ^(BOOL finished) { + + if ( finished ) + { + --self.currentAdIndex; + [self setNeedsLayout]; + } + }]; + } + // There are no slots to the left of the middle card anymore, spring back + else + { + [UIView animateWithDuration: kSpringDuration + delay: kDelay + usingSpringWithDamping: kSpringDampeningReturnSameCard + initialSpringVelocity: kInitialSpringVelocity + options: UIViewAnimationOptionCurveEaseInOut + animations: ^{ + + self.contentView.center = CGPointMake(0.5f * CGRectGetWidth( self.frame ), self.contentView.center.y); + } + completion:nil]; + } + } + // We are swiping right to left + else + { + // If there is a slot to the right of the middle card, execute the swipe + if ( self.currentAdIndex < [self.carouselModel nativeAdsCount]-1 && exceedsThreshold ) + { + [UIView animateWithDuration: springDuration + delay: kDelay + usingSpringWithDamping: kSpringDampeningGoNextCard + initialSpringVelocity: kInitialSpringVelocity + options: UIViewAnimationOptionCurveEaseInOut + animations: ^{ + + self.contentView.center = CGPointMake( (CGRectGetWidth(self.frame)/2.0f) - cardWidth, self.contentView.center.y); + } + completion:^(BOOL finished) { + + if ( finished ) + { + ++self.currentAdIndex; + [self setNeedsLayout]; + } + }]; + } + // There are no slots to the right of the middle card anymore. Spring back + else + { + [UIView animateWithDuration: kSpringDuration + delay: kDelay + usingSpringWithDamping: kSpringDampeningReturnSameCard + initialSpringVelocity: kInitialSpringVelocity + options: UIViewAnimationOptionCurveEaseInOut + animations: ^{ + + self.contentView.center = CGPointMake(0.5f * CGRectGetWidth( self.frame ), self.contentView.center.y); + } + completion:nil]; + } + } + break; + } + default: + { + break; + } + } +} + +@end + +#pragma mark ALCarouselPrecacheRouter + +@implementation ALCarouselPrecacheRouter + +#pragma mark - Initialization + +- (instancetype)initWithCarouselView:(ALCarouselView *)carouselView +{ + self = [super init]; + + if ( self ) + { + self.tag = @"ALCarouselPrecacheRouter"; + self.carouselView = carouselView; + } + + return self; +} + +#pragma mark - Precache Delegate Methods + +- (void)nativeAdService:(ALNativeAdService *)service didPrecacheImagesForAd:(ALNativeAd *)ad +{ + NSLog(@"Finished pre-caching images for slot (%@). Rendering...", ad.adIdNumber); + + const NSUInteger index = [self.carouselView.nativeAds indexOfObject: ad]; + + if ( index != NSNotFound ) + { + const NSInteger cardIndex = [self cardIndexForAdIndex: index]; + ALCarouselCardView* cardView = self.carouselView.cardViews[cardIndex]; + ALCarouselViewModel* model = self.carouselView.carouselModel; + ALCarouselCardState* cardState = [model cardStateAtNativeAdIndex: index]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [cardView renderViewForNativeAd: ad cardState: cardState]; + + if (cardIndex == kMidCardIndex) { + [cardView trackImpression]; + } + + cardView.hidden = NO; + }]; + } + else + { + NSLog(@"Finished pre-caching images for ad (%@). Card is not on screen, stashing...", ad.adIdNumber); + } +} + +- (void)nativeAdService:(ALNativeAdService *)service didPrecacheVideoForAd:(ALNativeAd *)ad +{ + if ( ad.videoURL ) + { + const NSUInteger index = [self.carouselView.nativeAds indexOfObject: ad]; + ALCarouselCardState *cardState = [self.carouselView.carouselModel cardStateAtNativeAdIndex: index]; + + // If video is loaded for a currently active ad, render the slot + if ( index != NSNotFound && cardState.currentlyActive ) + { + NSLog(@"Finished pre-caching for slot (%@) with valid video in active card", ad.adIdNumber); + + const NSInteger cardIndex = [self cardIndexForAdIndex: index]; + ALCarouselCardView *cardView = self.carouselView.cardViews[cardIndex]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + [cardView.mediaView renderViewForNativeAd: ad cardState: cardState]; + }]; + } + else + { + NSLog(@"Finished pre-caching video for ad (%@). Card is not on screen, stashing...", ad.adIdNumber); + } + } + else + { + // Finished pre-caching for slot without video + NSLog(@"Finished pre-caching for ad (%@) without video", ad.adIdNumber); + } + + [self.carouselView.carouselModel cardStateForNativeAd: ad].precaching = NO; +} + +- (void)nativeAdService:(ALNativeAdService *)service didFailToPrecacheImagesForAd:(ALNativeAd *)ad withError:(NSInteger)errorCode +{ + // Have activity indicator remain on card + [self.carouselView.carouselModel cardStateForNativeAd: ad].precaching = YES; + + NSLog(@"Failed to precache images for ad (%@) with error code: %ld", ad.adIdNumber, errorCode); +} + +- (void)nativeAdService:(ALNativeAdService *)service didFailToPrecacheVideoForAd:(ALNativeAd *)ad withError:(NSInteger)errorCode +{ + // If the slot already has its images pre-cached, it means video failed to pre-cache. Just ignore that + [self.carouselView.carouselModel cardStateForNativeAd: ad].precaching = NO; + + NSLog(@"Failed to precache video for ad (%@) with error code: %ld", ad.adIdNumber, errorCode); +} + +#pragma mark - Utility + +- (NSUInteger)cardIndexForAdIndex:(NSUInteger)slotIndex +{ + return kMidCardIndex + slotIndex - self.carouselView.currentAdIndex; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALNativeAdVideoView.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALNativeAdVideoView.h new file mode 100644 index 0000000000..fd5806828b --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALNativeAdVideoView.h @@ -0,0 +1,23 @@ +// +// ALVideoView.h +// sdk +// +// Created by Matt Szaro on 6/23/14. +// +// + +@import AppLovinSDK; +@import AVFoundation; + +NS_ASSUME_NONNULL_BEGIN + +@interface ALNativeAdVideoView : UIView + +@property (strong, nonatomic, readonly) AVPlayer *player; +@property (strong, nonatomic, readonly) AVPlayerLayer* playerLayer; + +- (instancetype)initWithPlayer:(AVPlayer *)aPlayer; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALNativeAdVideoView.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALNativeAdVideoView.m new file mode 100644 index 0000000000..279a6321fe --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/Carousel UI/Views/ALNativeAdVideoView.m @@ -0,0 +1,48 @@ +// +// ALVideoView.m +// sdk +// +// Created by Matt Szaro on 6/23/14. +// +// + +#import "ALNativeAdVideoView.h" + +@interface ALNativeAdVideoView() +@property (strong, nonatomic, readwrite) AVPlayer *player; +@end + +@implementation ALNativeAdVideoView +@dynamic player, playerLayer; + +-(instancetype) initWithPlayer:(AVPlayer *)aPlayer +{ + self = [super init]; + if(self) + { + self.player = aPlayer; + } + return self; +} + ++(Class) layerClass +{ + return [AVPlayerLayer class]; +} + +-(AVPlayerLayer*) playerLayer +{ + return (AVPlayerLayer*) self.layer; +} + +-(AVPlayer*) player +{ + return self.playerLayer.player; +} + +-(void) setPlayer: (AVPlayer *) aPlayer +{ + self.playerLayer.player = aPlayer; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoArticle.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoArticle.h new file mode 100644 index 0000000000..2b720eab1e --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoArticle.h @@ -0,0 +1,25 @@ +// +// ALDemoArticle.h +// iOS-SDK-Demo +// +// Created by Thomas So on 11/12/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ALDemoArticle : NSObject + +@property (nonatomic, copy) NSString *title; +@property (nonatomic, copy) NSString *pubDate; +@property (nonatomic, copy) NSString *creator; +@property (nonatomic, copy) NSString *articleDescription; +@property (nonatomic, strong) NSURL *link; + +@property (nonatomic, assign) BOOL isAd; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoArticle.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoArticle.m new file mode 100644 index 0000000000..2b24785788 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoArticle.m @@ -0,0 +1,13 @@ +// +// ALDemoArticle.m +// iOS-SDK-Demo +// +// Created by Thomas So on 11/12/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALDemoArticle.h" + +@implementation ALDemoArticle + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoRSSFeedRetriever.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoRSSFeedRetriever.h new file mode 100644 index 0000000000..0b76784375 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoRSSFeedRetriever.h @@ -0,0 +1,23 @@ +// +// ALDemoRSSFeedRetriever.h +// iOS-SDK-Demo +// +// Created by Thomas So on 11/12/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import +#import "ALDemoArticle.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ALDemoRSSFeedRetriever : NSObject + +typedef void(^ALDemoRSSFeedRetrieverBlock)(NSError *__nullable error, NSArray *articles); + ++ (ALDemoRSSFeedRetriever *)sharedRetriever; +- (void)startParsingWithCompletion:(ALDemoRSSFeedRetrieverBlock)completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoRSSFeedRetriever.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoRSSFeedRetriever.m new file mode 100644 index 0000000000..5d039dfefb --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Feed/RSS Feed Parsing/ALDemoRSSFeedRetriever.m @@ -0,0 +1,121 @@ +// +// ALDemoRSSFeedRetriever.m +// iOS-SDK-Demo +// +// Created by Thomas So on 11/12/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALDemoRSSFeedRetriever.h" + +@interface ALDemoRSSFeedRetriever() +@property (nonatomic, copy, nullable) NSString *currentElementName; +@property (nonatomic, strong, nullable) ALDemoArticle *currentArticle; + +@property (nonatomic, strong) NSMutableArray *articles; +@property (nonatomic, copy) ALDemoRSSFeedRetrieverBlock completionBlock; +@end + +@implementation ALDemoRSSFeedRetriever +static NSString *const kRSSFeedURL = @"https://blog.applovin.com/feed/"; + ++ (ALDemoRSSFeedRetriever *)sharedRetriever +{ + static dispatch_once_t pred; + static ALDemoRSSFeedRetriever *manager = nil; + dispatch_once(&pred, ^{ + manager = [[self alloc] init]; + }); + return manager; +} + +- (instancetype)init +{ + self = [super init]; + if ( self ) + { + self.articles = [NSMutableArray array]; + } + return self; +} + +- (void)startParsingWithCompletion:(ALDemoRSSFeedRetrieverBlock)completion +{ + // Do not retrieve if we already have retrieved some artivcles, just call completion + if ( self.articles.count > 0 ) + { + completion( nil, [NSArray arrayWithArray: self.articles] ); + } + else + { + self.completionBlock = completion; + + NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL: [NSURL URLWithString: kRSSFeedURL]]; + [parser setDelegate: self]; + [parser parse]; + } +} + +#pragma mark - Parser Delegate + +- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict +{ + self.currentElementName = elementName; + if ( [elementName isEqualToString: @"item"] ) + { + // Start of the parsing of a new article + + if ( self.articles.count == 3 ) + { + // Lets place an ad in the 4th slot + ALDemoArticle *adFauxArticle = [[ALDemoArticle alloc] init]; + adFauxArticle.isAd = YES; + [self.articles addObject: adFauxArticle]; + } + + self.currentArticle = [[ALDemoArticle alloc] init]; + [self.articles addObject: self.currentArticle]; + } +} + +- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string +{ + if ( self.currentArticle ) + { + if ( [self.currentElementName isEqualToString: @"title"] ) + { + self.currentArticle.title = string; + } + else if ( [self.currentElementName isEqualToString: @"link"] ) + { + self.currentArticle.link = [NSURL URLWithString: string]; + } + else if ( [self.currentElementName isEqualToString: @"pubDate"] ) + { + self.currentArticle.pubDate = [string substringToIndex: 11]; + } + else if ( [self.currentElementName isEqualToString: @"dc:creator"] ) + { + self.currentArticle.creator = string; + } + else if ( [self.currentElementName isEqualToString: @"description"] ) + { + self.currentArticle.articleDescription = [string stringByReplacingOccurrencesOfString: @"[…]" withString: @"..."]; + } + + // Reset current element name after we finish setting it + self.currentElementName = @""; + } +} + +- (void)parserDidEndDocument:(NSXMLParser *)parser +{ + self.completionBlock( nil, [NSArray arrayWithArray: self.articles] ); +} + +- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError +{ + self.completionBlock( parseError, @[] ); +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Programmatic/ALDemoNativeAdProgrammaticViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Programmatic/ALDemoNativeAdProgrammaticViewController.h new file mode 100644 index 0000000000..d49d97af19 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Programmatic/ALDemoNativeAdProgrammaticViewController.h @@ -0,0 +1,26 @@ +// +// ALDemoNativeAdProgrammaticViewController.h +// iOS-SDK-Demo +// +// Created by Thomas So on 9/24/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" +#import "ALCarouselMediaView.h" + +@interface ALDemoNativeAdProgrammaticViewController : ALBaseAdViewController + +@property (nonatomic, strong) IBOutlet UIBarButtonItem *precacheButton; +@property (nonatomic, strong) IBOutlet UIBarButtonItem *showButton; + +@property (nonatomic, strong) IBOutlet UILabel *impressionStatusLabel; + +@property (nonatomic, strong) IBOutlet UIImageView *appIcon; +@property (nonatomic, strong) IBOutlet UILabel *titleLabel; +@property (nonatomic, strong) IBOutlet UIImageView *rating; +@property (nonatomic, strong) IBOutlet UILabel *descriptionLabel; +@property (nonatomic, strong) IBOutlet ALCarouselMediaView *mediaView; +@property (nonatomic, strong) IBOutlet UIButton *ctaButton; + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Programmatic/ALDemoNativeAdProgrammaticViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Programmatic/ALDemoNativeAdProgrammaticViewController.m new file mode 100644 index 0000000000..d1b9ede32e --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Native Ads/Programmatic/ALDemoNativeAdProgrammaticViewController.m @@ -0,0 +1,174 @@ +// +// ALDemoNativeAdProgrammaticViewController.m +// iOS-SDK-Demo +// +// Created by Thomas So on 9/24/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALDemoNativeAdProgrammaticViewController.h" +#import + +@interface ALDemoNativeAdProgrammaticViewController() +@property (nonatomic, strong) ALNativeAd *cachedNativeAd; +@property (nonatomic, strong) ALNativeAd *nativeAd; +@end + +// Additional documentation - https://applovin.com/integration#iosNative + +@implementation ALDemoNativeAdProgrammaticViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.appIcon.layer.masksToBounds = YES; + self.appIcon.layer.cornerRadius = 3.0f; + + self.ctaButton.layer.masksToBounds = YES; + self.ctaButton.layer.cornerRadius = 3.0f; + + [self setUIElementsHidden: YES]; +} + +#pragma mark - Action Methods + +- (IBAction)loadNativeAd:(id)sender +{ + self.precacheButton.enabled = NO; + self.showButton.enabled = NO; + + self.impressionStatusLabel.text = @"No impression to track"; + + [[ALSdk shared].nativeAdService loadNextAdAndNotify: self]; +} + +- (IBAction)precacheNativeAd:(id)sender +{ + // You can use our pre-caching to retrieve assets (app icon, ad image, ad video) locally. OR you can do it with your preferred caching framework. + // iconURL, imageURL, videoURL needs to be retrieved manually before you can render them + + [[ALSdk shared].nativeAdService precacheResourcesForNativeAd: self.nativeAd andNotify: self]; +} + +- (IBAction)showNativeAd:(id)sender +{ + self.nativeAd = self.cachedNativeAd; + + self.appIcon.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: self.nativeAd.iconURL]]; // Local URL + self.titleLabel.text = self.nativeAd.title; + self.descriptionLabel.text = self.nativeAd.descriptionText; + [self.ctaButton setTitle: self.nativeAd.ctaText forState: UIControlStateNormal]; + + NSString *filename = [NSString stringWithFormat: @"Star_Sprite_%@", self.nativeAd.starRating.stringValue]; + self.rating.image = [UIImage imageNamed: filename]; + + // NOTE - Videos have aspect ratio of 1:1.85 + [self.mediaView renderViewForNativeAd: self.nativeAd]; + + [self setUIElementsHidden: NO]; + + // + // You are responsible for firing impressions + // + [self trackImpression: self.nativeAd]; + + [self.view layoutIfNeeded]; +} + +- (IBAction)ctaPressed:(id)sender +{ + [self.nativeAd launchClickTarget]; +} + +#pragma mark - Impressing Tracking + +- (void)trackImpression:(ALNativeAd *)ad +{ + // Callbacks may not happen on main queue + dispatch_async(dispatch_get_main_queue(), ^{ + self.impressionStatusLabel.text = @"Tracking impression..."; + }); + + [ad trackImpressionAndNotify: self]; +} + +- (void)postbackService:(ALPostbackService *)postbackService didExecutePostback:(NSURL *)postbackURL +{ + // Callbacks may not happen on main queue + dispatch_async(dispatch_get_main_queue(), ^{ + // Impression tracked! + self.impressionStatusLabel.text = @"Impression tracked"; + }); +} + +- (void)postbackService:(ALPostbackService *)postbackService didFailToExecutePostback:(nullable NSURL *)postbackURL errorCode:(NSInteger)errorCode +{ + // Callbacks may not happen on main queue + dispatch_async(dispatch_get_main_queue(), ^{ + // Impression could not be tracked. Retry the postback later. + self.impressionStatusLabel.text = [NSString stringWithFormat: @"Impression failed to track with error code %ld", errorCode]; + }); +} + +#pragma mark - Native Ad Load Delegate + +- (void)nativeAdService:(ALNativeAdService *)service didLoadAds:(NSArray *)ads +{ + [self logCallback: __PRETTY_FUNCTION__]; + + // Callbacks may not happen on main queue + dispatch_async(dispatch_get_main_queue(), ^{ + self.nativeAd = [ads firstObject]; + self.precacheButton.enabled = YES; + }); +} + +- (void)nativeAdService:(ALNativeAdService *)service didFailToLoadAdsWithError:(NSInteger)code +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Native Ad Precache Delegate + +- (void)nativeAdService:(ALNativeAdService *)service didPrecacheImagesForAd:(ALNativeAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)nativeAdService:(ALNativeAdService *)service didPrecacheVideoForAd:(ALNativeAd *)ad +{ + // This delegate method will get called whether an ad actually has a video to precache or not + [self logCallback: __PRETTY_FUNCTION__]; + + // Callbacks may not happen on main queue + dispatch_async(dispatch_get_main_queue(), ^{ + self.cachedNativeAd = ad; + self.showButton.enabled = YES; + self.precacheButton.enabled = NO; + }); +} + +- (void)nativeAdService:(ALNativeAdService *)service didFailToPrecacheImagesForAd:(ALNativeAd *)ad withError:(NSInteger)errorCode +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)nativeAdService:(ALNativeAdService *)service didFailToPrecacheVideoForAd:(ALNativeAd *)ad withError:(NSInteger)errorCode +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Utility + +- (void)setUIElementsHidden:(BOOL)hidden +{ + self.appIcon.hidden = hidden; + self.titleLabel.hidden = hidden; + self.rating.hidden = hidden; + self.descriptionLabel.hidden = hidden; + self.mediaView.hidden = hidden; + self.ctaButton.hidden = hidden; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Basic Integration/ALDemoRewardedVideosViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Basic Integration/ALDemoRewardedVideosViewController.h new file mode 100644 index 0000000000..978b58baf7 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Basic Integration/ALDemoRewardedVideosViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoRewardedVideosViewController.h +// iOS-SDK-Demo +// +// Created by Thomas So on 9/23/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoRewardedVideosViewController : ALBaseAdViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Basic Integration/ALDemoRewardedVideosViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Basic Integration/ALDemoRewardedVideosViewController.m new file mode 100644 index 0000000000..3a0672de08 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Basic Integration/ALDemoRewardedVideosViewController.m @@ -0,0 +1,148 @@ +// +// ALDemoRewardedVideosViewController.m +// iOS-SDK-Demo +// +// Created by Thomas So on 9/23/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALDemoRewardedVideosViewController.h" +#import + +@interface ALDemoRewardedVideosViewController() +@property (nonatomic, strong) ALIncentivizedInterstitialAd *rewardedAd; +@end + +@implementation ALDemoRewardedVideosViewController + +#pragma mark - IB Action Methods + +- (IBAction)showRewardedVideo:(id)sender +{ + // You need to preload each rewarded video before it can be displayed + if ( [[ALIncentivizedInterstitialAd shared] isReadyForDisplay] ) + { + // Optional: Assign delegates + self.rewardedAd = [ALIncentivizedInterstitialAd shared]; + self.rewardedAd.adDisplayDelegate = self; + self.rewardedAd.adVideoPlaybackDelegate = self; + + [self.rewardedAd showAndNotify: self]; + } + else + { + [self preloadRewardedVideo: nil]; + } +} + +// You need to preload each rewarded video before it can be displayed +- (IBAction)preloadRewardedVideo:(id)sender +{ + [self logCallback: __PRETTY_FUNCTION__]; + + [self.rewardedAd preloadAndNotify: self]; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Reward Delegate + +- (void)rewardValidationRequestForAd:(ALAd *)ad didSucceedWithResponse:(NSDictionary *)response +{ + /* AppLovin servers validated the reward. Refresh user balance from your server. We will also pass the number of coins + awarded and the name of the currency. However, ideally, you should verify this with your server before granting it. */ + + // i.e. - "Coins", "Gold", whatever you set in the dashboard. + NSString *currencyName = response[@"currency"]; + + // For example, "5" or "5.00" if you've specified an amount in the UI. + NSString *amountGivenString = response[@"amount"]; + NSNumber *amountGiven = @([amountGivenString floatValue]); + + // Do something with this information. + // [MYCurrencyManagerClass updateUserCurrency: currencyName withChange: amountGiven]; + [self logCallback: __PRETTY_FUNCTION__]; + + // By default we'll show a UIAlertView informing your user of the currency & amount earned. + // If you don't want this, you can turn it off in the Manage Apps UI. +} + +- (void)rewardValidationRequestForAd:(ALAd *)ad didFailWithError:(NSInteger)responseCode +{ + if (responseCode == kALErrorCodeIncentivizedUserClosedVideo) + { + // Your user exited the video prematurely. It's up to you if you'd still like to grant + // a reward in this case. Most developers choose not to. Note that this case can occur + // after a reward was initially granted (since reward validation happens as soon as a + // video is launched). + } + else if (responseCode == kALErrorCodeIncentivizedValidationNetworkTimeout || responseCode == kALErrorCodeIncentivizedUnknownServerError) + { + // Some server issue happened here. Don't grant a reward. By default we'll show the user + // a UIAlertView telling them to try again later, but you can change this in the + // Manage Apps UI. + } + else if (responseCode == kALErrorCodeIncentiviziedAdNotPreloaded) + { + // Indicates that the developer called for a rewarded video before one was available. + } + + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)rewardValidationRequestForAd:(ALAd *)ad didExceedQuotaWithResponse:(NSDictionary *)response +{ + // Your user has already earned the max amount you allowed for the day at this point, so + // don't give them any more money. By default we'll show them a UIAlertView explaining this, + // though you can change that from the Manage Apps UI. + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)rewardValidationRequestForAd:(ALAd *)ad wasRejectedWithResponse:(NSDictionary *)response +{ + // Your user couldn't be granted a reward for this view. This could happen if you've blacklisted + // them, for example. Don't grant them any currency. By default we'll show them a UIAlertView explaining this, + // though you can change that from the Manage Apps UI. + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Video Playback Delegate + +- (void)videoPlaybackBeganInAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)videoPlaybackEndedInAd:(ALAd *)ad atPlaybackPercent:(NSNumber *)percentPlayed fullyWatched:(BOOL)wasFullyWatched +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Zone Integration/ALDemoRewardedVideosZoneViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Zone Integration/ALDemoRewardedVideosZoneViewController.h new file mode 100644 index 0000000000..6df81b4a69 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Zone Integration/ALDemoRewardedVideosZoneViewController.h @@ -0,0 +1,13 @@ +// +// ALDemoRewardedVideosZoneViewController.h +// iOS-SDK-Demo-ObjC +// +// Created by Suyash Saxena on 6/19/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +#import "ALBaseAdViewController.h" + +@interface ALDemoRewardedVideosZoneViewController : ALBaseAdViewController + +@end diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Zone Integration/ALDemoRewardedVideosZoneViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Zone Integration/ALDemoRewardedVideosZoneViewController.m new file mode 100644 index 0000000000..276c259298 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/AppLovin/Rewarded/Zone Integration/ALDemoRewardedVideosZoneViewController.m @@ -0,0 +1,156 @@ +// +// ALDemoRewardedVideosZoneViewController.m +// iOS-SDK-Demo-ObjC +// +// Created by Suyash Saxena on 6/19/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +#import "ALDemoRewardedVideosZoneViewController.h" +#import + +@interface ALDemoRewardedVideosZoneViewController() +@property (nonatomic, strong) ALIncentivizedInterstitialAd *incentivizedInterstitial; +@end + +@implementation ALDemoRewardedVideosZoneViewController + +#pragma mark - View Lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.incentivizedInterstitial = [[ALIncentivizedInterstitialAd alloc] initWithZoneIdentifier: @"YOUR_ZONE_ID"]; +} + +#pragma mark - IB Action Methods + +- (IBAction)showRewardedVideo:(id)sender +{ + // You need to preload each rewarded video before it can be displayed + if ( self.incentivizedInterstitial.isReadyForDisplay ) + { + // Optional: Assign delegates + self.incentivizedInterstitial.adDisplayDelegate = self; + self.incentivizedInterstitial.adVideoPlaybackDelegate = self; + + [self.incentivizedInterstitial showAndNotify: self]; + } + else + { + [self preloadRewardedVideo: nil]; + } +} + +// You need to preload each rewarded video before it can be displayed +- (IBAction)preloadRewardedVideo:(id)sender +{ + [self logCallback: __PRETTY_FUNCTION__]; + + [self.incentivizedInterstitial preloadAndNotify: self]; +} + +#pragma mark - Ad Load Delegate + +- (void)adService:(ALAdService *)adService didLoadAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)adService:(ALAdService *)adService didFailToLoadAdWithError:(int)code +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Reward Delegate + +- (void)rewardValidationRequestForAd:(ALAd *)ad didSucceedWithResponse:(NSDictionary *)response +{ + /* AppLovin servers validated the reward. Refresh user balance from your server. We will also pass the number of coins + awarded and the name of the currency. However, ideally, you should verify this with your server before granting it. */ + + // i.e. - "Coins", "Gold", whatever you set in the dashboard. + NSString *currencyName = response[@"currency"]; + + // For example, "5" or "5.00" if you've specified an amount in the UI. + NSString *amountGivenString = response[@"amount"]; + NSNumber *amountGiven = @([amountGivenString floatValue]); + + // Do something with this information. + // [MYCurrencyManagerClass updateUserCurrency: currencyName withChange: amountGiven]; + [self logCallback: __PRETTY_FUNCTION__]; + + // By default we'll show a UIAlertView informing your user of the currency & amount earned. + // If you don't want this, you can turn it off in the Manage Apps UI. +} + +- (void)rewardValidationRequestForAd:(ALAd *)ad didFailWithError:(NSInteger)responseCode +{ + if (responseCode == kALErrorCodeIncentivizedUserClosedVideo) + { + // Your user exited the video prematurely. It's up to you if you'd still like to grant + // a reward in this case. Most developers choose not to. Note that this case can occur + // after a reward was initially granted (since reward validation happens as soon as a + // video is launched). + } + else if (responseCode == kALErrorCodeIncentivizedValidationNetworkTimeout || responseCode == kALErrorCodeIncentivizedUnknownServerError) + { + // Some server issue happened here. Don't grant a reward. By default we'll show the user + // a UIAlertView telling them to try again later, but you can change this in the + // Manage Apps UI. + } + else if (responseCode == kALErrorCodeIncentiviziedAdNotPreloaded) + { + // Indicates that the developer called for a rewarded video before one was available. + } + + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)rewardValidationRequestForAd:(ALAd *)ad didExceedQuotaWithResponse:(NSDictionary *)response +{ + // Your user has already earned the max amount you allowed for the day at this point, so + // don't give them any more money. By default we'll show them a UIAlertView explaining this, + // though you can change that from the Manage Apps UI. + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)rewardValidationRequestForAd:(ALAd *)ad wasRejectedWithResponse:(NSDictionary *)response +{ + // Your user couldn't be granted a reward for this view. This could happen if you've blacklisted + // them, for example. Don't grant them any currency. By default we'll show them a UIAlertView explaining this, + // though you can change that from the Manage Apps UI. + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Display Delegate + +- (void)ad:(ALAd *)ad wasDisplayedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasHiddenIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)ad:(ALAd *)ad wasClickedIn:(UIView *)view +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +#pragma mark - Ad Video Playback Delegate + +- (void)videoPlaybackBeganInAd:(ALAd *)ad +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +- (void)videoPlaybackEndedInAd:(ALAd *)ad atPlaybackPercent:(NSNumber *)percentPlayed fullyWatched:(BOOL)wasFullyWatched +{ + [self logCallback: __PRETTY_FUNCTION__]; +} + +@end diff --git a/DemoApp-ObjC/DemoApp-ObjC/ALBaseAdViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes/ALBaseAdViewController.h similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/ALBaseAdViewController.h rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes/ALBaseAdViewController.h diff --git a/DemoApp-ObjC/DemoApp-ObjC/ALBaseAdViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes/ALBaseAdViewController.m similarity index 81% rename from DemoApp-ObjC/DemoApp-ObjC/ALBaseAdViewController.m rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes/ALBaseAdViewController.m index f82f331940..193ae0eab8 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/ALBaseAdViewController.m +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes/ALBaseAdViewController.m @@ -22,6 +22,18 @@ - (void)viewDidLoad self.callbacks = [NSMutableArray array]; } +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear: animated]; + [self.navigationController setToolbarHidden: self.hidesBottomBarWhenPushed animated: YES]; +} + +- (void)viewWillDisappear:(BOOL)animated +{ + [self.navigationController setToolbarHidden: YES]; + [super viewWillDisappear: animated]; +} + - (void)logCallback:(const char *)name { [self.callbacks addObject: [NSString stringWithCString: name encoding: NSUTF8StringEncoding]]; diff --git a/DemoApp-ObjC/DemoApp-ObjC/ALHomeViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes/ALHomeViewController.h similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/ALHomeViewController.h rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes/ALHomeViewController.h diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes/ALHomeViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes/ALHomeViewController.m new file mode 100644 index 0000000000..1c3a7b8b90 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Base Classes/ALHomeViewController.m @@ -0,0 +1,173 @@ +// +// ALHomeViewController.m +// DemoApp-ObjC +// +// Created by Thomas So on 9/4/19. +// Copyright © 2019 AppLovin Corporation. All rights reserved. +// + +#import "ALHomeViewController.h" +#import +#import +#import + +@interface ALHomeViewController() +@property (nonatomic, weak) IBOutlet UITableViewCell *mediationDebuggerCell; +@property (nonatomic, strong) IBOutlet UIBarButtonItem *muteToggle; +@end + +@implementation ALHomeViewController +static NSString *const kSupportEmail = @"support@applovin.com"; +static NSString *const kSupportLink = @"https://support.applovin.com/support/home"; +static const NSInteger kRowIndexToHideForPhone = 3; + +- (void)viewDidLoad +{ + [super viewDidLoad]; + [self addFooterLabel]; + + self.muteToggle.image = [self muteIconForCurrentSdkMuteSetting]; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + [tableView deselectRowAtIndexPath: indexPath animated: YES]; + + if ( [tableView cellForRowAtIndexPath: indexPath] == self.mediationDebuggerCell ) + { + [[ALSdk shared] showMediationDebugger]; + } + else if ( indexPath.section == 2 ) + { + if ( indexPath.row == 0 ) + { + [self openSupportSite]; + } + else if ( indexPath.row == 1 ) + { + [self attemptSendEmail]; + } + } +} + +- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath +{ + if ( UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone && indexPath.section == 0 && indexPath.row == kRowIndexToHideForPhone ) + { + cell.hidden = YES; + } +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + if ( UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone && indexPath.section == 0 && indexPath.row == kRowIndexToHideForPhone ) + { + return 0; + } + return [super tableView: tableView heightForRowAtIndexPath: indexPath]; +} + +#pragma mark - Sound Toggling + +- (IBAction)toggleMute:(UIBarButtonItem *)sender +{ + /** + * Toggling the sdk mute setting will affect whether your video ads begin in a muted state or not. + */ + ALSdk *sdk = [ALSdk shared]; + sdk.settings.muted = !sdk.settings.muted; + sender.image = [self muteIconForCurrentSdkMuteSetting]; +} + +- (UIImage *)muteIconForCurrentSdkMuteSetting +{ + return [ALSdk shared].settings.muted ? [UIImage imageNamed: @"mute"] : [UIImage imageNamed: @"unmute"]; +} + +#pragma mark - Table View Actions + +- (void)openSupportSite +{ + NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; + if ( version.majorVersion > 8 ) + { + SFSafariViewController *safariController = [[SFSafariViewController alloc] initWithURL: [NSURL URLWithString: kSupportLink] + entersReaderIfAvailable: YES]; + [self presentViewController: safariController animated: YES completion:^{ + [[UIApplication sharedApplication] setStatusBarStyle: UIStatusBarStyleDefault]; + }]; + } + else + { + [[UIApplication sharedApplication] openURL: [NSURL URLWithString: kSupportLink]]; + } +} + +- (void)attemptSendEmail +{ + if ( [MFMailComposeViewController canSendMail] ) + { + MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init]; + mailController.mailComposeDelegate = self; + [mailController setSubject: @"iOS SDK support"]; + [mailController setToRecipients: @[kSupportEmail]]; + [mailController setMessageBody: [NSString stringWithFormat: @"\n\n---\nSDK Version: %@", [ALSdk version]] isHTML: NO]; + mailController.navigationBar.tintColor = [UIColor whiteColor]; + [self presentViewController: mailController animated: YES completion:^{ + [[UIApplication sharedApplication] setStatusBarStyle: UIStatusBarStyleLightContent]; + }]; + } + else + { + NSString *message = [NSString stringWithFormat: @"Your device is not configured for sending emails.\n\nPlease send emails to %@", kSupportEmail]; + [[[UIAlertView alloc] initWithTitle: @"Email Unavailable" + message: message + delegate: nil + cancelButtonTitle: @"OK" + otherButtonTitles: nil] show]; + } +} + +- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error +{ + switch ( result ) + { + case MFMailComposeResultSent: + [[[UIAlertView alloc] initWithTitle: @"Email Sent" + message: @"Thank you for your email, we will process it as soon as possible." + delegate: nil + cancelButtonTitle: @"OK" + otherButtonTitles: nil] show]; + case MFMailComposeResultCancelled: + case MFMailComposeResultSaved: + case MFMailComposeResultFailed: + default: + break; + } + + [self dismissViewControllerAnimated: YES completion: nil]; +} + +- (void)addFooterLabel +{ + UILabel *footer = [[UILabel alloc] init]; + footer.font = [UIFont systemFontOfSize: 14.0f]; + footer.numberOfLines = 0; + + NSString *appVersion = [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"]; + NSString *sdkVersion = [ALSdk version]; + NSString *systemVersion = [[UIDevice currentDevice] systemVersion]; + NSString *text = [NSString stringWithFormat: @"App Version: %@\nSDK Version: %@\niOS Version: %@\n\nLanguage: Objective-C", appVersion, sdkVersion, systemVersion]; + + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + style.alignment = NSTextAlignmentCenter; + style.minimumLineHeight = 20.0f; + footer.attributedText = [[NSAttributedString alloc] initWithString: text attributes: @{NSParagraphStyleAttributeName : style}]; + + CGRect frame = footer.frame; + frame.size.height = [footer sizeThatFits: CGSizeMake(CGRectGetWidth(footer.frame), CGFLOAT_MAX)].height + 60.0f; + footer.frame = frame; + self.tableView.tableFooterView = footer; +} + +@end diff --git a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALAutoLayoutBannerAdViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.h similarity index 77% rename from DemoApp-ObjC/DemoApp-ObjC/Ads/ALAutoLayoutBannerAdViewController.h rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.h index 2cda5c2e34..6374a694ce 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALAutoLayoutBannerAdViewController.h +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface ALAutoLayoutBannerAdViewController : ALBaseAdViewController +@interface ALMAXAutoLayoutBannerAdViewController : ALBaseAdViewController @end diff --git a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALAutoLayoutBannerAdViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.m similarity index 95% rename from DemoApp-ObjC/DemoApp-ObjC/Ads/ALAutoLayoutBannerAdViewController.m rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.m index dafff9f0ef..73ece8faed 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALAutoLayoutBannerAdViewController.m +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.m @@ -6,14 +6,14 @@ // Copyright © 2019 AppLovin Corporation. All rights reserved. // -#import "ALAutoLayoutBannerAdViewController.h" +#import "ALMAXAutoLayoutBannerAdViewController.h" #import -@interface ALAutoLayoutBannerAdViewController() +@interface ALMAXAutoLayoutBannerAdViewController() @property (nonatomic, strong) MAAdView *adView; @end -@implementation ALAutoLayoutBannerAdViewController +@implementation ALMAXAutoLayoutBannerAdViewController #pragma mark - View Lifecycle diff --git a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALFrameLayoutBannerAdViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.h similarity index 77% rename from DemoApp-ObjC/DemoApp-ObjC/Ads/ALFrameLayoutBannerAdViewController.h rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.h index fe728222b8..306166b670 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALFrameLayoutBannerAdViewController.h +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface ALFrameLayoutBannerAdViewController : ALBaseAdViewController +@interface ALMAXFrameLayoutBannerAdViewController : ALBaseAdViewController @end diff --git a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALFrameLayoutBannerAdViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.m similarity index 91% rename from DemoApp-ObjC/DemoApp-ObjC/Ads/ALFrameLayoutBannerAdViewController.m rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.m index fad5342a53..dc016585c4 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALFrameLayoutBannerAdViewController.m +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.m @@ -6,14 +6,14 @@ // Copyright © 2019 AppLovin Corporation. All rights reserved. // -#import "ALFrameLayoutBannerAdViewController.h" +#import "ALMAXFrameLayoutBannerAdViewController.h" #import -@interface ALFrameLayoutBannerAdViewController() +@interface ALMAXFrameLayoutBannerAdViewController() @property (nonatomic, strong) MAAdView *adView; @end -@implementation ALFrameLayoutBannerAdViewController +@implementation ALMAXFrameLayoutBannerAdViewController #pragma mark - View Lifecycle diff --git a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterfaceBuilderBannerAdViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.h similarity index 77% rename from DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterfaceBuilderBannerAdViewController.h rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.h index fc3b49b482..0acbafdbd1 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterfaceBuilderBannerAdViewController.h +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface ALInterfaceBuilderBannerAdViewController : ALBaseAdViewController +@interface ALMAXInterfaceBuilderBannerAdViewController : ALBaseAdViewController @end diff --git a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterfaceBuilderBannerAdViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.m similarity index 88% rename from DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterfaceBuilderBannerAdViewController.m rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.m index 7bc66f9a73..9c894bd8ab 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterfaceBuilderBannerAdViewController.m +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.m @@ -6,14 +6,14 @@ // Copyright © 2019 AppLovin Corporation. All rights reserved. // -#import "ALInterfaceBuilderBannerAdViewController.h" +#import "ALMAXInterfaceBuilderBannerAdViewController.h" #import -@interface ALInterfaceBuilderBannerAdViewController() +@interface ALMAXInterfaceBuilderBannerAdViewController() @property (nonatomic, strong) IBOutlet MAAdView *adView; @end -@implementation ALInterfaceBuilderBannerAdViewController +@implementation ALMAXInterfaceBuilderBannerAdViewController #pragma mark - View Lifecycle diff --git a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterstitialAdViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Interstitials/ALMAXInterstitialAdViewController.h similarity index 78% rename from DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterstitialAdViewController.h rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Interstitials/ALMAXInterstitialAdViewController.h index 67ae188e7d..447beac5d5 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterstitialAdViewController.h +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Interstitials/ALMAXInterstitialAdViewController.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface ALInterstitialAdViewController : ALBaseAdViewController +@interface ALMAXInterstitialAdViewController : ALBaseAdViewController @end diff --git a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterstitialAdViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Interstitials/ALMAXInterstitialAdViewController.m similarity index 75% rename from DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterstitialAdViewController.m rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Interstitials/ALMAXInterstitialAdViewController.m index c1470e39a0..c14c852339 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALInterstitialAdViewController.m +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Interstitials/ALMAXInterstitialAdViewController.m @@ -6,15 +6,16 @@ // Copyright © 2019 AppLovin Corporation. All rights reserved. // -#import "ALInterstitialAdViewController.h" +#import "ALMAXInterstitialAdViewController.h" #import "ALBaseAdViewController.h" #import -@interface ALInterstitialAdViewController() +@interface ALMAXInterstitialAdViewController() @property (nonatomic, strong) MAInterstitialAd *interstitialAd; +@property (nonatomic, assign) NSInteger retryAttempt; @end -@implementation ALInterstitialAdViewController +@implementation ALMAXInterstitialAdViewController #pragma mark - View Lifecycle @@ -45,14 +46,21 @@ - (void)didLoadAd:(MAAd *)ad { // Interstitial ad is ready to be shown. '[self.interstitialAd isReady]' will now return 'YES' [self logCallback: __PRETTY_FUNCTION__]; + + // Reset retry attempt + self.retryAttempt = 0; } - (void)didFailToLoadAdForAdUnitIdentifier:(NSString *)adUnitIdentifier withErrorCode:(NSInteger)errorCode { [self logCallback: __PRETTY_FUNCTION__]; - // Interstitial ad failed to load. We recommend re-trying in 3 seconds. - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + // Interstitial ad failed to load. We recommend retrying with exponentially higher delays. + + self.retryAttempt++; + NSInteger delaySec = pow(2, self.retryAttempt); + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delaySec * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ [self.interstitialAd loadAd]; }); } diff --git a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALRewardedAdViewController.h b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Rewarded/ALMAXRewardedAdViewController.h similarity index 79% rename from DemoApp-ObjC/DemoApp-ObjC/Ads/ALRewardedAdViewController.h rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Rewarded/ALMAXRewardedAdViewController.h index 4bb788790d..5668ba63aa 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALRewardedAdViewController.h +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Rewarded/ALMAXRewardedAdViewController.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface ALRewardedAdViewController : ALBaseAdViewController +@interface ALMAXRewardedAdViewController : ALBaseAdViewController @end diff --git a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALRewardedAdViewController.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Rewarded/ALMAXRewardedAdViewController.m similarity index 78% rename from DemoApp-ObjC/DemoApp-ObjC/Ads/ALRewardedAdViewController.m rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Rewarded/ALMAXRewardedAdViewController.m index cdfb68564c..73f2daf7b8 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/Ads/ALRewardedAdViewController.m +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/MAX/Rewarded/ALMAXRewardedAdViewController.m @@ -6,14 +6,15 @@ // Copyright © 2019 AppLovin Corporation. All rights reserved. // -#import "ALRewardedAdViewController.h" +#import "ALMAXRewardedAdViewController.h" #import -@interface ALRewardedAdViewController() +@interface ALMAXRewardedAdViewController() @property (nonatomic, strong) MARewardedAd *rewardedAd; +@property (nonatomic, assign) NSInteger retryAttempt; @end -@implementation ALRewardedAdViewController +@implementation ALMAXRewardedAdViewController #pragma mark - View Lifecycle @@ -44,14 +45,21 @@ - (void)didLoadAd:(MAAd *)ad { // Rewarded ad is ready to be shown. '[self.rewardedAd isReady]' will now return 'YES' [self logCallback: __PRETTY_FUNCTION__]; + + // Reset retry attempt + self.retryAttempt = 0; } - (void)didFailToLoadAdForAdUnitIdentifier:(NSString *)adUnitIdentifier withErrorCode:(NSInteger)errorCode { [self logCallback: __PRETTY_FUNCTION__]; - // Rewarded ad failed to load. We recommend re-trying in 3 seconds. - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + // Rewarded ad failed to load. We recommend retrying with exponentially higher delays. + + self.retryAttempt++; + NSInteger delaySec = pow(2, self.retryAttempt); + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delaySec * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ [self.rewardedAd loadAd]; }); } diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/Contents.json b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/Contents.json similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/Contents.json rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/Contents.json diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/bug.imageset/Contents.json b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/bug.imageset/Contents.json similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/bug.imageset/Contents.json rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/bug.imageset/Contents.json diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/bug.imageset/bug.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/bug.imageset/bug.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/bug.imageset/bug.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/bug.imageset/bug.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/logo.imageset/Contents.json b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/logo.imageset/Contents.json similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/logo.imageset/Contents.json rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/logo.imageset/Contents.json diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@2x.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@2x.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@2x.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@2x.png diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@3x.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@3x.png similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@3x.png rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@3x.png diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/mute.imageset/Contents.json b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/mute.imageset/Contents.json new file mode 100644 index 0000000000..502704ce90 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/mute.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "mute.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/mute.imageset/mute.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/mute.imageset/mute.png new file mode 100644 index 0000000000000000000000000000000000000000..0d76cad7ee7c90abc75b11951bb9f6055bd89f1f GIT binary patch literal 3677 zcma)92{=@1A0LElS%yR!V}_f_9WxlqOiXt)G?KkUu4RlFrkcgfvE}PphKo=bNwQ{W zQKTys5nZ&&GF&MX+GdnUw2=Bn+wDH}E$4a8dC&Wv-|zR|-uM4JC(Ye;lZv9QA_xRh zaUu~tCF9SM8?{_c^2}E=u8|BWfTnh5d!S8 zgZa3)LpQQGRH&sX#uN?1D?*`ATTTcK=Sg(@Oqa~;U||5j#-UJAQBkH*7N#stD9Q|r z#iG#WD06e91cBtnFadHjlF5aC68WM-q;e@7Ivb#~n9wC%axg0bu!F&t41K*m`^jLt zxO_FmPJj2;7R@4$saPgTVvQ%lqZ$Tir`SF5;*)vLI9oi zA9R+!e01SQG)cbyyWd}PA_>`6f_8Di5vd${BsIj5!(x0)44cg1QkR^xgMIPxTha+SEQ|eLvfog@lXc_JL+MQNZsOmjmx}TKr|Xk3di=r3Wgq)CN45eZwvWY6Wtr2e%{ z^Xe0qojdNB@2t!#@Vx_*oeWD>x70-Xs3W%u7>Qbr0$cC<41f8X)rNX$y!vY`-|bNXbkBVX|-%)N;f8MM;AIBhWR_TJU^)s zf_UowW_4qxM%l1EHokd?pbEV>*~%K-I1R(?LiiQA<)lbEy_W&%AcbAaw7mvMd3&n- zes_H~P@L~C6WI?zRBZCLPK=Xzl08cgTK5XRM1iR$o^NGB>p!SYl!tQ+s8y~#7Wj%vFGJ|wgYS|?b~mu(N~CIRuIoY> zbK#n+RM#cu4nAl(_|PdXgIQcR6k&i16&a%L3X7FG9ax|tt?}`smmryQ%EP~pb6fs= za>Yg6nRUQJROE0XH}ae&GlN$Lz9)m1BJ9}o_>Pecwmt2{F_UXKuWa|FoQ6YAo)oMB zAfvbVGyHikpc~|P8^71N2bfh}lXV-VPD5k+vR&!-R91piQ(X-gwO8?6?QpL=kd=q_ z)i=(lxf`DT4SXH&sw;%w?Y~b9U)j?YUaPYzCd$h5d`}XZ7bDbNC3Z{5&Wl%i5uQDp zrb92t?5(sbnV9YO@anTPah81`HsZ$@)g$R%$V-Gd%TYpI;2N(NP`0$YA_9lI#*->& zueQl96ln8srC@DXH8E5ZhqJ!x6n5G9k%#*P7c>$uN_u+NT~#if#={@lfPnGkozWcX1CekieC`-?-_=qi~O!e2N;e$V5^9_qKoV1 zW<8HmTo)z^`b=+N^rTnFSo*sU4{cT&C$M*Q{*h3MVw+uI=%*ZrGeDOJ6^wOjY$syO zJR!sUR;Fk;IrF}*Iwo*)gQBys_E8JPQ}X-t_Dj9=Z=Wo+%uW@T+0-5dJH@(J(=?*! z1hvwdf(zhX3B5DLP2kn#CLUJ{H+`4_Dd190klRwqZu`kPotK-tL|G*EC(z48J;{T4 zv+6oh3pX9~ubA~wm(9=RZhq5ue#Xd0F&N+1%_=Ixo$1y4m64(2GBj4WGY2|(v?jT& zEXy@&j};{O6=5)}p~tKDKElt?VULect7A~3l=Xxgus8*-Vpea(Zt!-^8#Ba34t0v3 zLc>W}UD zRVEEyG&0vS&R#+2lAXUR-fngSk>XXwdoLP35+pyWeW?2+IHvpR!PmISJ1rJvvy@Jg z%;2Xi!hDm#JIx5P<4Eqwr`MK0K&1(q);C2h*4v>;>(?RLv>7YWLj2;rseNs*E0sL@ zO1+T;NN?%AvT>>Gy6#ii_bw}nu^DzNhE>uIr7wfKY^D@B3>u65gm$ZSb$_zob6HOe zoC(`-^9>(5v$fF%Ful7`PyP1&4MhZ-Yq-OF)~VB{=ino=gVyk(nZx*!_pcEHA#>2s zzLD#3UKftft#KJthRNzjh21LUy#L);w3fg3Fs; z99=zH=D9^Ruxj;7fkDaIs(?CqkEm9SNOg|?qmtCjXw%n=Oed8 zVZlFDa7)~jiJ?AZ4MW3wR&?#IorNe+e_C~doG_f76)$~XtD52$IDz@ph<_|wK9II({(b&l!Erqn#{&J;q z%ei;m;Noj-mnpY1IbiCB-9Pc(w}c_?Nkh62qEp1Rzq#RP`}CZ{jizsR0YE^N6@ua? zTV7|J`$V`NyovxvUaU{mf2{FVf!g;cD~Y7ay(@}UlP~3S?qU-H8T#gr3-2eqE$v8H zS)QT3F7vYC+4&QL{)+#?-GuuiuZXo}@$Uk%-hrNF%tD~BNoiqX&s(V?O|>WG_ernI znDc@U@A5IX?2S6SFlK@s(M8q1Eu_YcjD46$b+P`L!*ah#RVGRCFCZQehGj-Pp=YsR z-;S!sWs6#g{JI_T$Ggk&RmRC-CgQqBgEcMQ!d(dh{|f@ISste11hMyx(vcVSU~jC2 zE1kHwt$`*pHT;UWLkMY`s%_LcwQR&({~q+7;bR~W2u`SoxAABs3dz4LYE8I-;FAM+ zdR{eZaM_H4w>57nXm(xMw+{(kOXpZc88PB=BEbWjg`|R{d1t^*q3}{Wsr)Hwm%2DI8%%!`!e__a+u?0`G-yo-8=Q%(jjLs*Q#mB>m*~%3d(m zmsYrljxc&UW+7CkPTHpO60)l)`;lWA%?xni!w-)P#i76ax;^F?~HAF=39saUd}SQ^YpU)i_j?HB~lk)wkfmu0mQppWMA^OdJ3R9(){`s67T#428L5cqc&r=*S literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/unmute.imageset/Contents.json b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/unmute.imageset/Contents.json new file mode 100644 index 0000000000..9e82e3cad9 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/unmute.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "unmute.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/unmute.imageset/unmute.png b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Assets.xcassets/unmute.imageset/unmute.png new file mode 100644 index 0000000000000000000000000000000000000000..f7d8e148467ca518e74a06ab0a577ff9230d4962 GIT binary patch literal 3074 zcma);2{_dGAIC?Na~-P#35~%&FfeG@gdbxE z4)pK>IkLHQkTnzqg@XxdAP@-8rA6X=h|Vi=*~ktY&F6D)Fj!(@A~ewo%H~GF5Lhf0 z21mk>NQle=!b@iH$w?3v&*TH-BZo-mQMn8bpTTB<yR*Uz5q|{omvMR1;asc$u|_2aZVRG7{)CXD*w$To?|S%cIMKv;%*PaTuu|m zVDb257L`sS5@aMOgF(aDpslTG6f_Njj6`A}C^`lO!II%*2tCpYi?TtGF<3Mm5Boy? zk2P4K)7aE_CY{A!q0?k^niUm8w~3@c=t#Jgj84Hq$OtMHLa{-jC|G(Vh6=}gq_2{H zrsH8>q`bV*KCJ)8dVJ~F0glCDoSdDU;Pz+)!U=_SlNMCYz=q9pg(axOIJDl z;FI)awN|ji|Ak#8{Q|p-%ZOsI$g#xFuH}pI|Cj4SbYH=&awXq$IGKrTImq$@L_pwS zU(BttmoagUTsoQ0=K8VOu>^a0UjTVUv-xaZG@Ao*bOf1ukf{uod~C5?y?@xRpztvH zL0xrlKXirVSy@{ksL7NcTMR+1QVWOz0F-q|M0>wL)msH2>G}Q}+M-%_>fZU?=YfxzITKuIZI~z(Z5l#;{kKB5z@l>hZ%@5Rs() zYUa~Zmulbk)ONg{MkMc&H7dxIaRJUoxe5S$F;@1M62WoKoDTL}YM4ntmZS=f=lXO+ zb}Nj&r{!K#kywcZ8e`6L2dC>%W96OUr4ws*o)gw3suyqS-HAw1E9&CpvPDFt=79)V zv;O{KC{V|>u)eEiKQe2>gsw63pItwOIHPv3y3W^gQ09|jq5d8 zJp~;=H%0K0`ZV>u_7In22p4Vy&MP-$#%Ya*Bn%_V^Ta{VV__Y;f$g{oMrV#?!gs>C znt<@_Nk&VF;_17)JtsOFUqGCGIvZB@@VshFR_^WI^eRbToT$jaKzTR{Y`?^e4F}dw zDQ+oQg74FfaV%)~d8joBJV{^h(tzsB8_h7(fhYyFqhenR?0DJr_$4HuZVy>kwMn*&_18R0aUMO@1l z+S|rCh~mHXYwtatdARD3DcQ|V^^z_qDK&>O|D@$nB*zy1dz;vsW4&)gDvmm2n-u(5 zM^$*BEOx7Q*KS;nje3p3oOK>0yItq+YtmQB1l5frge{g;4m*J7eq*^4X#Vd4!hsPM z-d3j{>_6h|OgRPH^zg(aq$4ftNd!M(n|HNoF}P~|Nc^vYKFYC7d|s&du|V;K2&3P# zE5o&=$BR+`0k$DeFzC)Sa&fyssk< zUbC$x`u>>gX8O1tv?q|)=)a1%+UW!6yWxq{6R8h> zKz3zl?zyDY6gKzOV&G4 zKmyF-BQ!Vh%o`i{y**jD{$``A82jL&`ciO}u`{b88x6^-dsS_$ zxHI+nfxTWa>M2vM)OYLPqI*)x=8EsjvuI|6?Dh6UySBdy0wme=1BoIHs&4iP{88k literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Banners.storyboard b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Banners.storyboard new file mode 100644 index 0000000000..61b830a888 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Banners.storyboard @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Base.lproj/LaunchScreen.storyboard b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Base.lproj/LaunchScreen.storyboard rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Base.lproj/LaunchScreen.storyboard diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Base.lproj/Main.storyboard b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..25d301b6b2 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Base.lproj/Main.storyboard @@ -0,0 +1,1185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Info.plist b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Info.plist similarity index 93% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Info.plist rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Info.plist index 40c3f7549c..ac7ec52c9a 100644 --- a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Info.plist +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Info.plist @@ -3,7 +3,7 @@ AppLovinSdkKey - SDK_KEY_HERE + 05TMDQ5tZabpXQ45_UTbmEGNUtVAzSTzT6KmWQc5_CuWdzccS4DCITZoL3yIWUG3bbq60QC_d4WF28tUC4gVTF CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Interstitials.storyboard b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Interstitials.storyboard new file mode 100644 index 0000000000..d0fc34e22c --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Interstitials.storyboard @@ -0,0 +1,312 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Leaders.storyboard b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Leaders.storyboard new file mode 100644 index 0000000000..e312fcdaaa --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Leaders.storyboard @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/MRECs.storyboard b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/MRECs.storyboard new file mode 100644 index 0000000000..6f4e5a6b59 --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/MRECs.storyboard @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Rewarded.storyboard b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Rewarded.storyboard new file mode 100644 index 0000000000..90a1ae546c --- /dev/null +++ b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/Rewarded.storyboard @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/main.m b/AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/main.m similarity index 100% rename from DemoApp-ObjC/DemoApp-ObjC/Supporting Files/main.m rename to AppLovin MAX Demo App - ObjC/AppLovin MAX Demo App - ObjC/Supporting Files/main.m diff --git a/DemoApp-ObjC/Podfile b/AppLovin MAX Demo App - ObjC/Podfile similarity index 60% rename from DemoApp-ObjC/Podfile rename to AppLovin MAX Demo App - ObjC/Podfile index f06145ec59..120409bd12 100644 --- a/DemoApp-ObjC/Podfile +++ b/AppLovin MAX Demo App - ObjC/Podfile @@ -1,6 +1,6 @@ use_frameworks! inhibit_all_warnings! -target 'MAX Demo App' do +target 'AppLovin MAX Demo App - ObjC' do pod 'AppLovinSDK' end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift.xcodeproj/project.pbxproj b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..dd6a8a9d4c --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift.xcodeproj/project.pbxproj @@ -0,0 +1,792 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 1DDCE59D242AE564007EAC8D /* ALMAXFrameLayoutBannerAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDCE596242AE564007EAC8D /* ALMAXFrameLayoutBannerAdViewController.swift */; }; + 1DDCE59E242AE564007EAC8D /* ALMAXInterfaceBuilderBannerAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDCE597242AE564007EAC8D /* ALMAXInterfaceBuilderBannerAdViewController.swift */; }; + 1DDCE59F242AE564007EAC8D /* ALMAXAutoLayoutBannerAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDCE598242AE564007EAC8D /* ALMAXAutoLayoutBannerAdViewController.swift */; }; + 1DDCE5A0242AE564007EAC8D /* ALMAXRewardedAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDCE59A242AE564007EAC8D /* ALMAXRewardedAdViewController.swift */; }; + 1DDCE5A1242AE564007EAC8D /* ALMAXInterstitialAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDCE59C242AE564007EAC8D /* ALMAXInterstitialAdViewController.swift */; }; + 1DDCE5A5242AE5CE007EAC8D /* ALHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDCE5A3242AE5CE007EAC8D /* ALHomeViewController.swift */; }; + 1DDCE5A6242AE5CE007EAC8D /* ALBaseAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DDCE5A4242AE5CE007EAC8D /* ALBaseAdViewController.swift */; }; + 3763B23723357C4B00E49783 /* ALAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763B23623357C4B00E49783 /* ALAppDelegate.swift */; }; + 3763B23E23357C4D00E49783 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3763B23D23357C4D00E49783 /* Assets.xcassets */; }; + 3763B24123357C4D00E49783 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3763B23F23357C4D00E49783 /* LaunchScreen.storyboard */; }; + 37C4D0E8233D834900096894 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C4D0E7233D834900096894 /* main.swift */; }; + E5220BF3243BFCA600514F16 /* ALDemoInterfaceBuilderMRECViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5220BF2243BFCA600514F16 /* ALDemoInterfaceBuilderMRECViewController.swift */; }; + E5BDBDFA2432C4E800F82D79 /* ALDemoInterstitialBasicIntegrationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBDF42432C4E800F82D79 /* ALDemoInterstitialBasicIntegrationViewController.swift */; }; + E5BDBDFB2432C4E800F82D79 /* ALDemoInterstitialZoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBDF52432C4E800F82D79 /* ALDemoInterstitialZoneViewController.swift */; }; + E5BDBDFC2432C4E800F82D79 /* ALDemoInterstitialManualLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBDF62432C4E800F82D79 /* ALDemoInterstitialManualLoadingViewController.swift */; }; + E5BDBDFD2432C4E800F82D79 /* ALDemoRewardedVideosViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBDF82432C4E800F82D79 /* ALDemoRewardedVideosViewController.swift */; }; + E5BDBDFE2432C4E800F82D79 /* ALDemoRewardedVideosZoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBDF92432C4E800F82D79 /* ALDemoRewardedVideosZoneViewController.swift */; }; + E5BDBE852433F4FF00F82D79 /* ALDemoInterfaceBuilderBannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBE822433F4FF00F82D79 /* ALDemoInterfaceBuilderBannerViewController.swift */; }; + E5BDBE862433F4FF00F82D79 /* ALDemoBannerZoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBE832433F4FF00F82D79 /* ALDemoBannerZoneViewController.swift */; }; + E5BDBE872433F4FF00F82D79 /* ALDemoProgrammaticBannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBE842433F4FF00F82D79 /* ALDemoProgrammaticBannerViewController.swift */; }; + E5BDBE8B243404D100F82D79 /* ALDemoProgrammaticLeaderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBE89243404D100F82D79 /* ALDemoProgrammaticLeaderViewController.swift */; }; + E5BDBE8C243404D100F82D79 /* ALDemoInterfaceBuilderLeaderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBE8A243404D100F82D79 /* ALDemoInterfaceBuilderLeaderViewController.swift */; }; + E5BDBE8F2434140600F82D79 /* ALDemoProgrammaticMRecViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBE8E2434140600F82D79 /* ALDemoProgrammaticMRecViewController.swift */; }; + E5BDBF9824350B1200F82D79 /* ALDemoNativeAdProgrammaticViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBF9724350B1200F82D79 /* ALDemoNativeAdProgrammaticViewController.swift */; }; + E5BDBF9E24350B6000F82D79 /* ALDemoArticle.m in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBF9B24350B5F00F82D79 /* ALDemoArticle.m */; }; + E5BDBF9F24350B6000F82D79 /* ALDemoRSSFeedRetriever.m in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBF9D24350B5F00F82D79 /* ALDemoRSSFeedRetriever.m */; }; + E5BDBFB324350BBF00F82D79 /* Star_Sprite_3.5.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFA324350BBD00F82D79 /* Star_Sprite_3.5.png */; }; + E5BDBFB424350BBF00F82D79 /* Star_Sprite_2.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFA424350BBD00F82D79 /* Star_Sprite_2.png */; }; + E5BDBFB524350BBF00F82D79 /* Star_Sprite_0.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFA524350BBD00F82D79 /* Star_Sprite_0.png */; }; + E5BDBFB624350BBF00F82D79 /* Star_Sprite_5.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFA624350BBD00F82D79 /* Star_Sprite_5.png */; }; + E5BDBFB724350BBF00F82D79 /* Star_Sprite_0.5.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFA724350BBD00F82D79 /* Star_Sprite_0.5.png */; }; + E5BDBFB824350BBF00F82D79 /* applovin_card_muted.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFA824350BBD00F82D79 /* applovin_card_muted.png */; }; + E5BDBFB924350BBF00F82D79 /* Star_Sprite_1.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFA924350BBE00F82D79 /* Star_Sprite_1.png */; }; + E5BDBFBA24350BBF00F82D79 /* applovin_card_unmuted.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFAA24350BBE00F82D79 /* applovin_card_unmuted.png */; }; + E5BDBFBB24350BBF00F82D79 /* Star_Sprite_4.5.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFAB24350BBE00F82D79 /* Star_Sprite_4.5.png */; }; + E5BDBFBC24350BBF00F82D79 /* Star_Sprite_2.5.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFAC24350BBE00F82D79 /* Star_Sprite_2.5.png */; }; + E5BDBFBD24350BBF00F82D79 /* Star_Sprite_4.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFAD24350BBE00F82D79 /* Star_Sprite_4.png */; }; + E5BDBFBE24350BBF00F82D79 /* Star_Sprite_3.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFAE24350BBE00F82D79 /* Star_Sprite_3.png */; }; + E5BDBFBF24350BBF00F82D79 /* Star_Sprite_1.5.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFAF24350BBE00F82D79 /* Star_Sprite_1.5.png */; }; + E5BDBFC024350BBF00F82D79 /* applovin_card_learn_more.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFB024350BBE00F82D79 /* applovin_card_learn_more.png */; }; + E5BDBFC124350BBF00F82D79 /* applovin_card_play.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFB124350BBE00F82D79 /* applovin_card_play.png */; }; + E5BDBFC224350BBF00F82D79 /* applovin_card_replay.png in Resources */ = {isa = PBXBuildFile; fileRef = E5BDBFB224350BBF00F82D79 /* applovin_card_replay.png */; }; + E5BDBFC724350BE600F82D79 /* UIView+ALActivityIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBFC624350BE600F82D79 /* UIView+ALActivityIndicator.m */; }; + E5BDBFD324350C1400F82D79 /* ALCarouselCardView.m in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBFC924350C1300F82D79 /* ALCarouselCardView.m */; }; + E5BDBFD424350C1400F82D79 /* ALCarouselMediaView.m in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBFCA24350C1300F82D79 /* ALCarouselMediaView.m */; }; + E5BDBFD524350C1400F82D79 /* ALCarouselView.m in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBFCD24350C1300F82D79 /* ALCarouselView.m */; }; + E5BDBFD624350C1400F82D79 /* ALCarouselReplayOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBFCE24350C1300F82D79 /* ALCarouselReplayOverlayView.m */; }; + E5BDBFD724350C1400F82D79 /* ALNativeAdVideoView.m in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBFD124350C1400F82D79 /* ALNativeAdVideoView.m */; }; + E5BDBFE124350C3A00F82D79 /* ALCarouselViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBFDA24350C3900F82D79 /* ALCarouselViewModel.m */; }; + E5BDBFE224350C3A00F82D79 /* ALCarouselCardState.m in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBFDE24350C3A00F82D79 /* ALCarouselCardState.m */; }; + E5BDBFE324350C3A00F82D79 /* ALNativeAdVideoPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBFDF24350C3A00F82D79 /* ALNativeAdVideoPlayer.m */; }; + E5BDBFE624350CC100F82D79 /* ALDemoNativeAdFeedTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBFE524350CC100F82D79 /* ALDemoNativeAdFeedTableViewController.swift */; }; + E5BDBFF124354C4000F82D79 /* ALEventTrackingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BDBFF024354C3F00F82D79 /* ALEventTrackingViewController.swift */; }; + E5EE50D5241C259D0032B659 /* Banners.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E5EE50D4241C259D0032B659 /* Banners.storyboard */; }; + E5EE50D8241C25AA0032B659 /* Leaders.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E5EE50D6241C25AA0032B659 /* Leaders.storyboard */; }; + E5EE50D9241C25AA0032B659 /* Interstitials.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E5EE50D7241C25AA0032B659 /* Interstitials.storyboard */; }; + E5EE50DC241C25B70032B659 /* Rewarded.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E5EE50DA241C25B60032B659 /* Rewarded.storyboard */; }; + E5EE50DD241C25B70032B659 /* MRECs.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E5EE50DB241C25B60032B659 /* MRECs.storyboard */; }; + E5EE50E7241D9E5B0032B659 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3763B23A23357C4B00E49783 /* Main.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1DDCE596242AE564007EAC8D /* ALMAXFrameLayoutBannerAdViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALMAXFrameLayoutBannerAdViewController.swift; sourceTree = ""; }; + 1DDCE597242AE564007EAC8D /* ALMAXInterfaceBuilderBannerAdViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALMAXInterfaceBuilderBannerAdViewController.swift; sourceTree = ""; }; + 1DDCE598242AE564007EAC8D /* ALMAXAutoLayoutBannerAdViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALMAXAutoLayoutBannerAdViewController.swift; sourceTree = ""; }; + 1DDCE59A242AE564007EAC8D /* ALMAXRewardedAdViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALMAXRewardedAdViewController.swift; sourceTree = ""; }; + 1DDCE59C242AE564007EAC8D /* ALMAXInterstitialAdViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALMAXInterstitialAdViewController.swift; sourceTree = ""; }; + 1DDCE5A3242AE5CE007EAC8D /* ALHomeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALHomeViewController.swift; sourceTree = ""; }; + 1DDCE5A4242AE5CE007EAC8D /* ALBaseAdViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALBaseAdViewController.swift; sourceTree = ""; }; + 3763B23323357C4B00E49783 /* AppLovin MAX Demo App - Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AppLovin MAX Demo App - Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3763B23623357C4B00E49783 /* ALAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALAppDelegate.swift; sourceTree = ""; }; + 3763B23B23357C4B00E49783 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 3763B23D23357C4D00E49783 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 3763B24023357C4D00E49783 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 3763B24223357C4D00E49783 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 37C4D0E7233D834900096894 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; + E5220BF2243BFCA600514F16 /* ALDemoInterfaceBuilderMRECViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALDemoInterfaceBuilderMRECViewController.swift; sourceTree = ""; }; + E5BDBDF42432C4E800F82D79 /* ALDemoInterstitialBasicIntegrationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoInterstitialBasicIntegrationViewController.swift; sourceTree = ""; }; + E5BDBDF52432C4E800F82D79 /* ALDemoInterstitialZoneViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoInterstitialZoneViewController.swift; sourceTree = ""; }; + E5BDBDF62432C4E800F82D79 /* ALDemoInterstitialManualLoadingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoInterstitialManualLoadingViewController.swift; sourceTree = ""; }; + E5BDBDF82432C4E800F82D79 /* ALDemoRewardedVideosViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoRewardedVideosViewController.swift; sourceTree = ""; }; + E5BDBDF92432C4E800F82D79 /* ALDemoRewardedVideosZoneViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoRewardedVideosZoneViewController.swift; sourceTree = ""; }; + E5BDBE822433F4FF00F82D79 /* ALDemoInterfaceBuilderBannerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoInterfaceBuilderBannerViewController.swift; sourceTree = ""; }; + E5BDBE832433F4FF00F82D79 /* ALDemoBannerZoneViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoBannerZoneViewController.swift; sourceTree = ""; }; + E5BDBE842433F4FF00F82D79 /* ALDemoProgrammaticBannerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoProgrammaticBannerViewController.swift; sourceTree = ""; }; + E5BDBE89243404D100F82D79 /* ALDemoProgrammaticLeaderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoProgrammaticLeaderViewController.swift; sourceTree = ""; }; + E5BDBE8A243404D100F82D79 /* ALDemoInterfaceBuilderLeaderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoInterfaceBuilderLeaderViewController.swift; sourceTree = ""; }; + E5BDBE8E2434140600F82D79 /* ALDemoProgrammaticMRecViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoProgrammaticMRecViewController.swift; sourceTree = ""; }; + E5BDBF9724350B1200F82D79 /* ALDemoNativeAdProgrammaticViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoNativeAdProgrammaticViewController.swift; sourceTree = ""; }; + E5BDBF9A24350B5F00F82D79 /* ALDemoRSSFeedRetriever.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoRSSFeedRetriever.h; sourceTree = ""; }; + E5BDBF9B24350B5F00F82D79 /* ALDemoArticle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoArticle.m; sourceTree = ""; }; + E5BDBF9C24350B5F00F82D79 /* ALDemoArticle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDemoArticle.h; sourceTree = ""; }; + E5BDBF9D24350B5F00F82D79 /* ALDemoRSSFeedRetriever.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALDemoRSSFeedRetriever.m; sourceTree = ""; }; + E5BDBFA124350B9600F82D79 /* ALCarouselViewSettings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALCarouselViewSettings.h; sourceTree = ""; }; + E5BDBFA324350BBD00F82D79 /* Star_Sprite_3.5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_3.5.png; sourceTree = ""; }; + E5BDBFA424350BBD00F82D79 /* Star_Sprite_2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_2.png; sourceTree = ""; }; + E5BDBFA524350BBD00F82D79 /* Star_Sprite_0.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_0.png; sourceTree = ""; }; + E5BDBFA624350BBD00F82D79 /* Star_Sprite_5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_5.png; sourceTree = ""; }; + E5BDBFA724350BBD00F82D79 /* Star_Sprite_0.5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_0.5.png; sourceTree = ""; }; + E5BDBFA824350BBD00F82D79 /* applovin_card_muted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = applovin_card_muted.png; sourceTree = ""; }; + E5BDBFA924350BBE00F82D79 /* Star_Sprite_1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_1.png; sourceTree = ""; }; + E5BDBFAA24350BBE00F82D79 /* applovin_card_unmuted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = applovin_card_unmuted.png; sourceTree = ""; }; + E5BDBFAB24350BBE00F82D79 /* Star_Sprite_4.5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_4.5.png; sourceTree = ""; }; + E5BDBFAC24350BBE00F82D79 /* Star_Sprite_2.5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_2.5.png; sourceTree = ""; }; + E5BDBFAD24350BBE00F82D79 /* Star_Sprite_4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_4.png; sourceTree = ""; }; + E5BDBFAE24350BBE00F82D79 /* Star_Sprite_3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_3.png; sourceTree = ""; }; + E5BDBFAF24350BBE00F82D79 /* Star_Sprite_1.5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Star_Sprite_1.5.png; sourceTree = ""; }; + E5BDBFB024350BBE00F82D79 /* applovin_card_learn_more.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = applovin_card_learn_more.png; sourceTree = ""; }; + E5BDBFB124350BBE00F82D79 /* applovin_card_play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = applovin_card_play.png; sourceTree = ""; }; + E5BDBFB224350BBF00F82D79 /* applovin_card_replay.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = applovin_card_replay.png; sourceTree = ""; }; + E5BDBFC424350BE600F82D79 /* UIView+ALActivityIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+ALActivityIndicator.h"; sourceTree = ""; }; + E5BDBFC524350BE600F82D79 /* ALCarouselView+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ALCarouselView+Internal.h"; sourceTree = ""; }; + E5BDBFC624350BE600F82D79 /* UIView+ALActivityIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+ALActivityIndicator.m"; sourceTree = ""; }; + E5BDBFC924350C1300F82D79 /* ALCarouselCardView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselCardView.m; sourceTree = ""; }; + E5BDBFCA24350C1300F82D79 /* ALCarouselMediaView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselMediaView.m; sourceTree = ""; }; + E5BDBFCB24350C1300F82D79 /* ALCarouselCardView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselCardView.h; sourceTree = ""; }; + E5BDBFCC24350C1300F82D79 /* ALCarouselView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselView.h; sourceTree = ""; }; + E5BDBFCD24350C1300F82D79 /* ALCarouselView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselView.m; sourceTree = ""; }; + E5BDBFCE24350C1300F82D79 /* ALCarouselReplayOverlayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselReplayOverlayView.m; sourceTree = ""; }; + E5BDBFCF24350C1400F82D79 /* ALCarouselReplayOverlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselReplayOverlayView.h; sourceTree = ""; }; + E5BDBFD024350C1400F82D79 /* ALNativeAdVideoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALNativeAdVideoView.h; sourceTree = ""; }; + E5BDBFD124350C1400F82D79 /* ALNativeAdVideoView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALNativeAdVideoView.m; sourceTree = ""; }; + E5BDBFD224350C1400F82D79 /* ALCarouselMediaView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselMediaView.h; sourceTree = ""; }; + E5BDBFD924350C3900F82D79 /* ALCarouselViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselViewModel.h; sourceTree = ""; }; + E5BDBFDA24350C3900F82D79 /* ALCarouselViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselViewModel.m; sourceTree = ""; }; + E5BDBFDB24350C3900F82D79 /* ALDebugLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALDebugLog.h; sourceTree = ""; }; + E5BDBFDC24350C3900F82D79 /* ALNativeAdVideoPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALNativeAdVideoPlayer.h; sourceTree = ""; }; + E5BDBFDD24350C3A00F82D79 /* ALCarouselCardState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselCardState.h; sourceTree = ""; }; + E5BDBFDE24350C3A00F82D79 /* ALCarouselCardState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALCarouselCardState.m; sourceTree = ""; }; + E5BDBFDF24350C3A00F82D79 /* ALNativeAdVideoPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALNativeAdVideoPlayer.m; sourceTree = ""; }; + E5BDBFE024350C3A00F82D79 /* ALCarouselRenderingProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALCarouselRenderingProtocol.h; sourceTree = ""; }; + E5BDBFE524350CC100F82D79 /* ALDemoNativeAdFeedTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALDemoNativeAdFeedTableViewController.swift; sourceTree = ""; }; + E5BDBFEC243515C100F82D79 /* AppLovin MAX Demo App Swift-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AppLovin MAX Demo App Swift-Bridging-Header.h"; sourceTree = ""; }; + E5BDBFEF2435197700F82D79 /* AppLovin MAX Demo App-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AppLovin MAX Demo App-Bridging-Header.h"; sourceTree = ""; }; + E5BDBFF024354C3F00F82D79 /* ALEventTrackingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALEventTrackingViewController.swift; sourceTree = ""; }; + E5EE50D4241C259D0032B659 /* Banners.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Banners.storyboard; sourceTree = ""; }; + E5EE50D6241C25AA0032B659 /* Leaders.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Leaders.storyboard; sourceTree = ""; }; + E5EE50D7241C25AA0032B659 /* Interstitials.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Interstitials.storyboard; sourceTree = ""; }; + E5EE50DA241C25B60032B659 /* Rewarded.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Rewarded.storyboard; sourceTree = ""; }; + E5EE50DB241C25B60032B659 /* MRECs.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MRECs.storyboard; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3763B23023357C4B00E49783 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 01A619B207BFFD657E517608 /* Pods */ = { + isa = PBXGroup; + children = ( + ); + path = Pods; + sourceTree = ""; + }; + 1DDCE595242AE564007EAC8D /* Banners */ = { + isa = PBXGroup; + children = ( + 1DDCE596242AE564007EAC8D /* ALMAXFrameLayoutBannerAdViewController.swift */, + 1DDCE597242AE564007EAC8D /* ALMAXInterfaceBuilderBannerAdViewController.swift */, + 1DDCE598242AE564007EAC8D /* ALMAXAutoLayoutBannerAdViewController.swift */, + ); + path = Banners; + sourceTree = ""; + }; + 1DDCE599242AE564007EAC8D /* Rewarded */ = { + isa = PBXGroup; + children = ( + 1DDCE59A242AE564007EAC8D /* ALMAXRewardedAdViewController.swift */, + ); + path = Rewarded; + sourceTree = ""; + }; + 1DDCE59B242AE564007EAC8D /* Interstitials */ = { + isa = PBXGroup; + children = ( + 1DDCE59C242AE564007EAC8D /* ALMAXInterstitialAdViewController.swift */, + ); + path = Interstitials; + sourceTree = ""; + }; + 1DDCE5A2242AE5CE007EAC8D /* Base Classes */ = { + isa = PBXGroup; + children = ( + 1DDCE5A3242AE5CE007EAC8D /* ALHomeViewController.swift */, + 1DDCE5A4242AE5CE007EAC8D /* ALBaseAdViewController.swift */, + ); + path = "Base Classes"; + sourceTree = ""; + }; + 3763B22A23357C4B00E49783 = { + isa = PBXGroup; + children = ( + E5BDBFEF2435197700F82D79 /* AppLovin MAX Demo App-Bridging-Header.h */, + 3763B23523357C4B00E49783 /* AppLovin MAX Demo App - Swift */, + 3763B23423357C4B00E49783 /* Products */, + 01A619B207BFFD657E517608 /* Pods */, + ); + sourceTree = ""; + }; + 3763B23423357C4B00E49783 /* Products */ = { + isa = PBXGroup; + children = ( + 3763B23323357C4B00E49783 /* AppLovin MAX Demo App - Swift.app */, + ); + name = Products; + sourceTree = ""; + }; + 3763B23523357C4B00E49783 /* AppLovin MAX Demo App - Swift */ = { + isa = PBXGroup; + children = ( + E5BDBDF22432C4E800F82D79 /* AppLovin */, + 1DDCE5A2242AE5CE007EAC8D /* Base Classes */, + 3763B24A23357CFB00E49783 /* MAX */, + 3763B23623357C4B00E49783 /* ALAppDelegate.swift */, + 3763B25323357D7900E49783 /* Supporting Files */, + ); + path = "AppLovin MAX Demo App - Swift"; + sourceTree = ""; + }; + 3763B24A23357CFB00E49783 /* MAX */ = { + isa = PBXGroup; + children = ( + 1DDCE595242AE564007EAC8D /* Banners */, + 1DDCE59B242AE564007EAC8D /* Interstitials */, + 1DDCE599242AE564007EAC8D /* Rewarded */, + ); + path = MAX; + sourceTree = ""; + }; + 3763B25323357D7900E49783 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + E5BDBFEC243515C100F82D79 /* AppLovin MAX Demo App Swift-Bridging-Header.h */, + E5EE50DB241C25B60032B659 /* MRECs.storyboard */, + E5EE50DA241C25B60032B659 /* Rewarded.storyboard */, + E5EE50D7241C25AA0032B659 /* Interstitials.storyboard */, + E5EE50D6241C25AA0032B659 /* Leaders.storyboard */, + E5EE50D4241C259D0032B659 /* Banners.storyboard */, + 37C4D0E7233D834900096894 /* main.swift */, + 3763B24223357C4D00E49783 /* Info.plist */, + 3763B23D23357C4D00E49783 /* Assets.xcassets */, + 3763B23F23357C4D00E49783 /* LaunchScreen.storyboard */, + 3763B23A23357C4B00E49783 /* Main.storyboard */, + ); + path = "Supporting Files"; + sourceTree = ""; + }; + E5BDBDF22432C4E800F82D79 /* AppLovin */ = { + isa = PBXGroup; + children = ( + E5BDBFF224354C5800F82D79 /* Event Tracking */, + E5BDBFE824350D0100F82D79 /* Native */, + E5BDBE902434141100F82D79 /* Mrecs */, + E5BDBE8D243404DD00F82D79 /* Leaders */, + E5BDBE882433F50800F82D79 /* Banners */, + E5BDBDF32432C4E800F82D79 /* Interstitials */, + E5BDBDF72432C4E800F82D79 /* Rewarded */, + ); + name = AppLovin; + path = "AppLovin MAX Demo App - Swift/AppLovin"; + sourceTree = SOURCE_ROOT; + }; + E5BDBDF32432C4E800F82D79 /* Interstitials */ = { + isa = PBXGroup; + children = ( + E5BDBDF42432C4E800F82D79 /* ALDemoInterstitialBasicIntegrationViewController.swift */, + E5BDBDF52432C4E800F82D79 /* ALDemoInterstitialZoneViewController.swift */, + E5BDBDF62432C4E800F82D79 /* ALDemoInterstitialManualLoadingViewController.swift */, + ); + path = Interstitials; + sourceTree = ""; + }; + E5BDBDF72432C4E800F82D79 /* Rewarded */ = { + isa = PBXGroup; + children = ( + E5BDBDF82432C4E800F82D79 /* ALDemoRewardedVideosViewController.swift */, + E5BDBDF92432C4E800F82D79 /* ALDemoRewardedVideosZoneViewController.swift */, + ); + path = Rewarded; + sourceTree = ""; + }; + E5BDBE882433F50800F82D79 /* Banners */ = { + isa = PBXGroup; + children = ( + E5BDBE842433F4FF00F82D79 /* ALDemoProgrammaticBannerViewController.swift */, + E5BDBE822433F4FF00F82D79 /* ALDemoInterfaceBuilderBannerViewController.swift */, + E5BDBE832433F4FF00F82D79 /* ALDemoBannerZoneViewController.swift */, + ); + path = Banners; + sourceTree = ""; + }; + E5BDBE8D243404DD00F82D79 /* Leaders */ = { + isa = PBXGroup; + children = ( + E5BDBE89243404D100F82D79 /* ALDemoProgrammaticLeaderViewController.swift */, + E5BDBE8A243404D100F82D79 /* ALDemoInterfaceBuilderLeaderViewController.swift */, + ); + path = Leaders; + sourceTree = ""; + }; + E5BDBE902434141100F82D79 /* Mrecs */ = { + isa = PBXGroup; + children = ( + E5BDBE8E2434140600F82D79 /* ALDemoProgrammaticMRecViewController.swift */, + E5220BF2243BFCA600514F16 /* ALDemoInterfaceBuilderMRECViewController.swift */, + ); + path = Mrecs; + sourceTree = ""; + }; + E5BDBF9924350B2400F82D79 /* Programattic */ = { + isa = PBXGroup; + children = ( + E5BDBF9724350B1200F82D79 /* ALDemoNativeAdProgrammaticViewController.swift */, + ); + path = Programattic; + sourceTree = ""; + }; + E5BDBFA024350B7400F82D79 /* RSS Feed Parsing */ = { + isa = PBXGroup; + children = ( + E5BDBF9A24350B5F00F82D79 /* ALDemoRSSFeedRetriever.h */, + E5BDBF9D24350B5F00F82D79 /* ALDemoRSSFeedRetriever.m */, + E5BDBF9C24350B5F00F82D79 /* ALDemoArticle.h */, + E5BDBF9B24350B5F00F82D79 /* ALDemoArticle.m */, + ); + path = "RSS Feed Parsing"; + sourceTree = ""; + }; + E5BDBFA224350BA600F82D79 /* Customizable SETTINGS */ = { + isa = PBXGroup; + children = ( + E5BDBFA124350B9600F82D79 /* ALCarouselViewSettings.h */, + ); + path = "Customizable SETTINGS"; + sourceTree = ""; + }; + E5BDBFC324350BD000F82D79 /* Assets */ = { + isa = PBXGroup; + children = ( + E5BDBFA524350BBD00F82D79 /* Star_Sprite_0.png */, + E5BDBFA724350BBD00F82D79 /* Star_Sprite_0.5.png */, + E5BDBFA924350BBE00F82D79 /* Star_Sprite_1.png */, + E5BDBFAF24350BBE00F82D79 /* Star_Sprite_1.5.png */, + E5BDBFA424350BBD00F82D79 /* Star_Sprite_2.png */, + E5BDBFAC24350BBE00F82D79 /* Star_Sprite_2.5.png */, + E5BDBFAE24350BBE00F82D79 /* Star_Sprite_3.png */, + E5BDBFA324350BBD00F82D79 /* Star_Sprite_3.5.png */, + E5BDBFAD24350BBE00F82D79 /* Star_Sprite_4.png */, + E5BDBFAB24350BBE00F82D79 /* Star_Sprite_4.5.png */, + E5BDBFA624350BBD00F82D79 /* Star_Sprite_5.png */, + E5BDBFB024350BBE00F82D79 /* applovin_card_learn_more.png */, + E5BDBFA824350BBD00F82D79 /* applovin_card_muted.png */, + E5BDBFB124350BBE00F82D79 /* applovin_card_play.png */, + E5BDBFB224350BBF00F82D79 /* applovin_card_replay.png */, + E5BDBFAA24350BBE00F82D79 /* applovin_card_unmuted.png */, + ); + path = Assets; + sourceTree = ""; + }; + E5BDBFC824350C0000F82D79 /* Categories */ = { + isa = PBXGroup; + children = ( + E5BDBFC524350BE600F82D79 /* ALCarouselView+Internal.h */, + E5BDBFC624350BE600F82D79 /* UIView+ALActivityIndicator.m */, + E5BDBFC424350BE600F82D79 /* UIView+ALActivityIndicator.h */, + ); + path = Categories; + sourceTree = ""; + }; + E5BDBFD824350C1B00F82D79 /* Views */ = { + isa = PBXGroup; + children = ( + E5BDBFCB24350C1300F82D79 /* ALCarouselCardView.h */, + E5BDBFC924350C1300F82D79 /* ALCarouselCardView.m */, + E5BDBFD224350C1400F82D79 /* ALCarouselMediaView.h */, + E5BDBFCA24350C1300F82D79 /* ALCarouselMediaView.m */, + E5BDBFCF24350C1400F82D79 /* ALCarouselReplayOverlayView.h */, + E5BDBFCE24350C1300F82D79 /* ALCarouselReplayOverlayView.m */, + E5BDBFCC24350C1300F82D79 /* ALCarouselView.h */, + E5BDBFCD24350C1300F82D79 /* ALCarouselView.m */, + E5BDBFD024350C1400F82D79 /* ALNativeAdVideoView.h */, + E5BDBFD124350C1400F82D79 /* ALNativeAdVideoView.m */, + ); + path = Views; + sourceTree = ""; + }; + E5BDBFE424350C7500F82D79 /* Carousel UI */ = { + isa = PBXGroup; + children = ( + E5BDBFDD24350C3A00F82D79 /* ALCarouselCardState.h */, + E5BDBFDE24350C3A00F82D79 /* ALCarouselCardState.m */, + E5BDBFE024350C3A00F82D79 /* ALCarouselRenderingProtocol.h */, + E5BDBFD924350C3900F82D79 /* ALCarouselViewModel.h */, + E5BDBFDA24350C3900F82D79 /* ALCarouselViewModel.m */, + E5BDBFDB24350C3900F82D79 /* ALDebugLog.h */, + E5BDBFDC24350C3900F82D79 /* ALNativeAdVideoPlayer.h */, + E5BDBFDF24350C3A00F82D79 /* ALNativeAdVideoPlayer.m */, + E5BDBFD824350C1B00F82D79 /* Views */, + E5BDBFC824350C0000F82D79 /* Categories */, + E5BDBFC324350BD000F82D79 /* Assets */, + E5BDBFA224350BA600F82D79 /* Customizable SETTINGS */, + ); + path = "Carousel UI"; + sourceTree = ""; + }; + E5BDBFE724350CD200F82D79 /* Feed */ = { + isa = PBXGroup; + children = ( + E5BDBFE524350CC100F82D79 /* ALDemoNativeAdFeedTableViewController.swift */, + E5BDBFE424350C7500F82D79 /* Carousel UI */, + E5BDBFA024350B7400F82D79 /* RSS Feed Parsing */, + ); + path = "Feed "; + sourceTree = ""; + }; + E5BDBFE824350D0100F82D79 /* Native */ = { + isa = PBXGroup; + children = ( + E5BDBFE724350CD200F82D79 /* Feed */, + E5BDBF9924350B2400F82D79 /* Programattic */, + ); + path = Native; + sourceTree = ""; + }; + E5BDBFF224354C5800F82D79 /* Event Tracking */ = { + isa = PBXGroup; + children = ( + E5BDBFF024354C3F00F82D79 /* ALEventTrackingViewController.swift */, + ); + path = "Event Tracking"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 3763B23223357C4B00E49783 /* AppLovin MAX Demo App - Swift */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3763B24523357C4D00E49783 /* Build configuration list for PBXNativeTarget "AppLovin MAX Demo App - Swift" */; + buildPhases = ( + 3763B22F23357C4B00E49783 /* Sources */, + 3763B23023357C4B00E49783 /* Frameworks */, + 3763B23123357C4B00E49783 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "AppLovin MAX Demo App - Swift"; + productName = "DemoApp-Swift"; + productReference = 3763B23323357C4B00E49783 /* AppLovin MAX Demo App - Swift.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 3763B22B23357C4B00E49783 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1030; + LastUpgradeCheck = 1030; + ORGANIZATIONNAME = AppLovin; + TargetAttributes = { + 3763B23223357C4B00E49783 = { + CreatedOnToolsVersion = 10.3; + LastSwiftMigration = 1030; + }; + }; + }; + buildConfigurationList = 3763B22E23357C4B00E49783 /* Build configuration list for PBXProject "AppLovin MAX Demo App - Swift" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 3763B22A23357C4B00E49783; + productRefGroup = 3763B23423357C4B00E49783 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 3763B23223357C4B00E49783 /* AppLovin MAX Demo App - Swift */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 3763B23123357C4B00E49783 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E5BDBFB724350BBF00F82D79 /* Star_Sprite_0.5.png in Resources */, + E5BDBFBD24350BBF00F82D79 /* Star_Sprite_4.png in Resources */, + E5EE50D8241C25AA0032B659 /* Leaders.storyboard in Resources */, + E5BDBFB824350BBF00F82D79 /* applovin_card_muted.png in Resources */, + E5EE50DD241C25B70032B659 /* MRECs.storyboard in Resources */, + 3763B24123357C4D00E49783 /* LaunchScreen.storyboard in Resources */, + E5BDBFC124350BBF00F82D79 /* applovin_card_play.png in Resources */, + E5EE50E7241D9E5B0032B659 /* Main.storyboard in Resources */, + E5BDBFBC24350BBF00F82D79 /* Star_Sprite_2.5.png in Resources */, + E5BDBFC224350BBF00F82D79 /* applovin_card_replay.png in Resources */, + E5BDBFBB24350BBF00F82D79 /* Star_Sprite_4.5.png in Resources */, + E5BDBFB624350BBF00F82D79 /* Star_Sprite_5.png in Resources */, + E5BDBFB924350BBF00F82D79 /* Star_Sprite_1.png in Resources */, + E5BDBFB524350BBF00F82D79 /* Star_Sprite_0.png in Resources */, + E5EE50DC241C25B70032B659 /* Rewarded.storyboard in Resources */, + E5BDBFB424350BBF00F82D79 /* Star_Sprite_2.png in Resources */, + E5BDBFBF24350BBF00F82D79 /* Star_Sprite_1.5.png in Resources */, + 3763B23E23357C4D00E49783 /* Assets.xcassets in Resources */, + E5BDBFC024350BBF00F82D79 /* applovin_card_learn_more.png in Resources */, + E5EE50D5241C259D0032B659 /* Banners.storyboard in Resources */, + E5BDBFB324350BBF00F82D79 /* Star_Sprite_3.5.png in Resources */, + E5EE50D9241C25AA0032B659 /* Interstitials.storyboard in Resources */, + E5BDBFBE24350BBF00F82D79 /* Star_Sprite_3.png in Resources */, + E5BDBFBA24350BBF00F82D79 /* applovin_card_unmuted.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3763B22F23357C4B00E49783 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E5BDBE852433F4FF00F82D79 /* ALDemoInterfaceBuilderBannerViewController.swift in Sources */, + 1DDCE5A1242AE564007EAC8D /* ALMAXInterstitialAdViewController.swift in Sources */, + E5BDBE8C243404D100F82D79 /* ALDemoInterfaceBuilderLeaderViewController.swift in Sources */, + E5BDBFD324350C1400F82D79 /* ALCarouselCardView.m in Sources */, + E5BDBFD424350C1400F82D79 /* ALCarouselMediaView.m in Sources */, + E5BDBE862433F4FF00F82D79 /* ALDemoBannerZoneViewController.swift in Sources */, + E5BDBE8B243404D100F82D79 /* ALDemoProgrammaticLeaderViewController.swift in Sources */, + 1DDCE59E242AE564007EAC8D /* ALMAXInterfaceBuilderBannerAdViewController.swift in Sources */, + E5BDBF9E24350B6000F82D79 /* ALDemoArticle.m in Sources */, + 1DDCE5A5242AE5CE007EAC8D /* ALHomeViewController.swift in Sources */, + E5BDBDFC2432C4E800F82D79 /* ALDemoInterstitialManualLoadingViewController.swift in Sources */, + E5BDBDFE2432C4E800F82D79 /* ALDemoRewardedVideosZoneViewController.swift in Sources */, + 1DDCE59F242AE564007EAC8D /* ALMAXAutoLayoutBannerAdViewController.swift in Sources */, + E5BDBF9824350B1200F82D79 /* ALDemoNativeAdProgrammaticViewController.swift in Sources */, + E5BDBFE224350C3A00F82D79 /* ALCarouselCardState.m in Sources */, + E5BDBFE324350C3A00F82D79 /* ALNativeAdVideoPlayer.m in Sources */, + 1DDCE5A0242AE564007EAC8D /* ALMAXRewardedAdViewController.swift in Sources */, + E5BDBE8F2434140600F82D79 /* ALDemoProgrammaticMRecViewController.swift in Sources */, + 37C4D0E8233D834900096894 /* main.swift in Sources */, + E5BDBFE624350CC100F82D79 /* ALDemoNativeAdFeedTableViewController.swift in Sources */, + E5BDBF9F24350B6000F82D79 /* ALDemoRSSFeedRetriever.m in Sources */, + 1DDCE5A6242AE5CE007EAC8D /* ALBaseAdViewController.swift in Sources */, + 1DDCE59D242AE564007EAC8D /* ALMAXFrameLayoutBannerAdViewController.swift in Sources */, + E5BDBFC724350BE600F82D79 /* UIView+ALActivityIndicator.m in Sources */, + E5BDBDFA2432C4E800F82D79 /* ALDemoInterstitialBasicIntegrationViewController.swift in Sources */, + 3763B23723357C4B00E49783 /* ALAppDelegate.swift in Sources */, + E5BDBE872433F4FF00F82D79 /* ALDemoProgrammaticBannerViewController.swift in Sources */, + E5BDBFD524350C1400F82D79 /* ALCarouselView.m in Sources */, + E5BDBFE124350C3A00F82D79 /* ALCarouselViewModel.m in Sources */, + E5220BF3243BFCA600514F16 /* ALDemoInterfaceBuilderMRECViewController.swift in Sources */, + E5BDBFF124354C4000F82D79 /* ALEventTrackingViewController.swift in Sources */, + E5BDBFD624350C1400F82D79 /* ALCarouselReplayOverlayView.m in Sources */, + E5BDBDFD2432C4E800F82D79 /* ALDemoRewardedVideosViewController.swift in Sources */, + E5BDBFD724350C1400F82D79 /* ALNativeAdVideoView.m in Sources */, + E5BDBDFB2432C4E800F82D79 /* ALDemoInterstitialZoneViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 3763B23A23357C4B00E49783 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 3763B23B23357C4B00E49783 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 3763B23F23357C4D00E49783 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 3763B24023357C4D00E49783 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 3763B24323357C4D00E49783 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 3763B24423357C4D00E49783 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 3763B24623357C4D00E49783 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 7WXN7X228H; + INFOPLIST_FILE = "$(SRCROOT)/AppLovin MAX Demo App - Swift/Supporting Files/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = com.applovin.enterprise.apps.demoapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "AppLovin MAX Demo App-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 3763B24723357C4D00E49783 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 7WXN7X228H; + INFOPLIST_FILE = "$(SRCROOT)/AppLovin MAX Demo App - Swift/Supporting Files/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = com.applovin.enterprise.apps.demoapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "AppLovin MAX Demo App-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3763B22E23357C4B00E49783 /* Build configuration list for PBXProject "AppLovin MAX Demo App - Swift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3763B24323357C4D00E49783 /* Debug */, + 3763B24423357C4D00E49783 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3763B24523357C4D00E49783 /* Build configuration list for PBXNativeTarget "AppLovin MAX Demo App - Swift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3763B24623357C4D00E49783 /* Debug */, + 3763B24723357C4D00E49783 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 3763B22B23357C4B00E49783 /* Project object */; +} diff --git a/DemoApp-Swift/DemoApp-Swift/ALAppDelegate.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/ALAppDelegate.swift similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/ALAppDelegate.swift rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/ALAppDelegate.swift diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoBannerZoneViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoBannerZoneViewController.swift new file mode 100644 index 0000000000..4708a4e040 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoBannerZoneViewController.swift @@ -0,0 +1,145 @@ +// +// ALDemoBannerZoneViewController.swift +// iOS-SDK-Demo-Swift +// +// Created by Suyash Saxena on 6/19/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoBannerZoneViewController : ALBaseAdViewController +{ + let kBannerHeight: CGFloat = 50 + + private let adView = ALAdView(size: ALAdSize.banner, zoneIdentifier: "YOUR_ZONE_ID") + @IBOutlet weak var loadButton: UIBarButtonItem! + + // MARK: View Lifecycle + + override func viewDidAppear(_ animated: Bool) + { + super.viewDidAppear(animated) + + // Optional: Implement the ad delegates to receive ad events. + adView.adLoadDelegate = self + adView.adDisplayDelegate = self + adView.adEventDelegate = self + adView.translatesAutoresizingMaskIntoConstraints = false + + // Call loadNextAd() to start showing ads + adView.loadNextAd() + + // Center the banner and anchor it to the bottom of the screen. + view.addSubview(adView) + view.addConstraints([ + constraint(with: adView, attribute: .leading), + constraint(with: adView, attribute: .trailing), + constraint(with: adView, attribute: .bottom), + NSLayoutConstraint(item: adView, + attribute: .height, + relatedBy: .equal, + toItem: nil, + attribute: .notAnAttribute, + multiplier: 1.0, + constant: kBannerHeight) + ]) + } + + override func viewDidDisappear(_ animated: Bool) + { + super.viewDidDisappear(animated) + + adView.adLoadDelegate = nil + adView.adDisplayDelegate = nil + adView.adEventDelegate = nil + } + + private func constraint(with adView: ALAdView, attribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint + { + return NSLayoutConstraint(item: adView, + attribute: attribute, + relatedBy: .equal, + toItem: view, + attribute: attribute, + multiplier: 1.0, + constant: 0.0) + } + + @IBAction func loadNextAd() + { + adView.loadNextAd() + + loadButton.isEnabled = false + } +} + +extension ALDemoBannerZoneViewController : ALAdLoadDelegate +{ + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + // Look at ALErrorCodes.h for list of error codes + logCallback() + + loadButton.isEnabled = true + } +} + +extension ALDemoBannerZoneViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + + loadButton.isEnabled = true + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoBannerZoneViewController : ALAdViewEventDelegate +{ + func ad(_ ad: ALAd, didPresentFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willLeaveApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didReturnToApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didFailToDisplayIn adView: ALAdView, withError code: ALAdViewDisplayErrorCode) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoInterfaceBuilderBannerViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoInterfaceBuilderBannerViewController.swift new file mode 100644 index 0000000000..871736cc41 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoInterfaceBuilderBannerViewController.swift @@ -0,0 +1,112 @@ +// +// ALDemoInterfaceBuilderBannerViewController.swift +// iOS-SDK-Demo-Swift +// +// Created by Thomas So on 3/6/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoInterfaceBuilderBannerViewController : ALBaseAdViewController +{ + @IBOutlet weak var adView: ALAdView! + @IBOutlet weak var loadButton: UIBarButtonItem! + + // MARK: View Lifecycle + + override func viewDidLoad() + { + super.viewDidLoad() + + adView.adLoadDelegate = self + adView.adDisplayDelegate = self + adView.adEventDelegate = self + } + + override func viewDidDisappear(_ animated: Bool) + { + super.viewDidDisappear(animated) + + adView.adLoadDelegate = nil + adView.adDisplayDelegate = nil + adView.adEventDelegate = nil + } + + @IBAction func loadNextAd() + { + adView.loadNextAd() + + loadButton.isEnabled = false + } +} + +extension ALDemoInterfaceBuilderBannerViewController : ALAdLoadDelegate +{ + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + // Look at ALErrorCodes.h for list of error codes + logCallback() + + loadButton.isEnabled = true + } +} + +extension ALDemoInterfaceBuilderBannerViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + + loadButton.isEnabled = true + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoInterfaceBuilderBannerViewController : ALAdViewEventDelegate +{ + func ad(_ ad: ALAd, didPresentFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willLeaveApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didReturnToApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didFailToDisplayIn adView: ALAdView, withError code: ALAdViewDisplayErrorCode) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoProgrammaticBannerViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoProgrammaticBannerViewController.swift new file mode 100644 index 0000000000..d2445a7ecd --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Banners/ALDemoProgrammaticBannerViewController.swift @@ -0,0 +1,145 @@ +// +// ALDemoProgrammaticBannerViewController.swift +// iOS-SDK-Demo-Swift +// +// Created by Thomas So on 3/6/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoProgrammaticBannerViewController : ALBaseAdViewController +{ + private let kBannerHeight: CGFloat = 50 + + private let adView = ALAdView(size: .banner) + @IBOutlet weak var loadButton: UIBarButtonItem! + + // MARK: View Lifecycle + + override func viewDidLoad() + { + super.viewDidLoad() + + // Optional: Implement the ad delegates to receive ad events. + adView.adLoadDelegate = self + adView.adDisplayDelegate = self + adView.adEventDelegate = self + adView.translatesAutoresizingMaskIntoConstraints = false + + // Call loadNextAd() to start showing ads + adView.loadNextAd() + + // Center the banner and anchor it to the bottom of the screen. + view.addSubview(adView) + view.addConstraints([ + constraint(with: adView, attribute: .leading), + constraint(with: adView, attribute: .trailing), + constraint(with: adView, attribute: .bottom), + NSLayoutConstraint(item: adView, + attribute: .height, + relatedBy: .equal, + toItem: nil, + attribute: .notAnAttribute, + multiplier: 1.0, + constant: kBannerHeight) + ]) + } + + override func viewDidDisappear(_ animated: Bool) + { + super.viewDidDisappear(animated) + + adView.adLoadDelegate = nil + adView.adDisplayDelegate = nil + adView.adEventDelegate = nil + } + + private func constraint(with adView: ALAdView, attribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint + { + return NSLayoutConstraint(item: adView, + attribute: attribute, + relatedBy: .equal, + toItem: view, + attribute: attribute, + multiplier: 1.0, + constant: 0.0) + } + + @IBAction func loadNextAd() + { + adView.loadNextAd() + + loadButton.isEnabled = false + } +} + +extension ALDemoProgrammaticBannerViewController : ALAdLoadDelegate +{ + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + // Look at ALErrorCodes.h for list of error codes + logCallback() + + loadButton.isEnabled = true + } +} + +extension ALDemoProgrammaticBannerViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + + loadButton.isEnabled = true + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoProgrammaticBannerViewController : ALAdViewEventDelegate +{ + func ad(_ ad: ALAd, didPresentFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willLeaveApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didReturnToApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didFailToDisplayIn adView: ALAdView, withError code: ALAdViewDisplayErrorCode) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Event Tracking/ALEventTrackingViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Event Tracking/ALEventTrackingViewController.swift new file mode 100644 index 0000000000..fdc9a071d5 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Event Tracking/ALEventTrackingViewController.swift @@ -0,0 +1,137 @@ +// +// ALEventTrackingViewController.swift +// iOS-SDK-Demo-Swift +// +// Created by Monica Ong on 6/5/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +import UIKit +import StoreKit +import AppLovinSDK + +struct ALDemoEvent +{ + var name: String + var purpose: String +} + +class ALEventTrackingViewController : UITableViewController +{ + private let events = [ALDemoEvent(name: "Began Checkout Event", purpose: "Track when user begins checkout procedure"), + ALDemoEvent(name: "Cart Event", purpose: "Track when user adds an item to cart"), + ALDemoEvent(name: "Completed Achievement Event", purpose: "Track when user completed an achievement"), + ALDemoEvent(name: "Completed Checkout Event", purpose: "Track when user completed checkout"), + ALDemoEvent(name: "Completed Level Event", purpose: "Track when user completed level"), + ALDemoEvent(name: "Created Reservation Event", purpose: "Track when user created a reservation"), + ALDemoEvent(name: "In-App Purchase Event", purpose: "Track when user makes an in-app purchase"), + ALDemoEvent(name: "Login Event", purpose: "Track when user logs in"), + ALDemoEvent(name: "Payment Info Event", purpose: "Tracks when user inputs their payment information"), + ALDemoEvent(name: "Registration Event", purpose: "Track when user registers"), + ALDemoEvent(name: "Search Event", purpose: "Track when user makes a search"), + ALDemoEvent(name: "Sent Invitation Event", purpose: "Track when user sends invitation"), + ALDemoEvent(name: "Shared Link Event", purpose: "Track when user shares a link"), + ALDemoEvent(name: "Spent Virtual Currency Event", purpose: "Track when users spends virtual currency"), + ALDemoEvent(name: "Tutorial Event", purpose: "Track when users does a tutorial"), + ALDemoEvent(name: "Viewed Content Event", purpose: "Track when user views content"), + ALDemoEvent(name: "Viewed Product Event", purpose: "Track when user views product"), + ALDemoEvent(name: "Wishlist Event", purpose: "Track when user adds an item to their wishlist")] + + // MARK: Table View Data Source + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int + { + return events.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + let cell = tableView.dequeueReusableCell(withIdentifier: "rootPrototype", for: indexPath) + cell.textLabel!.text = events[indexPath.row].name + cell.detailTextLabel?.text = events[indexPath.row].purpose + + return cell + } + + // MARK: Table View Delegate + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) + { + tableView.deselectRow(at: indexPath, animated: true) + + let eventService = ALSdk.shared()!.eventService + + switch indexPath.row + { + case 0: + eventService.trackEvent(kALEventTypeUserBeganCheckOut, + parameters: [kALEventParameterProductIdentifierKey : "PRODUCT SKU OR ID", + kALEventParameterRevenueAmountKey : "PRICE OF ITEM", + kALEventParameterRevenueCurrencyKey : "3-LETTER CURRENCY CODE"]) + case 1: + eventService.trackEvent(kALEventTypeUserAddedItemToCart, + parameters: [kALEventParameterProductIdentifierKey : "PRODUCT SKU OR ID"]) + + case 2: + eventService.trackEvent(kALEventTypeUserCompletedAchievement, + parameters: [kALEventParameterCompletedAchievementKey : "ACHIEVEMENT NAME OR ID"]) + + case 3: + eventService.trackEvent(kALEventTypeUserCompletedCheckOut, + parameters: [kALEventParameterCheckoutTransactionIdentifierKey : "UNIQUE TRANSACTION ID", + kALEventParameterProductIdentifierKey : "PRODUCT SKU OR ID", + kALEventParameterRevenueAmountKey : "AMOUNT OF MONEY SPENT", + kALEventParameterRevenueCurrencyKey : "3-LETTER CURRENCY CODE"]) + case 4: + eventService.trackEvent(kALEventTypeUserCompletedLevel, + parameters: [kALEventParameterCompletedLevelKey : "LEVEL NAME OR NUMBER"]) + case 5: + eventService.trackEvent(kALEventTypeUserCreatedReservation, + parameters: [kALEventParameterProductIdentifierKey : "PRODUCT SKU OR ID", + kALEventParameterReservationStartDateKey : "START NSDATE", + kALEventParameterReservationEndDateKey : "END NSDATE"]) + case 6: + //In-App Purchases + // let transaction: SKPaymentTransaction = ... // from paymentQueue:updatedTransactions: + //let product: SKProduct = ... // Appropriate product (matching productIdentifier property to SKPaymentTransaction) + eventService.trackInAppPurchase(withTransactionIdentifier: "transaction.transactionIdentifier", + parameters: [kALEventParameterRevenueAmountKey : "AMOUNT OF MONEY SPENT", + kALEventParameterRevenueCurrencyKey : "3-LETTER CURRENCY CODE", + kALEventParameterProductIdentifierKey : "product.productIdentifier"]) //product.productIdentifier + case 7: + eventService.trackEvent(kALEventTypeUserLoggedIn, + parameters: [kALEventParameterUserAccountIdentifierKey : "USERNAME"]) + case 8: + eventService.trackEvent(kALEventTypeUserProvidedPaymentInformation) + + case 9: + eventService.trackEvent(kALEventTypeUserCreatedAccount, + parameters: [kALEventParameterUserAccountIdentifierKey : "USERNAME"]) + + case 10: + eventService.trackEvent(kALEventTypeUserExecutedSearch, + parameters: [kALEventParameterSearchQueryKey : "USER'S SEARCH STRING"]) + case 11: + eventService.trackEvent(kALEventTypeUserSentInvitation) + case 12: + eventService.trackEvent(kALEventTypeUserSharedLink) + case 13: + eventService.trackEvent(kALEventTypeUserSpentVirtualCurrency, + parameters: [kALEventParameterVirtualCurrencyAmountKey : "NUMBER OF COINS SPENT", + kALEventParameterVirtualCurrencyNameKey : "CURRENCY NAME"]) + case 14: + eventService.trackEvent(kALEventTypeUserCompletedTutorial) + case 15: + eventService.trackEvent(kALEventTypeUserViewedContent, + parameters: [kALEventParameterContentIdentifierKey : "SOME ID DESCRIBING CONTENT"]) + case 16: + eventService.trackEvent(kALEventTypeUserViewedProduct, + parameters: [kALEventParameterProductIdentifierKey : "PRODUCT SKU OR ID"]) + case 17: + eventService.trackEvent(kALEventTypeUserAddedItemToWishlist, + parameters: [kALEventParameterProductIdentifierKey : "PRODUCT SKU OR ID"]) + default: + title = "Default event tracking initiated" + } + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialBasicIntegrationViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialBasicIntegrationViewController.swift new file mode 100644 index 0000000000..7f96e8c710 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialBasicIntegrationViewController.swift @@ -0,0 +1,86 @@ +// +// ALDemoInterstitialSingleInstanceViewController.swift +// iOS-SDK-Demo +// +// Created by Thomas So on 9/25/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoInterstitialBasicIntegrationViewController : ALBaseAdViewController +{ +// @IBOutlet weak var showButton: UIBarButtonItem! + + @IBOutlet weak var showButton: UIButton! + + private let interstitialAd = ALInterstitialAd.shared() + + // MARK: View Lifecycle + + override func viewDidLoad() + { + super.viewDidLoad() + + interstitialAd.adLoadDelegate = self + interstitialAd.adDisplayDelegate = self + interstitialAd.adVideoPlaybackDelegate = self + } + + // MARK: IB Action Methods + + @IBAction func showInterstitial(_ sender: AnyObject!) + { + self.showButton.isEnabled = false + + logCallback() + interstitialAd.show() + } +} + +extension ALDemoInterstitialBasicIntegrationViewController : ALAdLoadDelegate +{ + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + self.showButton.isEnabled = true + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + logCallback() + self.showButton.isEnabled = true + } +} + +extension ALDemoInterstitialBasicIntegrationViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoInterstitialBasicIntegrationViewController : ALAdVideoPlaybackDelegate +{ + func videoPlaybackBegan(in ad: ALAd) + { + logCallback() + } + + func videoPlaybackEnded(in ad: ALAd, atPlaybackPercent percentPlayed: NSNumber, fullyWatched wasFullyWatched: Bool) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialManualLoadingViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialManualLoadingViewController.swift new file mode 100644 index 0000000000..7d5d76ca60 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialManualLoadingViewController.swift @@ -0,0 +1,87 @@ +// +// ALDemoInterstitialManualLoadingViewController.swift +// iOS-SDK-Demo +// +// Created by Thomas So on 9/25/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoInterstitialManualLoadingViewController : ALBaseAdViewController +{ + @IBOutlet weak var showButton: UIBarButtonItem! + +//revisit + private var ad: ALAd? + private let interstitialAd = ALInterstitialAd.shared() + + @IBAction func loadInterstitial(_ sender: AnyObject!) + { + logCallback() + ALSdk.shared()!.adService.loadNextAd(ALAdSize.interstitial, andNotify: self) + } + + @IBAction func showInterstitial(_ sender: AnyObject!) + { + if let ad = self.ad + { + // Optional: Assign delegates + interstitialAd.adDisplayDelegate = self + interstitialAd.adVideoPlaybackDelegate = self + + interstitialAd.show(ad) + + logCallback() + } + } +} + +extension ALDemoInterstitialManualLoadingViewController : ALAdLoadDelegate +{ + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + + self.ad = ad + self.showButton.isEnabled = true + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + // Look at ALErrorCodes.h for list of error codes + logCallback() + } +} + +extension ALDemoInterstitialManualLoadingViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoInterstitialManualLoadingViewController : ALAdVideoPlaybackDelegate +{ + func videoPlaybackBegan(in ad: ALAd) + { + logCallback() + } + + func videoPlaybackEnded(in ad: ALAd, atPlaybackPercent percentPlayed: NSNumber, fullyWatched wasFullyWatched: Bool) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialZoneViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialZoneViewController.swift new file mode 100644 index 0000000000..9df8ef9ed6 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Interstitials/ALDemoInterstitialZoneViewController.swift @@ -0,0 +1,86 @@ +// +// ALDemoInterstitialZoneViewController.swift +// iOS-SDK-Demo-Swift +// +// Created by Suyash Saxena on 6/19/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoInterstitialZoneViewController : ALBaseAdViewController +{ + @IBOutlet weak var showButton: UIBarButtonItem! + + var ad: ALAd? + private let interstitialAd = ALInterstitialAd.shared() + + @IBAction func loadInterstitial(_ sender: AnyObject!) + { + logCallback() + ALSdk.shared()?.adService.loadNextAd(forZoneIdentifier: "YOUR_ZONE_ID", andNotify: self) + } + + @IBAction func showInterstitial(_ sender: AnyObject!) + { + if let ad = self.ad + { + // Optional: Assign delegates + interstitialAd.adDisplayDelegate = self + interstitialAd.adVideoPlaybackDelegate = self + + interstitialAd.show(ad) + + logCallback() + } + } +} + +extension ALDemoInterstitialZoneViewController : ALAdLoadDelegate +{ + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + + self.ad = ad + self.showButton.isEnabled = true + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + // Look at ALErrorCodes.h for list of error codes + logCallback() + } +} + +extension ALDemoInterstitialZoneViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoInterstitialZoneViewController : ALAdVideoPlaybackDelegate +{ + func videoPlaybackBegan(in ad: ALAd) + { + logCallback() + } + + func videoPlaybackEnded(in ad: ALAd, atPlaybackPercent percentPlayed: NSNumber, fullyWatched wasFullyWatched: Bool) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Leaders/ALDemoInterfaceBuilderLeaderViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Leaders/ALDemoInterfaceBuilderLeaderViewController.swift new file mode 100644 index 0000000000..cdbb0d2dc1 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Leaders/ALDemoInterfaceBuilderLeaderViewController.swift @@ -0,0 +1,112 @@ +// +// ALDemoInterfaceBuilderLeaderViewController.swift +// iOS-SDK-Demo-Swift +// +// Created by Santosh Bagadi on 4/5/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoInterfaceBuilderLeaderViewController : ALBaseAdViewController +{ + @IBOutlet weak var adView: ALAdView! + @IBOutlet weak var loadButton: UIBarButtonItem! + + // MARK: View Lifecycle + + override func viewDidLoad() + { + super.viewDidLoad() + + adView.adLoadDelegate = self + adView.adDisplayDelegate = self + adView.adEventDelegate = self + } + + override func viewDidDisappear(_ animated: Bool) + { + super.viewDidDisappear(animated) + + adView.adLoadDelegate = nil + adView.adDisplayDelegate = nil + adView.adEventDelegate = nil + } + + @IBAction func loadNextAd() + { + adView.loadNextAd() + + loadButton.isEnabled = false + } +} + +extension ALDemoInterfaceBuilderLeaderViewController : ALAdLoadDelegate +{ + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + // Look at ALErrorCodes.h for list of error codes + logCallback() + + loadButton.isEnabled = true + } +} + +extension ALDemoInterfaceBuilderLeaderViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + + loadButton.isEnabled = true + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoInterfaceBuilderLeaderViewController : ALAdViewEventDelegate +{ + func ad(_ ad: ALAd, didPresentFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willLeaveApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didReturnToApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didFailToDisplayIn adView: ALAdView, withError code: ALAdViewDisplayErrorCode) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Leaders/ALDemoProgrammaticLeaderViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Leaders/ALDemoProgrammaticLeaderViewController.swift new file mode 100644 index 0000000000..f632d23eef --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Leaders/ALDemoProgrammaticLeaderViewController.swift @@ -0,0 +1,144 @@ +// +// ALDemoProgrammaticLeaderViewController.swift +// iOS-SDK-Demo-Swift +// +// Created by Santosh Bagadi on 4/5/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoProgrammaticLeaderViewController : ALBaseAdViewController +{ + private let kLeaderHeight: CGFloat = 90 + + private let adView = ALAdView(size: .leader) + @IBOutlet weak var loadButton: UIBarButtonItem! + + // MARK: View Lifecycle + + override func viewDidLoad() + { + super.viewDidLoad() + + // Optional: Implement the ad delegates to receive ad events. + adView.adLoadDelegate = self + adView.adDisplayDelegate = self + adView.adEventDelegate = self + adView.translatesAutoresizingMaskIntoConstraints = false + + // Call loadNextAd() to start showing ads + adView.loadNextAd() + + // Center the banner and anchor it to the bottom of the screen. + view.addSubview(adView) + view.addConstraints([ + constraint(with: adView, attribute: .leading), + constraint(with: adView, attribute: .trailing), + constraint(with: adView, attribute: .bottom), + NSLayoutConstraint(item: adView, + attribute: .height, + relatedBy: .equal, + toItem: nil, + attribute: .notAnAttribute, + multiplier: 1.0, + constant: kLeaderHeight) + ]) + } + + override func viewDidDisappear(_ animated: Bool) + { + super.viewDidDisappear(animated) + + adView.adLoadDelegate = nil + adView.adDisplayDelegate = nil + adView.adEventDelegate = nil + } + + private func constraint(with adView: ALAdView, attribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint + { + return NSLayoutConstraint(item: adView, + attribute: attribute, + relatedBy: .equal, + toItem: view, + attribute: attribute, + multiplier: 1.0, + constant: 0.0) + } + + @IBAction func loadNextAd() + { + adView.loadNextAd() + + loadButton.isEnabled = false + } +} + +extension ALDemoProgrammaticLeaderViewController : ALAdLoadDelegate +{ + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + // Look at ALErrorCodes.h for list of error codes + logCallback() + loadButton.isEnabled = true + } +} + +extension ALDemoProgrammaticLeaderViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + + loadButton.isEnabled = true + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoProgrammaticLeaderViewController : ALAdViewEventDelegate +{ + func ad(_ ad: ALAd, didPresentFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willLeaveApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didReturnToApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didFailToDisplayIn adView: ALAdView, withError code: ALAdViewDisplayErrorCode) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Mrecs/ALDemoInterfaceBuilderMRECViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Mrecs/ALDemoInterfaceBuilderMRECViewController.swift new file mode 100644 index 0000000000..23290b9cd6 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Mrecs/ALDemoInterfaceBuilderMRECViewController.swift @@ -0,0 +1,108 @@ +// +// ALDemoInterfaceBuilderMRECViewController.swift +// AppLovin MAX Demo App - Swift +// +// Created by Varsha Hanji on 4/6/20. +// Copyright © 2020 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoInterfaceBuilderMRECViewController : ALBaseAdViewController +{ + + @IBOutlet weak var adView: ALAdView! + + + // MARK: View Lifecycle + + override func viewDidAppear(_ animated: Bool) + { + super.viewDidAppear(animated) + + adView.adLoadDelegate = self + adView.adDisplayDelegate = self + adView.adEventDelegate = self + + adView.adSize = ALAdSize.mrec + + // Call loadNextAd() to start showing ads + adView.loadNextAd() + + } + + override func viewDidDisappear(_ animated: Bool) + { + super.viewDidDisappear(animated) + + adView.adLoadDelegate = nil + adView.adDisplayDelegate = nil + adView.adEventDelegate = nil + } +} + +extension ALDemoInterfaceBuilderMRECViewController : ALAdLoadDelegate +{ + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + // Look at ALErrorCodes.h for list of error codes + logCallback() + } +} + +extension ALDemoInterfaceBuilderMRECViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoInterfaceBuilderMRECViewController : ALAdViewEventDelegate +{ + func ad(_ ad: ALAd, didPresentFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willLeaveApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didReturnToApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didFailToDisplayIn adView: ALAdView, withError code: ALAdViewDisplayErrorCode) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Mrecs/ALDemoProgrammaticMRecViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Mrecs/ALDemoProgrammaticMRecViewController.swift new file mode 100644 index 0000000000..54783f94a7 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Mrecs/ALDemoProgrammaticMRecViewController.swift @@ -0,0 +1,116 @@ +// +// ALDemoProgrammaticMRecViewController.swift +// iOS-SDK-Demo-Swift +// +// Created by Thomas So on 3/6/17. +// Copyright © 2017 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoProgrammaticMRecViewController : ALBaseAdViewController +{ + private let kMRecHeight: CGFloat = 250 + private let kMRecWidth: CGFloat = 300 + + private let adView = ALAdView(size: .mrec) + + // MARK: View Lifecycle + + override func viewDidAppear(_ animated: Bool) + { + super.viewDidAppear(animated) + + // Optional: Implement the ad delegates to receive ad events. + adView.adLoadDelegate = self + adView.adDisplayDelegate = self + adView.adEventDelegate = self + adView.translatesAutoresizingMaskIntoConstraints = false + + // Call loadNextAd() to start showing ads + adView.loadNextAd() + + view.addSubview(adView) + view.addConstraints([ + NSLayoutConstraint(item: adView, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0.0), + NSLayoutConstraint(item: adView, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1.0, constant: 0.0), + NSLayoutConstraint(item: adView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: kMRecHeight), + NSLayoutConstraint(item: adView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: kMRecWidth) + ]) + } + + override func viewDidDisappear(_ animated: Bool) + { + super.viewDidDisappear(animated) + + adView.adLoadDelegate = nil + adView.adDisplayDelegate = nil + adView.adEventDelegate = nil + } +} + +extension ALDemoProgrammaticMRecViewController : ALAdLoadDelegate +{ + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + // Look at ALErrorCodes.h for list of error codes + logCallback() + } +} + +extension ALDemoProgrammaticMRecViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoProgrammaticMRecViewController : ALAdViewEventDelegate +{ + func ad(_ ad: ALAd, didPresentFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didDismissFullscreenFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, willLeaveApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didReturnToApplicationFor adView: ALAdView) + { + logCallback() + } + + func ad(_ ad: ALAd, didFailToDisplayIn adView: ALAdView, withError code: ALAdViewDisplayErrorCode) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /ALDemoNativeAdFeedTableViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /ALDemoNativeAdFeedTableViewController.swift new file mode 100644 index 0000000000..6f38bc1ca9 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /ALDemoNativeAdFeedTableViewController.swift @@ -0,0 +1,94 @@ +// +// ALDemoNativeAdFeedTableViewController.swift +// iOS-SDK-Demo +// +// Created by Thomas So on 9/25/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +import UIKit + +class ALDemoNativeAdFeedTableViewController : UITableViewController +{ + let kArticleCellIdentifier = "articleCell" + let kAdCellIdentifier = "adCell" + + let kCellTagTitleLabel = 2 + let kCellTagSubtitleLabel = 3 + let kCellTagDescriptionLabel = 4 + + var articles = [ALDemoArticle]() + + // MARK: View Lifecycle + + override func viewDidLoad() + { + super.viewDidLoad() + + ALDemoRSSFeedRetriever.shared().startParsing(completion: { (error: Error?, articles: [ALDemoArticle]!) in + + DispatchQueue.main.async { + + guard error == nil && articles.count > 0 else { + + let alert = UIAlertController(title: "ERROR", message: error?.localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action: UIAlertAction) -> Void in + self.navigationController?.popViewController(animated: true) + })) + self.present(alert, animated: true, completion: nil) + + return + } + + self.articles = articles + self.tableView.reloadData() + } + }) + } + + // MARK: Table View Data Source + + override func numberOfSections(in tableView: UITableView) -> Int + { + return 1; + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int + { + return articles.count + } + + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat + { + return articles[indexPath.row].isAd ? 360 : 280 + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell + { + var cell: UITableViewCell! + let article = articles[indexPath.row] + + if article.isAd + { + // You can configure carousels in ALCarouselViewSettings.h + cell = tableView.dequeueReusableCell(withIdentifier: kAdCellIdentifier, for: indexPath) + } + else + { + cell = tableView.dequeueReusableCell(withIdentifier: kArticleCellIdentifier, for: indexPath) + (cell.viewWithTag(kCellTagTitleLabel) as! UILabel).text = article.title + (cell.viewWithTag(kCellTagSubtitleLabel) as! UILabel).text = article.creator + " - " + article.pubDate + (cell.viewWithTag(kCellTagDescriptionLabel) as! UILabel).text = article.articleDescription + } + + return cell + } + + // MARK: Table View Delegate + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) + { + tableView.deselectRow(at: indexPath, animated: true) + UIApplication.shared.openURL(articles[indexPath.row].link) + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselCardState.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselCardState.h new file mode 100644 index 0000000000..3c6061c88e --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselCardState.h @@ -0,0 +1,52 @@ +// +// ALCarouselCardState.h +// sdk +// +// Created by Matt Szaro on 4/17/15. +// +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Tracks the state of a card within an ALCarouselView. + */ +@interface ALCarouselCardState : NSObject + +/** + Retrieve an instance with appropriate default settings for use within a carousel view. + */ ++(instancetype) cardStateForCarousel; + +/** + Retrieve an instance with appropriate default settings for use within a single ALCarouselCardView outside a carousel. + */ ++(instancetype) cardStateForSingleCard; + +typedef NS_ENUM(NSUInteger, ALMuteState) +{ + ALMuteStateUnspecified, + ALMuteStateUnmuted, + ALMuteStateMuted +}; + +@property (assign, atomic, getter=wasVideoStarted) BOOL videoStarted; +@property (assign, atomic, getter=wasVideoCompleted) BOOL videoCompleted; +@property (assign, atomic, getter=wasVideoStartTracked) BOOL videoStartTracked; +@property (assign, atomic, getter=isFirstPlayback) BOOL firstPlayback; +@property (assign, atomic, getter=wasImpressionTracked) BOOL impressionTracked; +@property (assign, atomic, getter=isCurrentlyActive) BOOL currentlyActive; +@property (assign, atomic, getter=isReplayOverlayVisible) BOOL replayOverlayVisible; +@property (assign, atomic, getter=isPrecaching) BOOL precaching; // To prvent a slot from being redundantly pre-caching + +@property (assign, atomic) Float64 lastMediaPlayerPosition; +@property (assign, atomic) ALMuteState muteState; +@property (strong, atomic) UIImage *screenshot; +@property (assign, atomic) CGRect videoRect; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselCardState.m b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselCardState.m new file mode 100644 index 0000000000..d8350f8831 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselCardState.m @@ -0,0 +1,34 @@ +// +// ALCarouselCardState.m +// sdk +// +// Created by Matt Szaro on 4/17/15. +// +// + +#import "ALCarouselCardState.h" + +@implementation ALCarouselCardState + ++(instancetype) cardStateForCarousel +{ + ALCarouselCardState* state = [[[self class] alloc] init]; + + state.muteState = ALMuteStateUnspecified; + state.firstPlayback = YES; + + return state; +} + ++(instancetype) cardStateForSingleCard +{ + ALCarouselCardState* state = [[[self class] alloc] init]; + + state.muteState = ALMuteStateUnspecified; + state.currentlyActive = YES; + state.firstPlayback = YES; + + return state; +} + +@end \ No newline at end of file diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselRenderingProtocol.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselRenderingProtocol.h new file mode 100644 index 0000000000..30454997ed --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselRenderingProtocol.h @@ -0,0 +1,26 @@ +// +// ALCarouselRenderingProtocol.h +// sdk +// +// Created by Matt Szaro on 5/7/15. +// +// + +@import AppLovinSDK; +#import +#import "ALCarouselCardState.h" + +@protocol ALCarouselRenderingProtocol + +@optional +- (void)renderViewForNativeAd:(ALNativeAd *)ad; + +@required +- (void)renderViewForNativeAd:(ALNativeAd *)ad cardState:(ALCarouselCardState *)cardState; + +/** + * Resets the current card's view properties. + */ +- (void)clearView; + +@end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselViewModel.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselViewModel.h new file mode 100644 index 0000000000..4c51732b59 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselViewModel.h @@ -0,0 +1,32 @@ +// +// ALCarouselModel.h +// sdk +// +// Created by Matt Szaro on 4/20/15. +// +// + +#import +#import + +@class ALNativeAd; +@class ALCarouselCardState; +@class ALSdk; + +NS_ASSUME_NONNULL_BEGIN + +@interface ALCarouselViewModel : NSObject + +@property (strong, nonatomic, readonly) NSArray *nativeAds; +@property (assign, nonatomic, readonly) NSUInteger nativeAdsCount; + +- (instancetype)initWithNativeAds:(NSArray *)ads; +- (nullable ALCarouselCardState *)cardStateForNativeAd:(ALNativeAd *)ad; +- (nullable ALCarouselCardState *)cardStateAtNativeAdIndex:(NSUInteger)index; +- (nullable ALNativeAd *)nativeAdAtIndex:(NSUInteger)index; + +- (void)removeAllObjects; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselViewModel.m b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselViewModel.m new file mode 100644 index 0000000000..52c2a90efb --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALCarouselViewModel.m @@ -0,0 +1,175 @@ +// +// ALCarouselModel.m +// sdk +// +// Created by Matt Szaro on 4/20/15. +// +// + +#import "ALCarouselViewModel.h" +#import "ALCarouselCardState.h" +#import "ALDebugLog.h" + +@interface ALCarouselViewModel () + +@property (strong, nonatomic) ALSdk* sdk; + +@property (strong, nonatomic) NSArray* nativeAds; +@property (strong, nonatomic) NSMutableDictionary* cardStates; + +@end + +@implementation ALCarouselViewModel +@dynamic nativeAdsCount; + +static void* ALCarouselViewModelKVOContext = &ALCarouselViewModelKVOContext; +static NSString* kKeypathMuteState = @"muteState"; + +-(instancetype) initWithNativeAds: (NSArray *)ads +{ + self = [super init]; + if ( self ) + { + self.nativeAds = ads; + self.cardStates = [NSMutableDictionary dictionary]; + } + return self; +} + +- (ALCarouselCardState *)cardStateAtNativeAdIndex:(NSUInteger)index +{ + if ( index >= [self nativeAdsCount]) + { + ALLog(@"Requested card state at native ad index %lu is out-of-bounds", index); + return nil; + } + else + { + ALCarouselCardState* cardState = self.cardStates[ @(index) ]; + if ( cardState ) + { + ALLog(@"Requested card state at native ad index %lu", index); + return cardState; + } + else + { + ALLog(@"Requested card state at native ad index %lu does not exist yet. Creating new card state", index); + + ALCarouselCardState* newState = [ALCarouselCardState cardStateForCarousel]; + [self beginObservingCardState: newState]; + [self.cardStates setObject: newState forKey: @(index)]; + + return newState; + } + } +} + +-(void) beginObservingCardState: (ALCarouselCardState*) cardState +{ + // Observe any properties which need to be synchronized among all card states. + // E.g., propogate mute settings among cards while within a carousel view. + // This allows us to add syncronization features on top of the card state without adding complexity for single card integrations. + + [cardState addObserver: self + forKeyPath: kKeypathMuteState + options: NSKeyValueObservingOptionNew + context: ALCarouselViewModelKVOContext]; +} + +-(void) endObservingCardState: (ALCarouselCardState*) cardState +{ + // Observe any properties which need to be synchronized among all card states. + // E.g., propogate mute settings among cards while within a carousel view. + // This allows us to add syncronization features on top of the card state without adding complexity for single card integrations. + + @try { + [cardState removeObserver: self + forKeyPath: kKeypathMuteState + context: ALCarouselViewModelKVOContext]; + } + @catch (NSException* __unused ignore) + { + } +} + +-(void) observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context +{ + if (context == ALCarouselViewModelKVOContext) + { + if ([keyPath isEqual: kKeypathMuteState]) + { + NSNumber* newValue = change[NSKeyValueChangeNewKey]; + ALMuteState muteState = (ALMuteState) [newValue unsignedIntegerValue]; + + if (muteState != ALMuteStateUnspecified) + { + [self updateMuteStates: muteState]; + } + } + } + else + { + [super observeValueForKeyPath: keyPath ofObject: object change: change context: context]; + } +} + +-(void) updateMuteStates: (ALMuteState) newState +{ + [self.cardStates enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + ALCarouselCardState* cardState = (ALCarouselCardState*) obj; + [self endObservingCardState: cardState]; + cardState.muteState = newState; + [self beginObservingCardState: cardState]; + }]; +} + +- (ALCarouselCardState *)cardStateForNativeAd:(ALNativeAd *)ad +{ + NSUInteger index = [self.nativeAds indexOfObject: ad]; + if ( index != NSNotFound ) + { + return [self cardStateAtNativeAdIndex: index]; + } + else + { + return nil; + } +} + +- (ALNativeAd *)nativeAdAtIndex:(NSUInteger)index +{ + if ( index < self.nativeAds.count ) + { + ALLog(@"Requested native ad index %lu", index); + return [self.nativeAds objectAtIndex: index]; + } + else + { + ALLog(@"Requested native ad index %lu is out of bounds", index); + return nil; + } +} + +- (NSUInteger)nativeAdsCount +{ + return self.nativeAds.count; +} + +-(void) removeAllObjects; +{ + self.nativeAds = nil; + + [self.cardStates enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + ALCarouselCardState* cardState = (ALCarouselCardState*) obj; + [self endObservingCardState: cardState]; + }]; + + [self.cardStates removeAllObjects]; +} + +-(void) dealloc +{ + [self removeAllObjects]; +} + +@end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALDebugLog.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALDebugLog.h new file mode 100644 index 0000000000..9842559100 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALDebugLog.h @@ -0,0 +1,18 @@ +// +// ALDebugLog.h +// iOS Test App NG +// +// Created by Matt Szaro on 5/29/15. +// Copyright (c) 2015 AppLovin. All rights reserved. +// + +#ifndef iOS_Test_App_NG_ALDebugLog_h +#define iOS_Test_App_NG_ALDebugLog_h + +#ifdef DEBUG + #define ALLog(str, ...) NSLog(str, ##__VA_ARGS__) +#else + #define ALLog(str, ...) +#endif + +#endif diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALNativeAdVideoPlayer.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALNativeAdVideoPlayer.h new file mode 100644 index 0000000000..c48d014873 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALNativeAdVideoPlayer.h @@ -0,0 +1,30 @@ +// +// ALVideoPlayer.h +// sdk +// +// Created by Matt Szaro on 6/23/14. +// +// + +@import AppLovinSDK; +@import AVFoundation; +@import CoreMedia; +#import "ALNativeAdVideoView.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ALNativeAdVideoPlayer : NSObject + +@property (strong, nonatomic, readonly) ALNativeAdVideoView* videoView; +@property (strong, nonatomic, readonly) AVPlayerItem* playerItem; +@property (strong, nonatomic, readonly) AVAsset* playerAsset; +@property (strong, nonatomic, readwrite) NSURL* mediaSource; + +-(instancetype) initWithMediaSource: (nullable NSURL*) aMediaSource; + +-(void) playVideo; +-(void) stopVideo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALNativeAdVideoPlayer.m b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALNativeAdVideoPlayer.m new file mode 100644 index 0000000000..c52389bc76 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/ALNativeAdVideoPlayer.m @@ -0,0 +1,67 @@ +// +// ALVideoPlayer.m +// sdk +// +// Created by Matt Szaro on 6/23/14. +// +// + +#import "ALNativeAdVideoPlayer.h" + +@interface ALNativeAdVideoPlayer() + +@property (strong, nonatomic, readwrite) ALNativeAdVideoView* videoView; +@property (strong, nonatomic, readwrite) AVPlayerItem* playerItem; +@property (strong, nonatomic, readwrite) AVAsset* playerAsset; +@property (strong, nonatomic, readwrite) AVPlayer* player; + +@end + +@implementation ALNativeAdVideoPlayer + +-(instancetype) initWithMediaSource:(NSURL *)aMediaSource +{ + self = [super init]; + if(self) + { + self.mediaSource = aMediaSource; + self.videoView = [self createVideoView]; + } + return self; +} + +-(ALNativeAdVideoView*) createVideoView +{ + self.playerAsset = [AVAsset assetWithURL: self.mediaSource]; + self.playerItem = [AVPlayerItem playerItemWithAsset: self.playerAsset]; + self.player = [AVPlayer playerWithPlayerItem: self.playerItem]; + + ALNativeAdVideoView* videoView = [[ALNativeAdVideoView alloc] initWithPlayer: self.player]; + videoView.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; + + return videoView; +} + +-(void) playVideo +{ + [self.videoView.player play]; +} + +-(void) stopVideo +{ + [self.videoView.player pause]; +} + +-(void) setMediaSource:(NSURL *)mediaSource +{ + if (![_mediaSource isEqual: mediaSource]) { + + _mediaSource = mediaSource; + + self.playerAsset = [AVAsset assetWithURL: self.mediaSource]; + self.playerItem = [AVPlayerItem playerItemWithAsset: self.playerAsset]; + + [self.player replaceCurrentItemWithPlayerItem: self.playerItem]; + } +} +@end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_0.5.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_0.5.png new file mode 100644 index 0000000000000000000000000000000000000000..2e0f5341136a11ec50e5b5521e1dcb5ae0f20bb3 GIT binary patch literal 3806 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 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_0.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_0.png new file mode 100644 index 0000000000000000000000000000000000000000..9283ab8508ffa14fe7cda1f439268ada3c714271 GIT binary patch literal 3797 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 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_1.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_1.png new file mode 100644 index 0000000000000000000000000000000000000000..e14864d3cc55b5dd3485856bb71b032d8ce24829 GIT binary patch literal 3738 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 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_3.5.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_3.5.png new file mode 100644 index 0000000000000000000000000000000000000000..b96d07a0534cc80dab0cb9b37479244b4f0fca6c GIT binary patch literal 3810 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 AX8-^I literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_3.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_3.png new file mode 100644 index 0000000000000000000000000000000000000000..7ff58caeec1bf31eaee58e321edf5a2f37fe6ca9 GIT binary patch literal 3808 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 D?_8?D literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_4.5.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_4.5.png new file mode 100644 index 0000000000000000000000000000000000000000..8023c19deeeed82610f1fcca4144bc4d0f9c7368 GIT binary patch literal 3749 zcmbVPXIK+yyGAL}rA0s~AqWvPq|lN`s0k1uQ3xU+C~1UHl8}TJ78HadO+eAbV5A6A zR+JTVMbQi8qL+s1W>q;k zISsrw)?ZF;9Y{7uDl5tE!I{!k*y82nfZN42MwYFf!a05kaL<$v=4h4Ucs9 zfZMq_*uy+M;BYw39)t80cnr=fyJ|Ud@_ql|Dl^H)BPQ5_g}GS4=$a| zXLAGC?3f=_uqTquXY(T29H58Cx2yxV?PIcN>_nc`TAKd$8cXLg6X-NgE;| zzt}*xcR)DU+0r2>B+3o~ca-^$BvTL&Dw&3|cc4;iX(&g~cRcO?8$YPbDCkwdp^tAY1&?Omvp4gSfI*$*EB%$jPbR$79_BlAgUe%gooq={<~Q;MN@j9?b?` z3*C$=+24MI6rQN5UgAB_p^tqz-&Rpq9>AJ70*d+b!FDyQB8B3rOMVK-cP)CEz1`q= zMTb?zCF$cvDg%L$jFA#Q_#8-J)YPV=tuzW&ez|eumx`L3y4sQW*>bG~$cudB)hqEw z8Q|&i-8d88m&}~4mh(x91NwW|F)cs0p~?kSMI46$R*pIsPna~)(IFN~V~(y4JDk>S080Zu9t!JkIJe#p z^fvx-CB;&5pd>R>5`dXB>nEY3ef`_BGmblWh8p2U0pRR~w_{_|- ztc;>;LnWeQG5;u59HXyKPCax}-Y=Rm2Jfczlhp(MX%| z>7`ON711qTj0Ck+WI3JO`j9))ei*1AJ_UIB!+p3)k#=L5mJPkTg# zD>gMJ4J{xye)?kkfz;@=vYRM^6rHjFfB9nrh**W(yZzeD zpoyn#ZqA1udfY25oLS%Kx(K+_b>(&aKGN#Z3Fk(?a*q{EOgB5=@PZOi)Lh@_`ukO- z*UPHXM8M->{i*JO@kE7lS94E}73T*x0$=Y^#=26`3`=N)3q@CT4!D~38|p2f^7q^rK~pdr_m(X= zS2y-0O0SrSE?hnwSbgh|(r248`%GS{_hP6=!_?Y^>lhnvas`9UUdoI7 zG%rU&_vM01-HYT$7E#B}J7;~exIgjSdR^YQRE>jE=*<}@@5|(wa7|R&RAS%P+D&BQ zke^8|M!@DiKQT}!x>&Cnzj$z#O}0!Nh^>m5%zn<&%~x`++xFaQ*1S$!on~LS;_8LT zKQY$0ZK`qLs9!m*VW+iQENV8{)vtGakJvDI>B!+Soa$?pmkCIDOnhC{o&>Uekv{kG ztId5arns# z_}TiwC&kNUqpOd*9~|%uJa~tn2#T9C0?>!&cOlfPAU>^`JtZ+`tIoZ#ju4p7t9F$x z;0G2&>jlo@U~ydTG)m33l4P{Bv7vCgzn9sWv&0qhZ0)VIPO-U3pSJQ@T8hOwlXeHG zQw((h(GsR^d=qd2dEwVWnlq#KYRB?f8fN^}qwDD{Qk%u@&1xL~ ziNG2Eb>KlJiyD!%$z(coKj<{?lmc-Za?ZPtMB^Yc1U3#L+gk;>SNv%je6reKecP)K zz_QhqidWhVHT|Ou4#M&7T`D3maLRC! zyMtd21$(7S8{~%GKe1B}O;||&^m~4gaeWGM-Me<>R!{DkYwFD>xdo!JoG)-#+tXuJ zWq^+BA&4o8He#b9TT%qme38op{fpK;9dl^!6$*z>5a; zoHFwBKf}B#SKk`s8Nb|;9j;?_VB-r5JssU%5?&dprg9sV5FoMDUQhg^^g*!diNf~9 zN&?dD7rU$0z9}hNYEwV>W%J~`Fndcw;@5^VItH_m)PkwwoqA2nN}#g-a>k}}o2Mdi zNRnBmaam_I z4WtmFtt7xf)`MjO*vrV&PEo3&-P!Q0e5DhpN~_UO*XyS)f`bmNi`T2sz66#XYKo$c zkO8k~L<9tW)H*I~*K4{^-u|lLd@o7(AMZ8^yXdrpR z&5HE867k}u{~5eHsROA$^lBk!-jx=Hr!3tY-;vu~u$9=?oCPGF-t@vVNAR0Yht-AA zXJM*ouctvyF6Ez{Hqo$G=?Wm19)JOHBj)vd%RT7nrcc7W^O}MFgOa3_cWm&Kuc;{B z{*|aQrwrdN&>XB*5o}%#C%(~r@>5#Bm!)#Iy2f$s?3*=|o!R#He)0+VGYfp2{FBGk z7tBk;8Lh=Zg{?8fNzq(!Gd8Z**hZvQv;e-3B4tQVK6P67#cBt52m+auA9BR~JO)fx zempW{@=VyM6WxBJ*kaI3o^cm6UPd~LgOacYIfX zhsF<-5$>>!!?FoiC!IFrbCk7rd(9e!+99OJujPb{o*gC`1etXBzE~k zYD&R59VL|LGQZ)wq zr01Ek)tvd(h1s+7MHJJVePYx1AAJns3gRX*`+II#96-flf*(Cg(v*sF0cK(1G+EKs zyJE%=Jsur5^4q_@jhMNj-T(`&@RofX=wH#>c(d?*XJ{_?QWs;SWS?SI)>i@d#iJ{` znC||HFV9aQ^m~)4!5?h0{Anv?6Y!{c_fz%3o4rXc>2&YO^cymq*fMg_kKx%q1~;HnIvK9_^5op!Nc|x zt&;Op!!sDucY3~WM}RHa7ixR1dumMuZD8!ATvV}m(Pb4PR?#FHIP^gt7w6TCDMro4 zq$Rc;?to~yRX3L;Zo&X88zP^ojoh$Uulmm2cjtwzoFyDlTjF@QY{g3Oe!oA?=l$b-{&=4IzMt!RUEgcF@6Vm)b=X-&d8aY} z08nvt!FdA!8$hx#YO|v39hfX#lYMl!jsaXBW(YTmz@`A~$V?&y=t?J0Dc%$UIXa?~ zasU93i=_Dma05KtF(f7(LRgo9@aQZV8UQ$8!($OhVH7TqNTJdgSkPSaLlBTg#)6Jm zc)&bZ4wO)uOAMRh6LZ*?6ca{5lR-8Ifd_aPnF2b6O91ld;S3IjhXsApi;<1jm!Tlw zHxX_a7W5~n01q#q1CvbwT0ktpBpBQrXoZHrEiA3jR%SqiYz~E?pa>WkhQ?SRF)#%1 z`va0$W0ONL-Z-c4wq!FbD3r@(VW7~cs3=Gj62fFtp>Q-B4TT|~2n1Lr0p>(AxC9=U z!7==y0Y~AG*fbWG#$*83H4=!-Q(P=a#`I4Y=qwM9zZ5e#-vcEJ8I(t0LE#V>lulpw z>zg!(>rMGT8h#~b#G?hTX`vYK(Ux>UV(h5qOFWRco1LAFf;l`DK=OYcX7b;Mts^kVtx(_Ls1!6 z@h;erWNUuxTxq!+UX;Du6#gX{Ioc?=^YVl3mG*=V@44Bf7<=J_o+M=;7NwWEV+u7n z2vIC98t@dZ9Wg9BuPBf^Eg+GE6Xp3-ubPWz@=r2JCbUoEBvY@M3?xZ8Tv9Tj|X=B+mnld>#M>=1tvzvL@LvxO5^RBjx+A+Fmw zN!q>DJ_p5E2&RYx+i7uh>7ZhHl67}_<8zI&n@#WISy-#Li` zCi*Si*XWTNygu~Hmfvc&x(R6o(!IwGNbsrPG4w8#>X4tn^SiaIR5=D}6w?E9hcE@V zKc42S^P54=#Q8b`yHd1XEutfZIjmep)^eHj%sB7uOt1;H_NdLTY=ew<;^YeG9k`fl z*F(CUk-EY7dWfYz@A<^Q%E<0igdpMZqIq^UQdjfvh_w3&&htrb-}9q40f_W3_hvZZ zM|LufEp)fxJW0dV-L_nuHqT`7c1;(XWeN8#hy>S}Sc&K-v=j~~_ru4X_ zBFLqudGl$$>(%re*8O&+f|bG@EX7{m;b?b+9RBb}>D|(%VnNO|R;i{aIqQXY-qfZ% zuMqhmw^gX<^JHN>U@ZY9Ehy7B`xv-LEMIEg&EXk?Qr6p? zK}0~(n&AB&IxK0ta%kU z#qFI`68xe&$WfHuJUh|kmzWP8I3n&bqAi!m|7K#+a=t@-U_1WdwR@zXvJ-Z3d2?{_ zY5u)CXbzHH4HX}Ad4+MqKw{#ZSJ|bv<{)~IpNILCRpA#yjpwzC8SR@X&8;qo#=>5N z$SlM)R#%*iudfDfhKpRwBnO=rc5BOFvkfK7pq?)lr;0Klt#?P1ZI-rISf@o*6yQ4tk9rrKgb5$)j3wdS ztl^EU#C)s3Vq-GOP;N3h#W6-?hqY2ziW@PH(^?6Qt=sNfZWxox+x7MVT+++}HU`_P2?#FwnmnAc9i8akH5)BoKr?ts+V zCFc$}Kk*3^_8~ZQhq$?IWZTye+}!8Ak$nnhBdYhl2Gd`sUut-L68k3gQbVsT_Dx!V z=jE005md^tpox^0tijq)p02baEtzO!msA#GJU?4<$MEgvvs^uG=Niz16!VJ$WQ2KU zYqTyqw@?zSyfDoQ@J!JE|3qe6iNdsfw9j|Zcx|O zkeDhaKan?`E!h9n{>&e)?E>gYmREa|tETaQZB}_s=1s}I_tU10)gI623tY)SJ;`xoBN(zv4L-cdP;-uv$TeH?_z3Aw#Np5m__ z_~yZ)TMskFwuKI#S^Z*KzhR^E`=E6(bDc1 zD5zBYIdemd>P5R|g;+e>Y0>1tS|5z5(GCZ`aTCPW>>2ZY#>mv=v=bN;#e(H!x5O6ltrdv%qOPv_X`s$OUo!K@7!hEon}&`X>&$tiH2ci zt6xURUOQoLdFk$UB|$wuTGzE3Z6Dd#*0a&^Y=@5U<(@iv&H$rT{B-QN#){+kXFf>n z!`}EaM!M}em%CpIWG4cxPrV+2?1kWK{T)B91fJiuwdST4EBBJ8Qhwsg?f4X zR2HJ5nR=4i@U-=Vm;X;6q;Y9Ws$oO>hCD7`Q(4M!L=`pasUAqc0;qS4L`b=DowG>Q z7=C}5@l;8ie4u{E9JNaPCfBxYHeMqTsFBua4JX=1uUfhX5;~;uqBnDAi*pag)FZDH zHO5;Xw!@*C!`^p0KkAIV%C=B63_?3D?5=^UrPbS)4kuP_SE^quJm~KXe4Fw$l{aF0 z9Fp2n`)=ptuDd()$HwGQMFVjj>Y`q4)|jEV(XOYGh&^@KA$nwXQ`^X=PlsB4wE}t` zzqi{T_N%S@O7U_;H&TDk>%k;%pnl0v4g0Id)8hO4wKdvw9<_c^Q}!>9s74AJ4%>R_ ziLzCGYw)kj*R)`zFZ8b5F31S$8H*|1#zuIAxz*kkOO%G4UqsZF`pOp*ON<4d zHf$qWNYK(dQWm;C=Ct1rON38d(PEep7l3T z#lig0gB|D`o^$-{)u(|x$>D8NILt!$uaA^s@=F6f{p89{^#87G4z^qsb*bXa`WV2y z3|-UKAzd)j;5E9%Se*$!RvMhX#%SX=xEM&Z-d9Rb>Af_(rHXwSoV-H1bt<@LgYv>c zSQmp*W#2EHu&QiV`KNB`&`h46e##duQN)PW1&EHoZBx@sM8Nf$OFp;qJy%fuY5AF9 z!#C`FELR!ZpQqzCst;^_x4T7ujpuaH&ah@{G8#(Hvm7Lwhk=Ww1> vbBa~slJom)@4URcJ#}pwZtk~MjUNGC^i6Od=p8+}esOelJdCThKbiO+!48QO literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_5.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/Star_Sprite_5.png new file mode 100644 index 0000000000000000000000000000000000000000..ad40f083079246df9d1c7822ef2b7444b67e7387 GIT binary patch literal 3849 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 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_learn_more.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_learn_more.png new file mode 100644 index 0000000000000000000000000000000000000000..21b8472308809bf8e881e6c8ae90ab14e36f2b9f GIT binary patch literal 11806 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 k!GXc@q+qV)8;AN&;02><{9 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_play.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_play.png new file mode 100644 index 0000000000000000000000000000000000000000..2acb854f1827a12957418f1e22aa868459f37d2a GIT binary patch literal 10380 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 KtXZ!Pi}^narUn53 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_replay.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_replay.png new file mode 100644 index 0000000000000000000000000000000000000000..2ed3bd3bfef802b7e2c14abd2c16186ae5f67744 GIT binary patch literal 15399 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 ZmADu$o@n+~T)(=jEUzy2?xAVe{{yXJ(ii{$ literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_unmuted.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Assets/applovin_card_unmuted.png new file mode 100644 index 0000000000000000000000000000000000000000..773cdb07fe2297e3cc7993e849795b1c80a44c4f GIT binary patch literal 5483 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface UIView (ALActivityIndicator) + +/** + * Show an activity indicator with a semi-transparent black overlay underneath without fade. + */ +- (void)al_showActivityIndicator; + +/** + * Show an activity indicator with a semi-transparent black overlay underneath with a fade animation option. + */ +- (void)al_showActivityIndicatorAnimated:(BOOL)animated; + +/** + * Hides the activity indicator view without fade. + */ +- (void)al_hideActivityIndicator; + +/** + * Hides the activity indicator view with a fade animation option. + */ +- (void)al_hideActivityIndicatorAnimated:(BOOL)animated; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Categories/UIView+ALActivityIndicator.m b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Categories/UIView+ALActivityIndicator.m new file mode 100644 index 0000000000..a23678f6d8 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Categories/UIView+ALActivityIndicator.m @@ -0,0 +1,88 @@ +// +// UIView+ALActivityIndicator.m +// sdk +// +// Created by Thomas So on 5/15/15. +// +// + +#import "UIView+ALActivityIndicator.h" +#import "ALCarouselView+Internal.h" +#import "ALDebugLog.h" + +@implementation UIView(ALActivityIndicator) +static NSString *const kActivityIndicatorKey = @"activityIndicator"; +static NSString *const kActivityIndicatorOverlayKey = @"activityIndicatorOverlay"; + +static const CGFloat kTargetOverlayAlpha = 1.0f; +static const CGFloat kAnimationDuration = 0.35f; + +- (void)al_showActivityIndicator +{ + [self al_showActivityIndicatorAnimated: NO]; +} + +- (void)al_showActivityIndicatorAnimated:(BOOL)animated +{ + UIView *overlay = [self valueForKey: kActivityIndicatorOverlayKey]; + if ( !overlay ) + { + overlay = [[UIView alloc] init]; + overlay.backgroundColor = [UIColor whiteColor]; + overlay.frame = self.bounds; + + ALLog(@"Created overlay with frame: %@", NSStringFromCGRect(overlay.frame)); + + [self setValue: overlay forKey: kActivityIndicatorOverlayKey]; + } + + UIActivityIndicatorView *activityIndicator = [self valueForKey: kActivityIndicatorKey]; + if ( !activityIndicator ) + { + activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleGray]; + activityIndicator.hidesWhenStopped = YES; + activityIndicator.center = self.center; + + [overlay addSubview: activityIndicator]; + [overlay bringSubviewToFront: activityIndicator]; + + [self setValue: activityIndicator forKey: kActivityIndicatorKey]; + } + + [activityIndicator startAnimating]; + overlay.alpha = animated ? 0.0f : kTargetOverlayAlpha; + + [self addSubview: overlay]; + [self bringSubviewToFront: overlay]; + + if ( animated ) + { + [UIView animateWithDuration: kAnimationDuration animations:^{ + overlay.alpha = kTargetOverlayAlpha; + }]; + } +} + +- (void)al_hideActivityIndicator +{ + [self al_hideActivityIndicatorAnimated: NO]; +} + +- (void)al_hideActivityIndicatorAnimated:(BOOL)animated +{ + UIView *overlay = [self valueForKey: kActivityIndicatorOverlayKey]; + if ( !overlay.superview ) + { + return; + } + + UIActivityIndicatorView *activityIndicator = [self valueForKey: kActivityIndicatorKey]; + [UIView animateWithDuration: animated ? kAnimationDuration : 0.0f animations:^{ + overlay.alpha = 0.0f; + } completion:^(BOOL finished) { + [activityIndicator stopAnimating]; + [overlay removeFromSuperview]; + }]; +} + +@end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Customizable SETTINGS/ALCarouselViewSettings.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Customizable SETTINGS/ALCarouselViewSettings.h new file mode 100644 index 0000000000..84cad4d4fd --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Customizable SETTINGS/ALCarouselViewSettings.h @@ -0,0 +1,86 @@ +// +// ALCarouselViewSettings.h +// iOS Test App NG +// +// Created by Thomas So on 5/27/15. +// Copyright (c) 2015 AppLovin. All rights reserved. +// + +/** + * This file contains advanced UI configuration for ALCarouselView. + * Sizing and colors can be easily tweaked to your liking here. + */ + +#ifndef iOS_Test_App_NG_ALCarouselViewSettings_h +#define iOS_Test_App_NG_ALCarouselViewSettings_h + +#define kTextColor [UIColor darkTextColor] +#define kReplayTextColor [UIColor whiteColor] +#define kCarouselBackgroundColor [UIColor whiteColor] +#define kCardBackgroundColor [UIColor whiteColor] +#define kCardButtonColor [UIColor colorWithWhite: 0.84f alpha: 1.0f] +#define kVideoViewBackgroundColor [UIColor blackColor] +#define kVideoViewBackgroundWhilePlayingColor [UIColor blackColor] +#define kReplayOverlayBackgroundColor [UIColor blackColor] +#define kButtonHighlightTint [UIColor colorWithWhite: 0.87f alpha: 1.0f] + +// Media controls +static BOOL const kIsAutoplay = YES; +static BOOL const kVideoClicksThrough = YES; +static BOOL const kConfigIsMuted = YES; +static BOOL const kRenderVideoScreenshotAsFallbackImage = YES; + +// Replay overlay controls +static NSString *const kTextReplayVideo = @"Replay Video"; +static NSString *const kTextLearnMore = @"Learn More"; +static CGFloat const kConfigReplayOverlayAlpha = 0.75f; + + + +// Carousel constants +static NSUInteger const kNativeAdsToLoadCount = 3; +static NSString *const kFontFamily = @""; + +// Carousel layout constants +static CGFloat const kCardWidthPercentage = 0.90f; // As percentage of width of screen +static CGFloat const kCardMargin = 5.0f; // The margin on the side of each card. So a margin of 5px would result in a total of 10px separation card-to-card + +// Spring animation constants +static CGFloat const kSpringDuration = 0.3f; +static CGFloat const kDelay = 0.0f; +static CGFloat const kSpringDampeningGoNextCard = 0.9f; +static CGFloat const kSpringDampeningReturnSameCard = 0.7f; +static CGFloat const kInitialSpringVelocity = 0.0f; +static CGFloat const kConfigSwipeThreshold = 40.0f; // The number of pixels that will trigger a swipe event + +// Card layout constants +static CGFloat const kCardPadding = 10.0f; +static CGFloat const kTopMargin = 5.0f; +static CGFloat const kAppIconSize = 40.0f; +static CGFloat const kRatingHeight = 15.0f; +static CGFloat const kStarRatingTopPadding = 0.0f; +static CGFloat const kMaxStarRatingHeight = 15.0f; +static NSUInteger const kStarWidthToHeightMultiplier = 5; +static CGFloat const kDescriptionVerticalMargin = 16.0f; +static CGFloat const kVideoAspectRatio = 1.0f/1.78f; +static CGFloat const kDescriptionTextHeight = 36.0f; +static CGFloat const kCtaMaxHeight = 40.0f; +static CGFloat const kCtaCornerRadius = 3.0f; + +// Card configurations +static BOOL const kConfigEntireCardClickable = YES; +static CGFloat const kFontSizeTitle = 14.0f; +static CGFloat const kFontSizeDescription = 14.0f; +static CGFloat const kFontSizeButton = 18.0f; +static NSUInteger const kDescriptionMaxLines = 2; + +// Media layout constants +static CGFloat const kPadding = 10.0f; +static CGFloat const kMuteButtonPadding = 12.0f; +static CGFloat const kMuteWidth = 20.0f; +static CGFloat const kMuteHeight = 20.0f; +static CGFloat const kMuteButtonMargin = 8.0f; +static CGFloat const kPlayReplayWidth = 40.0f; +static CGFloat const kPlayReplayHeight = 40.0f; + +#endif \ No newline at end of file diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselCardView.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselCardView.h new file mode 100644 index 0000000000..399ed62aea --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselCardView.h @@ -0,0 +1,54 @@ +// +// ALCarouselCardView.h +// sdk +// +// Created by Thomas So on 4/20/15. +// +// + +#import + +#import "ALCarouselMediaView.h" +#import "ALCarouselViewModel.h" +#import "ALCarouselRenderingProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@class ALCarouselView; + +/** + * This view is used for paging of the carousel. + */ +@interface ALCarouselCardView : UIView + +/** + * The view containing the ad video or image. + */ +@property (strong, nonatomic) ALCarouselMediaView *mediaView; + +/** + * Initializes a newly allocated card view object with the specified sdk + */ +- (instancetype) initWithSdk:(ALSdk *)sdk; + +/** + * Redirects to the CTA for the given ad. + * + * @param ad The ad with the CTA URL to redirect to. + */ +- (void)handleClickForAd:(ALNativeAd *)ad; + +/** + Call this method when your view is displayed to the user. + Will track an impression. + */ +- (void)trackImpression; + +@property (strong, nonatomic, nullable) UIActivityIndicatorView *activityIndicator; +@property (strong, nonatomic, nullable) UIView *activityIndicatorOverlay; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselCardView.m b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselCardView.m new file mode 100644 index 0000000000..ebc5c9fb4b --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselCardView.m @@ -0,0 +1,354 @@ +// +// ALCarouselCardView.m +// sdk +// +// Created by Thomas So on 4/20/15. +// +// + +@import AppLovinSDK; +#import "ALCarouselCardView.h" +#import "ALCarouselCardState.h" +#import "UIView+ALActivityIndicator.h" +#import "ALCarouselViewSettings.h" +#import "ALDebugLog.h" + +@interface ALCarouselCardView() + +@property (weak, nonatomic) ALSdk *sdk; + +@property (strong, nonatomic) ALCarouselCardState *cardState; +@property (strong, nonatomic) ALNativeAd *ad; + +@property (strong, nonatomic) UITapGestureRecognizer *cardTapGesture; +@property (strong, nonatomic) UIView *contentView; +@property (strong, nonatomic) UIView *topBarContainer; +@property (strong, nonatomic) UIImageView *appIcon; +@property (strong, nonatomic) UILabel *titleLabel; +@property (strong, nonatomic) UIImageView *ratingImageView; +@property (strong, nonatomic) UILabel *descriptionLabel; +@property (strong, nonatomic) UIButton *ctaButton; + +@end + +@implementation ALCarouselCardView +static NSString *const TAG = @"ALCarouselCardView"; + +#pragma mark - Initialization + +- (instancetype)initWithSdk:(ALSdk *)sdk +{ + self = [super init]; + if ( self ) + { + [self baseInitWithSdk: sdk]; + } + return self; +} + +- (instancetype)initWithFrame: (CGRect) frame +{ + self = [super initWithFrame: frame]; + if ( self ) + { + [self baseInitWithSdk: [ALSdk shared]]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder: aDecoder]; + if ( self ) + { + [self baseInitWithSdk: [ALSdk shared]]; + } + return self; +} + +- (void)baseInitWithSdk:(ALSdk*) sdk +{ + self.sdk = sdk; + + self.backgroundColor = [UIColor clearColor]; + + self.cardTapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(didTapCard:)]; + self.cardTapGesture.enabled = kConfigEntireCardClickable; + [self addGestureRecognizer: self.cardTapGesture]; + + self.contentView = [[UIView alloc] init]; + self.contentView.backgroundColor = kCardBackgroundColor; + [self addSubview: self.contentView]; + + self.topBarContainer = [[UIView alloc] init]; + [self.contentView addSubview: self.topBarContainer]; + + self.appIcon = [[UIImageView alloc] init]; + self.appIcon.userInteractionEnabled = YES; + [self.contentView addSubview: self.appIcon]; + + UITapGestureRecognizer *appIconTapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(didTapAppIcon:)]; + [self.appIcon addGestureRecognizer: appIconTapGesture]; + + self.titleLabel = [[UILabel alloc] init]; + self.titleLabel.textColor = kTextColor; + [self.topBarContainer addSubview: self.titleLabel]; + + self.ratingImageView = [[UIImageView alloc] init]; + self.ratingImageView.contentMode = UIViewContentModeScaleAspectFit; + [self.topBarContainer addSubview: self.ratingImageView]; + + self.descriptionLabel = [[UILabel alloc] init]; + self.descriptionLabel.textColor = kTextColor; + self.descriptionLabel.numberOfLines = kDescriptionMaxLines; + [self.contentView addSubview: self.descriptionLabel]; + + self.mediaView = [[ALCarouselMediaView alloc] initWithSdk: self.sdk parentView: self]; + [self.contentView addSubview: self.mediaView]; + + self.ctaButton = [[UIButton alloc] init]; + self.ctaButton.layer.masksToBounds = YES; + self.ctaButton.layer.cornerRadius = kCtaCornerRadius; + self.ctaButton.backgroundColor = kCardButtonColor; + [self.ctaButton setTitleColor: kTextColor forState: UIControlStateNormal]; + [self.ctaButton addTarget: self action: @selector(didTapCTAButton:) forControlEvents: UIControlEventTouchUpInside]; + [self.contentView addSubview: self.ctaButton]; + + // Determine label fonts + if ( [[UIFont familyNames] containsObject: kFontFamily] ) + { + self.titleLabel.font = [UIFont fontWithName: kFontFamily size: kFontSizeTitle]; + self.descriptionLabel.font = [UIFont fontWithName: kFontFamily size: kFontSizeDescription]; + self.ctaButton.titleLabel.font = [UIFont fontWithName: kFontFamily size: kFontSizeButton]; + } + else + { + self.titleLabel.font = [UIFont systemFontOfSize: kFontSizeTitle]; + self.descriptionLabel.font = [UIFont systemFontOfSize: kFontSizeDescription]; + self.ctaButton.titleLabel.font = [UIFont systemFontOfSize: kFontSizeButton]; + } +} + +#pragma mark - Action Methods + +- (void)didTapCTAButton:(UIButton *)sender +{ + ALLog(@"Redirecting from cta button click"); + [self handleClickForAd: self.ad]; +} + +- (void)didTapAppIcon:(UITapGestureRecognizer *)tapGesture +{ + ALLog(@"Redirecting from app icon click"); + [self handleClickForAd: self.ad]; +} + +- (void)didTapCard:(UITapGestureRecognizer *)tapGesture +{ + ALLog(@"Redirecting from card click"); + [self handleClickForAd: self.ad]; +} + +#pragma mark - View Management + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + // Layout content view + CGRect contentFrame = CGRectZero; + contentFrame.origin.x = kCardMargin; + contentFrame.size.width = CGRectGetWidth(self.bounds) - (2*kCardMargin); + contentFrame.size.height = CGRectGetHeight(self.bounds); + self.contentView.frame = contentFrame; + + // Layout app icon + self.appIcon.frame = CGRectMake(kCardPadding, kTopMargin, kAppIconSize, kAppIconSize); + + // Layout title + CGRect titleFrame = CGRectZero; + titleFrame.size.width = (CGRectGetWidth(self.contentView.frame) - CGRectGetMaxX(self.appIcon.frame)) - (2*kCardPadding); + titleFrame.size.height = [self.titleLabel sizeThatFits: CGSizeMake(CGRectGetWidth(titleFrame), CGFLOAT_MAX)].height; + self.titleLabel.frame = titleFrame; + + // Layout star rating + const CGFloat ratingWidth = kMaxStarRatingHeight*kStarWidthToHeightMultiplier; + self.ratingImageView.frame = CGRectMake(CGRectGetMinX(titleFrame), CGRectGetMaxY(titleFrame) + kStarRatingTopPadding, ratingWidth, kRatingHeight); + + // Layout top bar + CGRect topBarFrame = CGRectZero; + topBarFrame.origin.x = CGRectGetMaxX(self.appIcon.frame) + kCardPadding; + topBarFrame.size.width = CGRectGetWidth(self.contentView.frame) - (3*kCardPadding) - kAppIconSize; + topBarFrame.size.height = CGRectGetHeight(self.titleLabel.frame) + kRatingHeight; + self.topBarContainer.frame = topBarFrame; + self.topBarContainer.center = CGPointMake(self.topBarContainer.center.x, self.appIcon.center.y); + + // Layout description + CGRect descriptionFrame = CGRectZero; + descriptionFrame.origin.x = kCardPadding; + descriptionFrame.origin.y = CGRectGetMaxY(self.topBarContainer.frame) + kDescriptionVerticalMargin; + descriptionFrame.size.width = CGRectGetWidth(self.contentView.frame) - (2*kCardPadding); + descriptionFrame.size.height = [self.descriptionLabel sizeThatFits: CGSizeMake(CGRectGetWidth(descriptionFrame), CGFLOAT_MAX)].height; + self.descriptionLabel.frame = descriptionFrame; + + // Layout media view + const CGFloat maxHeightPossible = (CGRectGetMaxY(self.frame) - kCardPadding) - (CGRectGetMaxY(self.descriptionLabel.frame) + kCardPadding); + const CGFloat fittedHeight = (CGRectGetWidth(self.frame)) * kVideoAspectRatio; + + CGRect mediaFrame = CGRectZero; + mediaFrame.origin.y = CGRectGetMaxY(self.topBarContainer.frame) + (2*kDescriptionVerticalMargin) + kDescriptionTextHeight; + mediaFrame.size.width = CGRectGetWidth(self.contentView.frame); + mediaFrame.size.height = MIN(maxHeightPossible, fittedHeight); + self.mediaView.frame = mediaFrame; + + // Layout CTA button (center it between bottom of video and bottom of card) + const CGFloat ctaOriginY = CGRectGetMaxY(self.mediaView.frame) + (CGRectGetMaxY(self.contentView.frame) - CGRectGetMaxY(self.mediaView.frame))/2 - kCtaMaxHeight/2; + + // If button will overflow bottom, hide it + BOOL willOverflow = (ctaOriginY + kCtaMaxHeight) > CGRectGetMaxY(self.contentView.frame); + if ( willOverflow ) + { + self.ctaButton.frame = CGRectZero; + self.ctaButton.hidden = YES; + } + else + { + CGRect ctaFrame = CGRectZero; + ctaFrame.origin.y = ctaOriginY; + ctaFrame.size.height = kCtaMaxHeight; + ctaFrame.size.width = (2*kCardPadding) + [self.ctaButton sizeThatFits: CGSizeMake(CGFLOAT_MAX, 0.0f)].width; + ctaFrame.origin.x = CGRectGetWidth(self.contentView.frame) - kCardPadding - CGRectGetWidth(ctaFrame); + + self.ctaButton.frame = ctaFrame; + } + + // Activity Views from category + self.activityIndicatorOverlay.frame = self.bounds; + self.activityIndicator.center = self.activityIndicatorOverlay.center; +} + +#pragma mark - View Rendering + +- (void)renderViewForNativeAd:(ALNativeAd *)ad cardState:(ALCarouselCardState *)cardState +{ + if ( ad ) + { + self.ad = ad; + self.cardState = cardState; + + [self refresh]; + } + else + { + [self clearView]; + } +} + +- (void)refresh +{ + ALLog(@"----------Begin refreshing carousel card view----------"); + + ALNativeAd *ad = self.ad; + + // If there is ad, render + if ( ad ) + { + ALLog(@"Refreshing ad (%@) for card view", ad.adIdNumber); + + self.titleLabel.text = ad.title; + self.descriptionLabel.text = ad.descriptionText; + self.mediaView.hidden = NO; + self.ctaButton.hidden = NO; + [self.ctaButton setTitle: ad.ctaText forState: UIControlStateNormal]; + [self populateStarRating: ad]; + + // If the images are pre-cached, just render them + if ( [ad isImagePrecached] ) + { + ALLog(@"Native ad (%@) is pre-cached. Refreshing image resources", ad.adIdNumber); + +#pragma mark - Populate with appropriate stars asset depending on starRating + self.appIcon.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: ad.iconURL]]; // Local URL + + [self.mediaView renderViewForNativeAd: self.ad cardState: self.cardState]; + + [self al_hideActivityIndicatorAnimated: YES]; + [self setNeedsLayout]; + } + else + { + [self clearView]; + [self al_showActivityIndicator]; + } + } + // There is no slot to render, so clear view + else + { + ALLog(@"Refreshing nil native ad for card view. Clearing view..."); + + [self clearView]; + [self al_hideActivityIndicatorAnimated: YES]; + } + + ALLog(@"----------Finish refreshing carousel card view----------"); +} + +#pragma mark - Utility + +- (void)handleClickForAd:(ALNativeAd *)ad +{ + if ( ad ) + { + [ad launchClickTarget]; + } + else + { + // Something is wrong, trying to redirect when card view does not have a native ad (like when ad is still loading) + ALLog(@"Attempting to open CTA URL with a nil native ad"); + } +} + +- (void)trackImpression +{ + if ( self.ad && self.cardState ) + { + ALLog(@"Handling displaying of native ad (%@)", self.ad.adIdNumber); + + if ( !self.cardState.impressionTracked ) + { + self.cardState.impressionTracked = YES; + ALLog(@"Tracking impression for ad (%@)", self.ad.adIdNumber); + [self.ad trackImpression]; + } + } + else + { + ALLog(@"Attempting to handle a nil slot or nil card state being displayed"); + } +} + +- (void)populateStarRating:(ALNativeAd*) ad +{ + NSString* filename = [NSString stringWithFormat: @"Star_Sprite_%@", ad.starRating.stringValue]; + UIImage* starRating = [UIImage imageNamed: filename]; + self.ratingImageView.image = starRating; +} + +- (void)clearView +{ + self.titleLabel.text = @""; + self.descriptionLabel.text = @""; + self.ratingImageView.image = nil; + self.appIcon.image = nil; + + self.ctaButton.hidden = YES; + [self.ctaButton setTitle: @"" forState: UIControlStateNormal]; + + self.mediaView.hidden = YES; + + // Reset State + self.cardState = nil; + self.ad = nil; +} + +@end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselMediaView.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselMediaView.h new file mode 100644 index 0000000000..bfb300920e --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselMediaView.h @@ -0,0 +1,36 @@ +// +// ALCarouselMediaView.h +// sdk +// +// Created by Thomas So on 4/20/15. +// +// + +#import + +#import "ALCarouselViewModel.h" +#import "ALCarouselRenderingProtocol.h" + +@class ALCarouselCardView; + +NS_ASSUME_NONNULL_BEGIN + +/** + * This view is used to store the ad's media. + */ +@interface ALCarouselMediaView : UIView + +/** + * Saves the current video's states and clears it. This is for when moving a slot out of the middle card. + */ +- (void)setInactive; + +/** + * Initializes a newly allocated media view object with the specified sdk and parent card view. + */ +- (instancetype)initWithSdk:(ALSdk *)sdk parentView:(ALCarouselCardView *)parentView; +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselMediaView.m b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselMediaView.m new file mode 100644 index 0000000000..a3eae4be75 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselMediaView.m @@ -0,0 +1,599 @@ +// +// ALCarouselMediaView.m +// sdk +// +// Created by Thomas So on 4/20/15. +// +// + +@import AppLovinSDK; +#import "ALCarouselMediaView.h" +#import "ALCarouselCardState.h" +#import "ALCarouselReplayOverlayView.h" +#import "ALCarouselCardView.h" +#import "ALCarouselViewSettings.h" +#import "ALDebugLog.h" +#import "ALNativeAdVideoPlayer.h" +#import "ALNativeAdVideoView.h" + +@interface ALCarouselMediaView() + +@property (weak, nonatomic) ALSdk *sdk; + +@property (strong, nonatomic) ALCarouselCardView *cardView; +@property (strong, nonatomic) ALCarouselCardState *cardState; +@property (strong, nonatomic) ALNativeAd *ad; + +@property (strong, nonatomic) UIImageView *adImageView; + +@property (strong, nonatomic) ALNativeAdVideoPlayer *videoPlayer; +@property (weak, nonatomic) ALNativeAdVideoView *videoView; +@property (strong, nonatomic) UIButton *muteButton; +@property (strong, nonatomic) UIButton *playButton; +@property (strong, nonatomic) ALCarouselReplayOverlayView *replayOverlayView; + +@end + +@implementation ALCarouselMediaView +static NSString *const TAG = @"ALCarouselMediaView"; + +#pragma mark - Initialization Methods + +- (instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder:coder]; + if (self) + { + self.sdk = [ALSdk shared]; + [self setup]; + } + return self; +} + +- (instancetype)initWithSdk:(ALSdk *)sdk parentView:(ALCarouselCardView *)parentView; +{ + self = [super init]; + if ( self ) + { + self.sdk = sdk; + self.cardView = parentView; + [self setup]; + } + return self; +} + +- (void)setup +{ + self.backgroundColor = kVideoViewBackgroundColor; + + self.adImageView = [[UIImageView alloc] init]; + self.adImageView.userInteractionEnabled = NO; + self.adImageView.backgroundColor = [UIColor clearColor]; + + UITapGestureRecognizer *adImageTapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(didTapAdImage:)]; + [self.adImageView addGestureRecognizer: adImageTapGesture]; + + if ( self.replayOverlayView ) + { + [self.replayOverlayView removeFromSuperview]; + } + + self.replayOverlayView = [[ALCarouselReplayOverlayView alloc] initWithParentView: self]; + self.replayOverlayView.alpha = 0.0f; + self.replayOverlayView.userInteractionEnabled = YES; + [self.replayOverlayView.replayIconButton addTarget: self action: @selector(didTapReplayButton:) forControlEvents: UIControlEventTouchUpInside]; + [self.replayOverlayView.replayButton addTarget: self action: @selector(didTapReplayButton:) forControlEvents: UIControlEventTouchUpInside]; + [self.replayOverlayView.learnMoreIconButton addTarget: self action: @selector(didTapLearnMoreButton:) forControlEvents: UIControlEventTouchUpInside]; + [self.replayOverlayView.learnMoreButton addTarget: self action: @selector(didTapLearnMoreButton:) forControlEvents: UIControlEventTouchUpInside]; + + [self addSubview: self.adImageView]; + [self addSubview: self.replayOverlayView]; +} + +#pragma mark - App Notifications + +- (void)appPaused:(NSNotification *)notification +{ + [self deactivateIfNeeded]; +} + +- (void)appResumed:(NSNotification *)notification +{ + [self reactivateIfNeeded]; +} + +#pragma mark - View Management + +- (void)willMoveToWindow:(UIWindow *)newWindow +{ + [super willMoveToWindow: newWindow]; + + // Will be moved into a window + if ( newWindow ) + { + [self reactivateIfNeeded]; + + [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(appPaused:) name: UIApplicationDidEnterBackgroundNotification object: nil]; + [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(appResumed:) name: UIApplicationDidBecomeActiveNotification object: nil]; + } + // Will be removed from window + else + { + [self deactivateIfNeeded]; + + [[NSNotificationCenter defaultCenter] removeObserver: self]; + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + self.videoView.frame = self.bounds; + + const CGFloat muteButtonWidth = kMuteWidth + kMuteButtonPadding; + const CGFloat muteButtonHeight = kMuteHeight + kMuteButtonPadding; + + CGRect muteFrame = CGRectZero; + muteFrame.origin.x = kMuteButtonMargin; + muteFrame.origin.y = CGRectGetMaxY(self.videoView.frame) - muteButtonHeight - kMuteButtonMargin; + muteFrame.size.width = muteButtonWidth; + muteFrame.size.height = muteButtonHeight; + self.muteButton.frame = muteFrame; + self.muteButton.imageEdgeInsets = UIEdgeInsetsMake(kMuteButtonPadding, 0.0f, 0.0f, kMuteButtonPadding); + + self.playButton.frame = CGRectMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds), kPlayReplayWidth, kPlayReplayHeight); + self.playButton.center = self.videoView.center; + + self.replayOverlayView.frame = self.bounds; + self.adImageView.frame = self.cardState.screenshot ? self.cardState.videoRect : self.bounds; +} + +// Entry points +- (void)renderViewForNativeAd:(ALNativeAd *)ad +{ + ALCarouselCardState *cardState = [ALCarouselCardState cardStateForSingleCard]; + [self renderViewForNativeAd: ad cardState: cardState]; +} + +- (void)renderViewForNativeAd:(ALNativeAd *)ad cardState:(ALCarouselCardState *)cardState +{ + if ( ad ) + { + self.ad = ad; + self.cardState = cardState; + [self createVideoPlayerIfNeeded]; + [self refresh]; + } + else + { + [self clearView]; + } +} + +- (void)refresh +{ + const ALNativeAd *ad = self.ad; + if ( ad ) + { + // If we get to this point, ad image is pre-cached + ALLog(@"Begin refresh media view for slot ID: %@ %@", ad.adIdNumber, ad.title); + + self.adImageView.userInteractionEnabled = YES; + + // Populate ad image behind replay overlay + if ( self.cardState.screenshot && !CGRectIsEmpty(self.cardState.videoRect) ) + { + // If this card has a video screenshot rendered, set that as the ad image + self.adImageView.image = self.cardState.screenshot; + self.adImageView.frame = self.cardState.videoRect; + } + else + { + // Else, just set the ad image to the default one + self.adImageView.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: ad.imageURL]]; + self.adImageView.frame = self.bounds; + } + + // If the replay overlay is supposed to be visible, show it and hide the video controls + if ( self.cardState.replayOverlayVisible ) + { + self.replayOverlayView.alpha = 1.0f; + self.playButton.alpha = 0.0f; + self.muteButton.alpha = 0.0f; + } + // Else, hide the replay overlay. Visibility of video controls will be determined if autoplay is on/off + else + { + self.replayOverlayView.alpha = 0.0f; + } + + // If this is the middle/single card, we give it special treatment + if ( self.cardState.currentlyActive ) + { + // If the video is pre-cached or we should stream + if ( [ad isVideoPrecached] ) + { + // Autoplay if required (if config says so, and replay overlay is not currently showing) + [self autoplayIfRequired]; + } + // Video is still pre-caching. Will show on top of ad image when pre-cached from carousel view + else + { + ALLog(@"Video still waiting to be pre-cached for slot (%@)...", ad.adIdNumber); + } + } + else + { + // No special treatment for non-middle cards + } + } + else + { + ALLog(@"Begin refresh for nil slot. Clearing view..."); + [self clearView]; + } +} + +#pragma mark - Video Action Methods + +- (void)autoplayIfRequired +{ + self.adImageView.userInteractionEnabled = NO; + + // Update the mute button regardless if we're autoplaying since we'll have to animate it regardless + [self updateMuteState]; + + // If the video is not waiting to be replayed + if ( !self.cardState.replayOverlayVisible ) + { + // If configuration says we should autoplay + if ( kIsAutoplay ) + { + [self playVideoIfInactive]; + } + else + { + // If we're not going to autoplay the video, animate in the video controls + [UIView animateWithDuration: 0.5f animations:^{ + self.playButton.alpha = 1.0f; + }]; + } + } +} + +- (void)playVideoIfInactive +{ + if ( [self isCurrentlyPlayingVideo] ) + { + ALLog(@"Attempting to play a video that's already playing"); + } + else + { + ALLog(@"Video play requested..."); + + self.videoView.playerLayer.backgroundColor = kVideoViewBackgroundWhilePlayingColor.CGColor; + self.videoView.backgroundColor = kVideoViewBackgroundWhilePlayingColor; + + // Prepare the view to play video + [UIView animateWithDuration: 1.0f animations:^{ + + self.muteButton.alpha = 1.0f; + self.playButton.alpha = 0.0f; + + // Crossfade the video in and the ad image out then autoplay the video if needed + self.adImageView.alpha = 0.0f; + self.videoView.alpha = 1.0f; + }]; + + self.adImageView.userInteractionEnabled = NO; + + // When replaying a video we do not fade out the replay overlay + self.replayOverlayView.alpha = 0.0f; + self.cardState.replayOverlayVisible = NO; + + // Attach new observer to get notified when video ends + [[NSNotificationCenter defaultCenter] addObserver: self + selector: @selector(playerItemDidReachEnd:) + name: AVPlayerItemDidPlayToEndTimeNotification + object: self.videoView.player.currentItem]; + + // Prepare the video + self.videoPlayer.mediaSource = self.ad.videoURL; + [self seekToPosition: self.cardState.lastMediaPlayerPosition]; + self.cardState.videoStarted = YES; + [self.videoPlayer playVideo]; + + + // Track the video start if we didn't track it yet + if ( !self.cardState.wasVideoStartTracked ) + { + ALLog(@"Tracking video start for native ad (%@)", self.ad.adIdNumber); + [self.sdk.postbackService dispatchPostbackAsync: self.ad.videoStartTrackingURL andNotify: nil]; + + self.cardState.videoStartTracked = YES; + } + } +} + +- (void)setInactive +{ + self.adImageView.alpha = 1.0f; + self.adImageView.userInteractionEnabled = YES; + self.videoView.alpha = 0.0f; + self.muteButton.alpha = 0.0f; + self.playButton.alpha = 0.0f; + + // Reset background colors + self.backgroundColor = kVideoViewBackgroundColor; + self.videoView.playerLayer.backgroundColor = kVideoViewBackgroundColor.CGColor; + self.videoView.backgroundColor = kVideoViewBackgroundColor; + + [self deactivateIfNeeded]; +} + +- (void)playerItemDidReachEnd:(NSNotification *)notification +{ + ALLog(@"Video finished playing for native ad (%@)", self.ad.adIdNumber); + + self.cardState.lastMediaPlayerPosition = 0.0f; + self.cardState.videoCompleted = YES; + self.cardState.replayOverlayVisible = YES; + + [UIView animateWithDuration: 0.5f animations:^{ + self.muteButton.alpha = 0.0f; + self.replayOverlayView.alpha = 1.0f; + }]; + + [self handleVideoStopPlaying]; +} + +#pragma mark - Action Methods + +- (void)didTapMuteButton:(UIButton *)muteButton +{ + self.cardState.muteState = self.cardState.muteState == ALMuteStateMuted ? ALMuteStateUnmuted : ALMuteStateMuted; + [self updateMuteState]; +} + +- (void)didTapPlayButton:(UIButton *)sender +{ + [self playVideoIfInactive]; +} + +- (void)didTapReplayButton:(UIButton *)sender +{ + [self playVideoIfInactive]; +} + +- (void)didTapLearnMoreButton:(UIButton *)sender +{ + ALLog(@"Redirecting from Learn More button"); + [self handleClick]; +} + +- (void)didTapVideo:(UITapGestureRecognizer *)tapGesture +{ + if ( kVideoClicksThrough ) + { + ALLog(@"Redirecting from video click"); + [self handleClick]; + [self setInactive]; + } + else + { + if ( [self isCurrentlyPlayingVideo] ) + { + [self setInactive]; + [UIView animateWithDuration: 0.5f animations:^{ + self.playButton.alpha = 1.0f; + }]; + } + else + { + [self playVideoIfInactive]; + } + } +} + +- (void)didTapAdImage:(UITapGestureRecognizer *)tapGesture +{ + ALLog(@"Redirecting from ad image click"); + [self handleClick]; +} + +- (void)handleClick +{ + if ( self.cardView ) + { + [self.cardView handleClickForAd: self.ad]; + } + else + { + [self.ad launchClickTarget]; + } +} + +#pragma mark - Video Utility Methods + +- (Float64)currentVideoPosition +{ + return CMTimeGetSeconds(self.videoView.player.currentTime); +} + +- (Float64)videoDuration +{ + return CMTimeGetSeconds(self.videoPlayer.playerAsset.duration); +} + +- (NSNumber *)percentViewed +{ + Float64 viewedRatio = [self currentVideoPosition] / [self videoDuration]; + return @( viewedRatio * 100 ); +} + +- (UIImage *)frameForVideoAtCurrentPosition +{ + AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset: self.videoPlayer.playerAsset]; + + // Set the tolerance so we have a precise screenshot + if ( [imageGenerator respondsToSelector: @selector(setRequestedTimeToleranceBefore:)] && + [imageGenerator respondsToSelector: @selector(setRequestedTimeToleranceAfter:)] ) + { + [imageGenerator setRequestedTimeToleranceBefore: kCMTimeZero]; + [imageGenerator setRequestedTimeToleranceAfter: kCMTimeZero]; + } + + return [UIImage imageWithCGImage: [imageGenerator copyCGImageAtTime: self.videoView.player.currentItem.currentTime + actualTime: nil + error: nil]]; +} + +- (void)handleVideoStopPlaying +{ + self.videoView.playerLayer.backgroundColor = kVideoViewBackgroundColor.CGColor; + + if ( kRenderVideoScreenshotAsFallbackImage ) + { + self.cardState.screenshot = [self frameForVideoAtCurrentPosition]; + self.adImageView.image = self.cardState.screenshot; + + // Save the video rect, so at layoutSubviews: in an inactive card, we know what aspect ratio size the screenshot + // should be rendered in. + self.cardState.videoRect = self.videoView.playerLayer.videoRect; + } + + [[NSNotificationCenter defaultCenter] removeObserver: self name: AVPlayerItemDidPlayToEndTimeNotification object: nil]; + + // Track video completion if we havn't already + if ( self.cardState.videoStarted ) + { + NSURL* postbackUrl = [self.ad videoEndTrackingURL: [[self percentViewed] unsignedIntegerValue] firstPlay: self.cardState.firstPlayback]; + [self.sdk.postbackService dispatchPostbackAsync: postbackUrl andNotify: nil]; + } + + self.cardState.firstPlayback = NO; +} + +- (BOOL)isCurrentlyPlayingVideo +{ + return self.videoView.player.rate > 0.0f; +} + +- (void)seekToPosition:(Float64)position +{ + CMTimeScale timeScale = self.videoView.player.currentItem.asset.duration.timescale; + CMTime time = CMTimeMakeWithSeconds(position, timeScale); + [self.videoView.player seekToTime: time toleranceBefore: kCMTimeZero toleranceAfter: kCMTimeZero]; +} + +- (void)updateMuteState +{ + ALMuteState currentState = self.cardState.muteState; + + // If the current state is unspecify, determine if it should be muted or unmuted according to settings + if ( currentState == ALMuteStateUnspecified ) + { + currentState = kConfigIsMuted ? ALMuteStateMuted : ALMuteStateUnmuted; + self.cardState.muteState = currentState; + } + + if ( currentState == ALMuteStateMuted ) + { + self.muteButton.selected = YES; + self.videoView.player.muted = YES; + } + else if ( currentState == ALMuteStateUnmuted ) + { + self.muteButton.selected = NO; + self.videoView.player.muted = NO; + } +} + +#pragma mark - Utility Methods + +- (void)clearView +{ + self.adImageView.image = nil; + self.adImageView.alpha = 0.0f; + self.adImageView.userInteractionEnabled = NO; + self.videoView.alpha = 0.0f; + self.replayOverlayView.alpha = 0.0f; + self.cardState = nil; +} + +// Called when resuming app or going back into the containing VC +- (void)reactivateIfNeeded +{ + if ( self.cardState.currentlyActive && [self.ad isVideoPrecached] ) + { + [self autoplayIfRequired]; + } +} + +// Called when pausing app or leaving VC *OR* when setting a card inactive +- (void)deactivateIfNeeded +{ + if ( [self isCurrentlyPlayingVideo] ) + { + self.cardState.lastMediaPlayerPosition = [self currentVideoPosition]; + + [self.videoPlayer stopVideo]; + [self handleVideoStopPlaying]; + } +} + +- (void)destroyVideoPlayer +{ + // We don't need a video player; remove any one left behind. + [self.videoPlayer stopVideo]; + self.videoPlayer = nil; + + [self.videoView removeFromSuperview]; + self.videoView = nil; + + [self.muteButton removeFromSuperview]; + [self.playButton removeFromSuperview]; + + self.playButton = nil; + self.muteButton = nil; +} + +- (void)createVideoPlayerIfNeeded +{ + [self destroyVideoPlayer]; + + // Create video player only for middle card + if ( self.cardState.currentlyActive && [self.ad isVideoPrecached] ) + { + self.videoPlayer = [[ALNativeAdVideoPlayer alloc] initWithMediaSource: nil]; + self.videoView.player.actionAtItemEnd = AVPlayerActionAtItemEndPause; + self.videoView.playerLayer.backgroundColor = [UIColor clearColor].CGColor; + + self.videoView = self.videoPlayer.videoView; + [self.videoView.playerLayer setNeedsDisplay]; + self.videoView.backgroundColor = kVideoViewBackgroundColor; + + self.muteButton = [[UIButton alloc] init]; + self.muteButton.alpha = 0.0f; + [self.muteButton addTarget: self action: @selector(didTapMuteButton:) forControlEvents: UIControlEventTouchUpInside]; + + self.playButton = [[UIButton alloc] init]; + self.playButton.alpha = 0.0f; + [self.playButton addTarget: self action: @selector(didTapPlayButton:) forControlEvents: UIControlEventTouchUpInside]; + + UITapGestureRecognizer *videoTapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(didTapVideo:)]; + [self.videoView addGestureRecognizer: videoTapGesture]; + + [self insertSubview: self.videoView belowSubview: self.adImageView]; + [self insertSubview: self.playButton aboveSubview: self.adImageView]; + [self insertSubview: self.muteButton aboveSubview: self.videoView]; + + // Popualte the assets + [self.playButton setImage: [UIImage imageNamed: @"applovin_card_play"] forState: UIControlStateNormal]; + [self.muteButton setImage: [UIImage imageNamed: @"applovin_card_unmuted"] forState: UIControlStateNormal]; + [self.muteButton setImage: [UIImage imageNamed: @"applovin_card_muted"] forState: UIControlStateSelected]; + } +} + +@end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselReplayOverlayView.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselReplayOverlayView.h new file mode 100644 index 0000000000..7c14e79d75 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselReplayOverlayView.h @@ -0,0 +1,29 @@ +// +// ALCarouselReplayOverlayView.h +// sdk +// +// Created by Thomas So on 4/22/15. +// +// + +@import UIKit; +#import "ALCarouselMediaView.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * This view provides a few buttons to allow the user to replay or click through an ad. + */ +@interface ALCarouselReplayOverlayView : UIView + +@property (strong, nonatomic) UIView *overlay; +@property (strong, nonatomic) UIButton *replayButton; +@property (strong, nonatomic) UIButton *replayIconButton; +@property (strong, nonatomic) UIButton *learnMoreButton; +@property (strong, nonatomic) UIButton *learnMoreIconButton; + +- (instancetype)initWithParentView:(ALCarouselMediaView *)parentView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselReplayOverlayView.m b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselReplayOverlayView.m new file mode 100644 index 0000000000..02a4d8768b --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselReplayOverlayView.m @@ -0,0 +1,99 @@ +// +// ALCarouselReplayOverlayView.m +// sdk +// +// Created by Thomas So on 4/22/15. +// +// + +#import "ALCarouselReplayOverlayView.h" +#import "ALCarouselViewSettings.h" + +@interface ALCarouselReplayOverlayView() +@property (weak, nonatomic) ALCarouselMediaView *mediaView; +@end + +@implementation ALCarouselReplayOverlayView + +#pragma mark - Initialization + +- (instancetype)initWithParentView:(ALCarouselMediaView *)parentView +{ + self = [super init]; + if ( self ) + { + self.mediaView = parentView; + + self.backgroundColor = [UIColor clearColor]; + + self.overlay = [[UIView alloc] init]; + + self.overlay.backgroundColor = kReplayOverlayBackgroundColor; + self.overlay.alpha = kConfigReplayOverlayAlpha; + [self addSubview: self.overlay]; + + self.replayIconButton = [[UIButton alloc] init]; + [self addSubview: self.replayIconButton]; + + self.replayButton = [[UIButton alloc] init]; + [self addSubview: self.replayButton]; + + self.learnMoreIconButton = [[UIButton alloc] init]; + [self addSubview: self.learnMoreIconButton]; + + self.learnMoreButton = [[UIButton alloc] init]; + [self addSubview: self.learnMoreButton]; + + [self.replayIconButton setTintColor: kButtonHighlightTint]; + [self.replayButton setTitleColor: kReplayTextColor forState: UIControlStateNormal]; + [self.replayButton setTitleColor: kButtonHighlightTint forState: UIControlStateHighlighted]; + [self.learnMoreButton setTitleColor: kReplayTextColor forState: UIControlStateNormal]; + [self.learnMoreButton setTitleColor: kButtonHighlightTint forState: UIControlStateHighlighted]; + [self.learnMoreIconButton setTintColor: kButtonHighlightTint]; + + [self.replayIconButton setImage: [UIImage imageNamed: @"applovin_card_replay"] forState: UIControlStateNormal]; + [self.learnMoreIconButton setImage: [UIImage imageNamed: @"applovin_card_learn_more"] forState: UIControlStateNormal]; + + [self.replayButton setTitle: kTextReplayVideo forState: UIControlStateNormal]; + [self.learnMoreButton setTitle: kTextLearnMore forState: UIControlStateNormal]; + } + return self; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + self.overlay.frame = self.bounds; + + const CGFloat replayButtonWidth = [self.replayButton sizeThatFits: CGSizeMake(CGFLOAT_MAX, 0.0f)].width; + const CGFloat totalReplayWidth = kPlayReplayWidth + kPadding + replayButtonWidth; + + const CGFloat learnMoreButtonWidth = [self.learnMoreButton sizeThatFits: CGSizeMake(CGFLOAT_MAX, 0.0f)].width; + const CGFloat totalLearnMoreWidth = kPlayReplayWidth + kPadding + learnMoreButtonWidth; + + const CGFloat totalContentHeight = (2*kPlayReplayHeight) + kPadding; + + // We will center and align depending on which button has the longer total width + const CGFloat longerWidth = totalReplayWidth >= totalLearnMoreWidth ? totalReplayWidth : totalLearnMoreWidth; + + self.replayIconButton.frame = CGRectMake(CGRectGetMidX(self.frame) - longerWidth/2.0f, + CGRectGetMidY(self.frame) - totalContentHeight/2.0f, + kPlayReplayWidth, + kPlayReplayHeight); + self.replayButton.frame = CGRectMake(CGRectGetMaxX(self.replayIconButton.frame) + kPadding, + CGRectGetMinY(self.replayIconButton.frame), + replayButtonWidth, + kPlayReplayHeight); + + self.learnMoreIconButton.frame = CGRectMake(CGRectGetMidX(self.frame) - longerWidth/2.0f, + CGRectGetMaxY(self.replayIconButton.frame) + kPadding, + kPlayReplayWidth, + kPlayReplayHeight); + self.learnMoreButton.frame = CGRectMake(CGRectGetMaxX(self.learnMoreIconButton.frame) + kPadding, + CGRectGetMaxY(self.replayIconButton.frame) + kPadding, + learnMoreButtonWidth, + kPlayReplayHeight); +} + +@end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselView.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselView.h new file mode 100644 index 0000000000..ba438deb10 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselView.h @@ -0,0 +1,34 @@ +// +// ALCarouselView.h +// +// Created by Thomas So on 3/30/15. +// Copyright (c) 2015, AppLovin Corporation. All rights reserved. +// + +@import AppLovinSDK; + +NS_ASSUME_NONNULL_BEGIN + +/** + * This class is used to display native ads to the user. + */ +@interface ALCarouselView : UIView + +/** + * An object conforming to the ALNativeAdGroupLoadDelegate protocol, which, if set, will be notified of ad load events. + */ +@property (weak, nonatomic, nullable) id loadDelegate; + +/** + * The current native ad(s) being displayed. + */ +@property (strong, nonatomic, readonly) NSArray *nativeAds; + +- (instancetype)initWithFrame:(CGRect)frame; +- (instancetype)initWithFrame:(CGRect)frame sdk:(ALSdk *)sdk; +- (instancetype)initWithFrame:(CGRect)frame sdk:(ALSdk *)sdk nativeAds:(NSArray *)nativeAds; +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselView.m b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselView.m new file mode 100644 index 0000000000..4a77221daa --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALCarouselView.m @@ -0,0 +1,643 @@ +// +// ALCarouselView.m +// +// Created by Thomas So on 3/30/15. +// Copyright (c) 2015, AppLovin Corporation. All rights reserved. +// + +@import AppLovinSDK; +#import "ALCarouselView.h" +#import "ALCarouselView+Internal.h" +#import "UIView+ALActivityIndicator.h" +#import "ALCarouselCardView.h" +#import "ALCarouselCardState.h" +#import "ALCarouselViewModel.h" +#import "ALCarouselViewSettings.h" +#import "ALDebugLog.h" + +@class ALCarouselView; + +/** + * This class acts as an intermediary between pre-cache events of slots and which card to notify. + */ +@interface ALCarouselPrecacheRouter : NSObject + +@property (copy, nonatomic) NSString *tag; +@property (weak, nonatomic) ALCarouselView *carouselView; + +- (instancetype)initWithCarouselView:(ALCarouselView *)carouselView; + +@end + +@interface ALCarouselView() + +@property (strong, nonatomic) ALSdk *sdk; +@property (weak, nonatomic) ALPostbackService *postbackService; + +@property (weak, atomic) NSArray *previousNativeAdsRendered; +@property (strong, nonatomic) ALCarouselViewModel *carouselModel; +@property (assign, nonatomic) NSInteger currentAdIndex; + +@property (strong, nonatomic) ALCarouselPrecacheRouter *precacheRouter; + +// This array has 5 card objets that are used to render the ad +@property (strong, nonatomic) NSArray *cardViews; +@property (strong, nonatomic) UIView *contentView; +@property (strong, nonatomic) UIPanGestureRecognizer *panGesture; + +@end + +#pragma mark ALCarouselView + +@implementation ALCarouselView +static NSString * const TAG = @"ALCarouselView"; + +// Card constants +static NSInteger const kNumSideCards = 2; +static NSInteger const kNumCards = 5; +static NSInteger const kMidCardIndex = 2; + +#pragma mark - Initialization + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder: aDecoder]; + if ( self ) + { + [self baseInitWithSdk: [ALSdk shared]]; + } + return self; +} + +- (instancetype)init +{ + self = [super init]; + if (self) + { + self.sdk = [ALSdk shared]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + return [self initWithFrame: frame sdk: [ALSdk shared]]; +} + +- (instancetype)initWithFrame:(CGRect)frame sdk:(ALSdk *)sdk +{ + return [self initWithFrame: frame sdk: sdk nativeAds: @[]]; +} + +- (instancetype)initWithFrame:(CGRect)frame sdk:(ALSdk *)sdk nativeAds:(NSArray *)nativeAds +{ + self = [super initWithFrame: frame]; + if ( self ) + { + [self baseInitWithSdk: sdk]; + self.nativeAds = nativeAds; + } + return self; +} + +- (void)baseInitWithSdk:(ALSdk *)sdk +{ + self.sdk = sdk; + self.postbackService = sdk.postbackService; + + self.currentAdIndex = 0; + + self.precacheRouter = [[ALCarouselPrecacheRouter alloc] initWithCarouselView: self]; + + // Setup View + self.userInteractionEnabled = YES; + self.clipsToBounds = YES; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.backgroundColor = kCarouselBackgroundColor; + + self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; + self.panGesture.delegate = self; + self.panGesture.delaysTouchesBegan = NO; + self.panGesture.delaysTouchesEnded = NO; + [self addGestureRecognizer: self.panGesture]; + + self.contentView = [[UIView alloc] init]; + self.contentView.backgroundColor = kCarouselBackgroundColor; + [self addSubview: self.contentView]; + + NSMutableArray* tempCards = [NSMutableArray arrayWithCapacity: kNumCards]; + for ( NSInteger i = 0; i < kNumCards; ++i ) + { + ALCarouselCardView *cardView = [[ALCarouselCardView alloc] initWithSdk: sdk]; + cardView.backgroundColor = [UIColor clearColor]; + cardView.hidden = YES; + + + [self.contentView addSubview: cardView]; + + [tempCards addObject: cardView]; + } + + self.cardViews = [NSArray arrayWithArray: tempCards]; +} + +-(void) didMoveToSuperview +{ + [super didMoveToSuperview]; + + // If there are attached from initialization + if ( self.nativeAds.count > 0 ) + { + // All of the objects in the array are checked to be of proper type in setter. Render. + [self renderAdsIfNeeded]; + } + // If there isn't currently any native ad(s) attached, load one. + else + { + [self al_showActivityIndicator]; + + // AppLovin SDK has deprecated loading of multiple native ads + [self.sdk.nativeAdService loadNextAdAndNotify: self]; + //[self.sdk.nativeAdService loadNativeAdGroupOfCount: kNativeAdsToLoadCount andNotify: self]; + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + const CGFloat cardWidth = (CGRectGetWidth(self.bounds) * kCardWidthPercentage) - (2 * kCardMargin); + const CGFloat contentWidth = kNumCards * cardWidth; + + CGRect contentFrame = CGRectZero; + contentFrame.origin.x = CGRectGetMidX(self.bounds) - (contentWidth/2); + contentFrame.size.width = contentWidth; + contentFrame.size.height = CGRectGetHeight(self.bounds); + self.contentView.frame = contentFrame; + + CGFloat currentX = 0.0f; + for ( ALCarouselCardView *cardView in self.cardViews ) + { + cardView.frame = CGRectMake(currentX, 0.0f, cardWidth, CGRectGetHeight(self.bounds)); + currentX += cardWidth; + } + + // Activity Views from category + self.activityIndicatorOverlay.frame = self.bounds; + self.activityIndicator.center = self.activityIndicatorOverlay.center; +} + +#pragma mark - Native Ad Load Delegate + +- (void)nativeAdService:(ALNativeAdService *)service didLoadAds:(NSArray *)ads +{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + self.nativeAds = ads; + [self renderAdsIfNeeded]; + + @try + { + if ( [(id)self.loadDelegate respondsToSelector: @selector(nativeAdService:didLoadAds:)] ) + { + [self.loadDelegate nativeAdService: service didLoadAds: ads]; + } + } + @catch (NSException *exception) + { + ALLog(@"Unable to notify native ad load delegate because of exception: %@", exception); + } + }]; +} + +- (void)nativeAdService:(ALNativeAdService *)service didFailToLoadAdsWithError:(NSInteger)code +{ + ALLog(@"Native ad service did fail to load native ad with error: %ld", code); + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + @try + { + if ( [(id)self.loadDelegate respondsToSelector: @selector(nativeAdService:didFailToLoadAdsWithError:)] ) + { + [self.loadDelegate nativeAdService: service didFailToLoadAdsWithError: code]; + } + } + @catch (NSException *exception) + { + ALLog(@"Unable to notify native ad load delegate about failing to load because of exception: %@", exception); + } + }]; +} + +#pragma mark - View Rendering + +- (void)renderAdsIfNeeded +{ + if ( [self.nativeAds isEqual: self.previousNativeAdsRendered] ) + { + ALLog(@"Attempting to re-render native ad(s)"); + } + else + { + // Keep track of native ad(s) rendered to prevent re-rendering + self.previousNativeAdsRendered = self.nativeAds; + + self.carouselModel = [[ALCarouselViewModel alloc] initWithNativeAds: self.nativeAds]; + + self.panGesture.enabled = self.nativeAds.count > 1; // Don't allow swiping if only one card + + self.currentAdIndex = 0; + + [self refreshView]; + } +} + +- (void)refreshView +{ + ALLog(@"Begin refreshing carousel view for number of native ads: %lu", self.nativeAds.count); + + // Update/save states before refreshing each card + for ( ALCarouselCardView *cardView in self.cardViews ) + { + [cardView.mediaView setInactive]; + } + + for ( NSInteger cardIndex = 0; cardIndex < kNumCards ; ++cardIndex ) + { + ALLog(@"Begin refreshing for card at index %ld", cardIndex); + + ALCarouselCardView *cardView = self.cardViews[cardIndex]; + + // Determine what slot should be displaying in the card with index 'cardIndex' now + // Please Note: Will return an out-of-bounds if we're not supposed to render ad for card at this index + NSInteger adIndex = [self adIndexForCardIndex: cardIndex]; + + // If ad exists for this card view, render + if ( (adIndex >= 0) && (adIndex < self.nativeAds.count) ) + { + ALLog(@"Refreshing card view at index: %ld with slot at index: %ld", cardIndex, adIndex); + ALNativeAd *ad = self.nativeAds[adIndex]; + + ALCarouselCardState *cardState = [self.carouselModel cardStateAtNativeAdIndex: adIndex]; + cardState.currentlyActive = (cardIndex == kMidCardIndex); + + if ( [ad isImagePrecached] ) + { + ALLog(@"Card at index: %ld currently has its images pre-cached", cardIndex); + + [cardView renderViewForNativeAd: ad cardState: cardState]; + + // Only middle (active) card can be clicked on + cardView.userInteractionEnabled = cardState.currentlyActive; + cardView.hidden = NO; + + if ( cardState.currentlyActive ) + { + // Handle events when middle card is displayed + [cardView trackImpression]; + } + } + // If images are not pre-cached, then videos are not pre-cached as well + else + { + ALLog(@"Card at index: %ld is not pre-cached", cardIndex); + + [cardView clearView]; + [cardView al_showActivityIndicator]; + cardView.hidden = NO; + + if ( cardState.precaching ) + { + ALLog(@"Card at index: %ld is already currently pre-caching", cardIndex); + } + else + { + cardState.precaching = YES; + + ALLog(@"Begin pre-caching for card at index: %ld", cardIndex); + [self.sdk.nativeAdService precacheResourcesForNativeAd: ad andNotify: self.precacheRouter]; + } + } + } + // Slot does not exist for this card view, hide + else + { + ALLog(@"Hiding card at card index: %ld", cardIndex); + + [cardView clearView]; + cardView.hidden = YES; + + if ( cardIndex == kMidCardIndex ) + { + ALLog(@"Hiding middle card because of nil ad."); + } + } + } + + [self al_hideActivityIndicatorAnimated: YES]; + + ALLog(@"Finish refreshing carousel view"); +} + +- (void)clearView +{ + for ( ALCarouselCardView *cardView in self.cardViews ) + { + [cardView clearView]; + } + [self.carouselModel removeAllObjects]; +} + +#pragma mark - Overridden Getters/Setters + +- (void)setNativeAds:(NSArray * __nullable)nativeAds +{ + if ( self.nativeAds.count > 0 ) + { + // Check if array contains objects of the proper ad type + for ( id obj in self.nativeAds ) + { + if ( ![obj isKindOfClass: [ALNativeAd class]] ) + { + // Found an object of invalid type + ALLog(@"Found an object of invalid type (%@) in nativeAds", NSStringFromClass([obj class])); + + return; + } + } + } + else + { + // We clear the view if native ads is set to an empty array + ALLog(@"Setting native ads of count 0. Clearing view..."); + [self clearView]; + } + + _nativeAds = nativeAds; +} + +- (void)setCurrentAdIndex:(NSInteger)currentAdIndex +{ + if ( currentAdIndex < self.nativeAds.count ) + { + if ( self.currentAdIndex == currentAdIndex ) + { + ALLog(@"Setting same current ad index of %ld", currentAdIndex); + } + else + { + ALLog(@"Setting new current ad index of %ld", currentAdIndex); + + _currentAdIndex = currentAdIndex; + + [self refreshView]; + } + } + else + { + ALLog(@"Setting out-of-bounds index of %ld", currentAdIndex); + } +} + +#pragma mark - Utility + +- (NSInteger)adIndexForCardIndex:(NSUInteger)cardIndex +{ + return self.currentAdIndex + cardIndex - kNumSideCards; +} + +#pragma mark - Gesture Recognizers + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + if ( [gestureRecognizer isEqual: self.panGesture] ) + { + // Our pan gesture should only recognize horizontal pans + CGPoint translation = [self.panGesture velocityInView: self]; + return fabs(translation.y) < fabs(translation.x); + } + + return YES; +} + +- (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer +{ + switch ( recognizer.state ) + { + case UIGestureRecognizerStateChanged: + { + CGFloat xOffset = [recognizer translationInView: self].x; + self.contentView.frame = CGRectOffset(self.contentView.frame, xOffset, 0.0f); + + [recognizer setTranslation: CGPointZero inView: self]; + + break; + } + case UIGestureRecognizerStateEnded: + { + const CGFloat cardWidth = (CGRectGetWidth(self.bounds) * kCardWidthPercentage) - (2 * kCardMargin); + const CGFloat contentOffset = fabs(self.center.x - self.contentView.center.x); + const CGFloat percentageSwiped = 1.0f - (contentOffset/CGRectGetWidth(self.bounds)); + const CGFloat springDuration = percentageSwiped * kSpringDuration; + + const BOOL exceedsThreshold = contentOffset > kConfigSwipeThreshold; + const BOOL leftToRight = [recognizer velocityInView: self].x > 0.0f; + + // If user is swiping left to right + if ( leftToRight ) + { + // If the middle card has a slot to the left of it, then execute the swipe + if ( self.currentAdIndex >= 1 && exceedsThreshold ) + { + [UIView animateWithDuration: springDuration + delay: kDelay + usingSpringWithDamping: kSpringDampeningGoNextCard + initialSpringVelocity: kInitialSpringVelocity + options: UIViewAnimationOptionCurveEaseInOut + animations: ^{ + + self.contentView.center = CGPointMake( (CGRectGetWidth(self.frame)/2.0f) + cardWidth, self.contentView.center.y); + } + completion: ^(BOOL finished) { + + if ( finished ) + { + --self.currentAdIndex; + [self setNeedsLayout]; + } + }]; + } + // There are no slots to the left of the middle card anymore, spring back + else + { + [UIView animateWithDuration: kSpringDuration + delay: kDelay + usingSpringWithDamping: kSpringDampeningReturnSameCard + initialSpringVelocity: kInitialSpringVelocity + options: UIViewAnimationOptionCurveEaseInOut + animations: ^{ + + self.contentView.center = CGPointMake(0.5f * CGRectGetWidth( self.frame ), self.contentView.center.y); + } + completion:nil]; + } + } + // We are swiping right to left + else + { + // If there is a slot to the right of the middle card, execute the swipe + if ( self.currentAdIndex < [self.carouselModel nativeAdsCount]-1 && exceedsThreshold ) + { + [UIView animateWithDuration: springDuration + delay: kDelay + usingSpringWithDamping: kSpringDampeningGoNextCard + initialSpringVelocity: kInitialSpringVelocity + options: UIViewAnimationOptionCurveEaseInOut + animations: ^{ + + self.contentView.center = CGPointMake( (CGRectGetWidth(self.frame)/2.0f) - cardWidth, self.contentView.center.y); + } + completion:^(BOOL finished) { + + if ( finished ) + { + ++self.currentAdIndex; + [self setNeedsLayout]; + } + }]; + } + // There are no slots to the right of the middle card anymore. Spring back + else + { + [UIView animateWithDuration: kSpringDuration + delay: kDelay + usingSpringWithDamping: kSpringDampeningReturnSameCard + initialSpringVelocity: kInitialSpringVelocity + options: UIViewAnimationOptionCurveEaseInOut + animations: ^{ + + self.contentView.center = CGPointMake(0.5f * CGRectGetWidth( self.frame ), self.contentView.center.y); + } + completion:nil]; + } + } + break; + } + default: + { + break; + } + } +} + +@end + +#pragma mark ALCarouselPrecacheRouter + +@implementation ALCarouselPrecacheRouter + +#pragma mark - Initialization + +- (instancetype)initWithCarouselView:(ALCarouselView *)carouselView +{ + self = [super init]; + + if ( self ) + { + self.tag = @"ALCarouselPrecacheRouter"; + self.carouselView = carouselView; + } + + return self; +} + +#pragma mark - Precache Delegate Methods + +- (void)nativeAdService:(ALNativeAdService *)service didPrecacheImagesForAd:(ALNativeAd *)ad +{ + ALLog(@"Finished pre-caching images for slot (%@). Rendering...", ad.adIdNumber); + + const NSUInteger index = [self.carouselView.nativeAds indexOfObject: ad]; + + if ( index != NSNotFound ) + { + const NSInteger cardIndex = [self cardIndexForAdIndex: index]; + ALCarouselCardView* cardView = self.carouselView.cardViews[cardIndex]; + ALCarouselViewModel* model = self.carouselView.carouselModel; + ALCarouselCardState* cardState = [model cardStateAtNativeAdIndex: index]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [cardView renderViewForNativeAd: ad cardState: cardState]; + + if (cardIndex == kMidCardIndex) { + [cardView trackImpression]; + } + + cardView.hidden = NO; + }]; + } + else + { + ALLog(@"Finished pre-caching images for ad (%@). Card is not on screen, stashing...", ad.adIdNumber); + } +} + +- (void)nativeAdService:(ALNativeAdService *)service didPrecacheVideoForAd:(ALNativeAd *)ad +{ + if ( ad.videoURL ) + { + const NSUInteger index = [self.carouselView.nativeAds indexOfObject: ad]; + ALCarouselCardState *cardState = [self.carouselView.carouselModel cardStateAtNativeAdIndex: index]; + + // If video is loaded for a currently active ad, render the slot + if ( index != NSNotFound && cardState.currentlyActive ) + { + ALLog(@"Finished pre-caching for slot (%@) with valid video in active card", ad.adIdNumber); + + const NSInteger cardIndex = [self cardIndexForAdIndex: index]; + ALCarouselCardView *cardView = self.carouselView.cardViews[cardIndex]; + + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + + [cardView.mediaView renderViewForNativeAd: ad cardState: cardState]; + }]; + } + else + { + ALLog(@"Finished pre-caching video for ad (%@). Card is not on screen, stashing...", ad.adIdNumber); + } + } + else + { + // Finished pre-caching for slot without video + ALLog(@"Finished pre-caching for ad (%@) without video", ad.adIdNumber); + } + + [self.carouselView.carouselModel cardStateForNativeAd: ad].precaching = NO; +} + +- (void)nativeAdService:(ALNativeAdService *)service didFailToPrecacheImagesForAd:(ALNativeAd *)ad withError:(NSInteger)errorCode +{ + // Have activity indicator remain on card + [self.carouselView.carouselModel cardStateForNativeAd: ad].precaching = YES; + + ALLog(@"Failed to precache images for ad (%@) with error code: %ld", ad.adIdNumber, errorCode); +} + +- (void)nativeAdService:(ALNativeAdService *)service didFailToPrecacheVideoForAd:(ALNativeAd *)ad withError:(NSInteger)errorCode +{ + // If the slot already has its images pre-cached, it means video failed to pre-cache. Just ignore that + [self.carouselView.carouselModel cardStateForNativeAd: ad].precaching = NO; + + ALLog(@"Failed to precache video for ad (%@) with error code: %ld", ad.adIdNumber, errorCode); +} + +#pragma mark - Utility + +- (NSUInteger)cardIndexForAdIndex:(NSUInteger)slotIndex +{ + return kMidCardIndex + slotIndex - self.carouselView.currentAdIndex; +} + +@end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALNativeAdVideoView.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALNativeAdVideoView.h new file mode 100644 index 0000000000..fd5806828b --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALNativeAdVideoView.h @@ -0,0 +1,23 @@ +// +// ALVideoView.h +// sdk +// +// Created by Matt Szaro on 6/23/14. +// +// + +@import AppLovinSDK; +@import AVFoundation; + +NS_ASSUME_NONNULL_BEGIN + +@interface ALNativeAdVideoView : UIView + +@property (strong, nonatomic, readonly) AVPlayer *player; +@property (strong, nonatomic, readonly) AVPlayerLayer* playerLayer; + +- (instancetype)initWithPlayer:(AVPlayer *)aPlayer; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALNativeAdVideoView.m b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALNativeAdVideoView.m new file mode 100644 index 0000000000..279a6321fe --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /Carousel UI/Views/ALNativeAdVideoView.m @@ -0,0 +1,48 @@ +// +// ALVideoView.m +// sdk +// +// Created by Matt Szaro on 6/23/14. +// +// + +#import "ALNativeAdVideoView.h" + +@interface ALNativeAdVideoView() +@property (strong, nonatomic, readwrite) AVPlayer *player; +@end + +@implementation ALNativeAdVideoView +@dynamic player, playerLayer; + +-(instancetype) initWithPlayer:(AVPlayer *)aPlayer +{ + self = [super init]; + if(self) + { + self.player = aPlayer; + } + return self; +} + ++(Class) layerClass +{ + return [AVPlayerLayer class]; +} + +-(AVPlayerLayer*) playerLayer +{ + return (AVPlayerLayer*) self.layer; +} + +-(AVPlayer*) player +{ + return self.playerLayer.player; +} + +-(void) setPlayer: (AVPlayer *) aPlayer +{ + self.playerLayer.player = aPlayer; +} + +@end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoArticle.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoArticle.h new file mode 100644 index 0000000000..2b720eab1e --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoArticle.h @@ -0,0 +1,25 @@ +// +// ALDemoArticle.h +// iOS-SDK-Demo +// +// Created by Thomas So on 11/12/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ALDemoArticle : NSObject + +@property (nonatomic, copy) NSString *title; +@property (nonatomic, copy) NSString *pubDate; +@property (nonatomic, copy) NSString *creator; +@property (nonatomic, copy) NSString *articleDescription; +@property (nonatomic, strong) NSURL *link; + +@property (nonatomic, assign) BOOL isAd; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoArticle.m b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoArticle.m new file mode 100644 index 0000000000..2b24785788 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoArticle.m @@ -0,0 +1,13 @@ +// +// ALDemoArticle.m +// iOS-SDK-Demo +// +// Created by Thomas So on 11/12/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALDemoArticle.h" + +@implementation ALDemoArticle + +@end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoRSSFeedRetriever.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoRSSFeedRetriever.h new file mode 100644 index 0000000000..0b76784375 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoRSSFeedRetriever.h @@ -0,0 +1,23 @@ +// +// ALDemoRSSFeedRetriever.h +// iOS-SDK-Demo +// +// Created by Thomas So on 11/12/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import +#import "ALDemoArticle.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ALDemoRSSFeedRetriever : NSObject + +typedef void(^ALDemoRSSFeedRetrieverBlock)(NSError *__nullable error, NSArray *articles); + ++ (ALDemoRSSFeedRetriever *)sharedRetriever; +- (void)startParsingWithCompletion:(ALDemoRSSFeedRetrieverBlock)completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoRSSFeedRetriever.m b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoRSSFeedRetriever.m new file mode 100644 index 0000000000..5d039dfefb --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Feed /RSS Feed Parsing/ALDemoRSSFeedRetriever.m @@ -0,0 +1,121 @@ +// +// ALDemoRSSFeedRetriever.m +// iOS-SDK-Demo +// +// Created by Thomas So on 11/12/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +#import "ALDemoRSSFeedRetriever.h" + +@interface ALDemoRSSFeedRetriever() +@property (nonatomic, copy, nullable) NSString *currentElementName; +@property (nonatomic, strong, nullable) ALDemoArticle *currentArticle; + +@property (nonatomic, strong) NSMutableArray *articles; +@property (nonatomic, copy) ALDemoRSSFeedRetrieverBlock completionBlock; +@end + +@implementation ALDemoRSSFeedRetriever +static NSString *const kRSSFeedURL = @"https://blog.applovin.com/feed/"; + ++ (ALDemoRSSFeedRetriever *)sharedRetriever +{ + static dispatch_once_t pred; + static ALDemoRSSFeedRetriever *manager = nil; + dispatch_once(&pred, ^{ + manager = [[self alloc] init]; + }); + return manager; +} + +- (instancetype)init +{ + self = [super init]; + if ( self ) + { + self.articles = [NSMutableArray array]; + } + return self; +} + +- (void)startParsingWithCompletion:(ALDemoRSSFeedRetrieverBlock)completion +{ + // Do not retrieve if we already have retrieved some artivcles, just call completion + if ( self.articles.count > 0 ) + { + completion( nil, [NSArray arrayWithArray: self.articles] ); + } + else + { + self.completionBlock = completion; + + NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL: [NSURL URLWithString: kRSSFeedURL]]; + [parser setDelegate: self]; + [parser parse]; + } +} + +#pragma mark - Parser Delegate + +- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict +{ + self.currentElementName = elementName; + if ( [elementName isEqualToString: @"item"] ) + { + // Start of the parsing of a new article + + if ( self.articles.count == 3 ) + { + // Lets place an ad in the 4th slot + ALDemoArticle *adFauxArticle = [[ALDemoArticle alloc] init]; + adFauxArticle.isAd = YES; + [self.articles addObject: adFauxArticle]; + } + + self.currentArticle = [[ALDemoArticle alloc] init]; + [self.articles addObject: self.currentArticle]; + } +} + +- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string +{ + if ( self.currentArticle ) + { + if ( [self.currentElementName isEqualToString: @"title"] ) + { + self.currentArticle.title = string; + } + else if ( [self.currentElementName isEqualToString: @"link"] ) + { + self.currentArticle.link = [NSURL URLWithString: string]; + } + else if ( [self.currentElementName isEqualToString: @"pubDate"] ) + { + self.currentArticle.pubDate = [string substringToIndex: 11]; + } + else if ( [self.currentElementName isEqualToString: @"dc:creator"] ) + { + self.currentArticle.creator = string; + } + else if ( [self.currentElementName isEqualToString: @"description"] ) + { + self.currentArticle.articleDescription = [string stringByReplacingOccurrencesOfString: @"[…]" withString: @"..."]; + } + + // Reset current element name after we finish setting it + self.currentElementName = @""; + } +} + +- (void)parserDidEndDocument:(NSXMLParser *)parser +{ + self.completionBlock( nil, [NSArray arrayWithArray: self.articles] ); +} + +- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError +{ + self.completionBlock( parseError, @[] ); +} + +@end diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Programattic/ALDemoNativeAdProgrammaticViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Programattic/ALDemoNativeAdProgrammaticViewController.swift new file mode 100644 index 0000000000..fb37384cee --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Native/Programattic/ALDemoNativeAdProgrammaticViewController.swift @@ -0,0 +1,193 @@ +// +// ALDemoNativeAdProgrammaticViewController.swift +// iOS-SDK-Demo +// +// Created by Thomas So on 9/25/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +import UIKit + +// Additional documentation - https://applovin.com/integration#iosNative + +class ALDemoNativeAdProgrammaticViewController : ALBaseAdViewController +{ + @IBOutlet weak var precacheButton: UIBarButtonItem! + @IBOutlet weak var showButton: UIBarButtonItem! + + @IBOutlet weak var impressionStatusLabel: UILabel! + + @IBOutlet weak var appIcon: UIImageView! + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var rating: UIImageView! + @IBOutlet weak var descriptionLabel: UILabel! + @IBOutlet weak var mediaView: ALCarouselMediaView! + @IBOutlet weak var ctaButton: UIButton! + + var nativeAd: ALNativeAd? + + override func viewDidLoad() + { + super.viewDidLoad() + + appIcon.layer.masksToBounds = true + appIcon.layer.cornerRadius = 3 + + ctaButton.layer.masksToBounds = true + ctaButton.layer.cornerRadius = 3 + + setUIElementsHidden(true) + } + + // MARK: Action Methods + + @IBAction func loadNativeAd(_ sender: AnyObject!) + { + logCallback() + + precacheButton.isEnabled = false + showButton.isEnabled = false + + impressionStatusLabel.text = "No impression to track" + + ALSdk.shared()!.nativeAdService.loadNextAdAndNotify(self) + } + + @IBAction func precacheNativeAd(_ sender: AnyObject!) + { + // You can use our pre-caching to retrieve assets (app icon, ad image, ad video) locally. OR you can do it with your preferred caching framework. + // iconURL, imageURL, videoURL needs to be retrieved manually before you can render them + + logCallback() + + if let ad = nativeAd + { + ALSdk.shared()!.nativeAdService.precacheResources(for: ad, andNotify: self) + } + } + + @IBAction func showNativeAd(_ sender: AnyObject!) + { + logCallback() + + if let ad = nativeAd, let iconURL = ad.iconURL + { + if let imageData = try? Data(contentsOf: iconURL) + { + appIcon.image = UIImage(data: imageData ) // Local URL + } + + titleLabel.text = ad.title + descriptionLabel.text = ad.descriptionText + ctaButton.setTitle(ad.ctaText, for: .normal) + + let starFilename = "Star_Sprite_\(String(describing: ad.starRating?.stringValue))" + rating.image = UIImage(named: starFilename) + + // NOTE - Videos have aspect ratio of 1:1.85 + mediaView.renderView(for: ad) + + setUIElementsHidden(false) + + // + // You are responsible for firing all necessary postback URLs + // + trackImpression(ad) + + view.layoutIfNeeded() + } + } + + @IBAction func ctaPressed(_ sender: AnyObject!) + { + nativeAd?.launchClickTarget() + } + + func trackImpression(_ ad: ALNativeAd!) + { + // Callbacks may not happen on main queue + DispatchQueue.main.async { + ad.trackImpressionAndNotify(self) + } + } + + func setUIElementsHidden(_ hidden: Bool) + { + appIcon.isHidden = hidden + titleLabel.isHidden = hidden + rating.isHidden = hidden + descriptionLabel.isHidden = hidden + mediaView.isHidden = hidden + ctaButton.isHidden = hidden + } +} + +extension ALDemoNativeAdProgrammaticViewController : ALPostbackDelegate +{ + func postbackService(_ postbackService: ALPostbackService, didExecutePostback postbackURL: URL) + { + // Callbacks may not happen on main queue + DispatchQueue.main.async { + // Impression tracked! + self.impressionStatusLabel.text = "Impression tracked" + } + } + + func postbackService(_ postbackService: ALPostbackService, didFailToExecutePostback postbackURL: URL?, errorCode: Int) + { + // Callbacks may not happen on main queue + DispatchQueue.main.async { + // Impression could not be tracked. Retry the postback later. + self.impressionStatusLabel.text = "Impression failed to track with error code \(errorCode)" + } + } +} + +extension ALDemoNativeAdProgrammaticViewController : ALNativeAdLoadDelegate +{ + func nativeAdService(_ service: ALNativeAdService, didLoadAds ads: [Any]) + { + logCallback() + + // Callbacks may not happen on main queue + DispatchQueue.main.async { + self.nativeAd = ads.first as? ALNativeAd + self.precacheButton.isEnabled = true + } + } + + func nativeAdService(_ service: ALNativeAdService, didFailToLoadAdsWithError code: Int) + { + logCallback() + } +} + +extension ALDemoNativeAdProgrammaticViewController : ALNativeAdPrecacheDelegate +{ + func nativeAdService(_ service: ALNativeAdService, didPrecacheImagesFor ad: ALNativeAd) + { + logCallback() + } + + func nativeAdService(_ service: ALNativeAdService, didPrecacheVideoFor ad: ALNativeAd) + { + // This delegate method will get called whether an ad actually has a video to precache or not + logCallback() + + // Callbacks may not happen on main queue + DispatchQueue.main.async { + self.showButton.isEnabled = true + self.precacheButton.isEnabled = false + } + } + + func nativeAdService(_ service: ALNativeAdService, didFailToPrecacheImagesFor ad: ALNativeAd, withError errorCode: Int) + { + logCallback() + } + + func nativeAdService(_ service: ALNativeAdService, didFailToPrecacheVideoFor ad: ALNativeAd, withError errorCode: Int) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Rewarded/ALDemoRewardedVideosViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Rewarded/ALDemoRewardedVideosViewController.swift new file mode 100644 index 0000000000..6f4c6a4618 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Rewarded/ALDemoRewardedVideosViewController.swift @@ -0,0 +1,141 @@ +// +// ALDemoRewardedVideosViewController.swift +// iOS-SDK-Demo +// +// Created by Thomas So on 9/25/15. +// Copyright © 2015 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoRewardedVideosViewController : ALBaseAdViewController +{ + private let rewardedAd = ALIncentivizedInterstitialAd.shared() + + @IBAction func showRewardedVideo() + { + // You need to preload each rewarded video before it can be displayed + if ALIncentivizedInterstitialAd.shared().isReadyForDisplay + { + // Optional: Assign delegates + rewardedAd.adDisplayDelegate = self + rewardedAd.adVideoPlaybackDelegate = self + + rewardedAd.showAndNotify(self) + } + else + { + preloadRewardedVideo() + } + } + + // You need to preload each rewarded video before it can be displayed + @IBAction func preloadRewardedVideo() + { + logCallback() + rewardedAd.preloadAndNotify(self) + } +} + +extension ALDemoRewardedVideosViewController : ALAdLoadDelegate +{ + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + // Look at ALErrorCodes.h for list of error codes + logCallback() + } +} + +extension ALDemoRewardedVideosViewController : ALAdRewardDelegate +{ + func rewardValidationRequest(for ad: ALAd, didSucceedWithResponse response: [AnyHashable: Any]) + { + /** + * AppLovin servers validated the reward. Refresh user balance from your server. We will also pass the number of coins + * awarded and the name of the currency. However, ideally, you should verify this with your server before granting it. + */ + + // "current" - "Coins", "Gold", whatever you set in the dashboard. + // "amount" - "5" or "5.00" if you've specified an amount in the UI. + if let amount = response["amount"] as? NSString, let currencyName = response["currency"] as? NSString + { + logCallback() + } + } + + func rewardValidationRequest(for ad: ALAd, didFailWithError responseCode: Int) + { + if responseCode == kALErrorCodeIncentivizedUserClosedVideo + { + // Your user exited the video prematurely. It's up to you if you'd still like to grant + // a reward in this case. Most developers choose not to. Note that this case can occur + // after a reward was initially granted (since reward validation happens as soon as a + // video is launched). + } + else if responseCode == kALErrorCodeIncentivizedValidationNetworkTimeout || responseCode == kALErrorCodeIncentivizedUnknownServerError + { + // Some server issue happened here. Don't grant a reward. By default we'll show the user + // a UIAlertView telling them to try again later, but you can change this in the + // Manage Apps UI. + } + else if responseCode == kALErrorCodeIncentiviziedAdNotPreloaded + { + // Indicates that you called for a rewarded video before one was available. + } + + logCallback() + } + + func rewardValidationRequest(for ad: ALAd, didExceedQuotaWithResponse response: [AnyHashable: Any]) + { + // Your user has already earned the max amount you allowed for the day at this point, so + // don't give them any more money. By default we'll show them a UIAlertView explaining this, + // though you can change that from the Manage Apps UI. + logCallback() + } + + func rewardValidationRequest(for ad: ALAd, wasRejectedWithResponse response: [AnyHashable: Any]) + { + // Your user couldn't be granted a reward for this view. This could happen if you've blacklisted + // them, for example. Don't grant them any currency. By default we'll show them a UIAlertView explaining this, + // though you can change that from the Manage Apps UI. + logCallback() + } +} + +extension ALDemoRewardedVideosViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoRewardedVideosViewController : ALAdVideoPlaybackDelegate +{ + func videoPlaybackBegan(in ad: ALAd) + { + logCallback() + } + + func videoPlaybackEnded(in ad: ALAd, atPlaybackPercent percentPlayed: NSNumber, fullyWatched wasFullyWatched: Bool) + { + logCallback() + } +} diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Rewarded/ALDemoRewardedVideosZoneViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Rewarded/ALDemoRewardedVideosZoneViewController.swift new file mode 100644 index 0000000000..6bedb9ccb4 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/AppLovin/Rewarded/ALDemoRewardedVideosZoneViewController.swift @@ -0,0 +1,146 @@ +// +// ALDemoRewardedVideosZoneViewController.swift +// iOS-SDK-Demo-Swift +// +// Created by Suyash Saxena on 6/19/18. +// Copyright © 2018 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK + +class ALDemoRewardedVideosZoneViewController : ALBaseAdViewController +{ + private var incentivizedInterstitial: ALIncentivizedInterstitialAd! + + override func viewDidLoad() + { + super.viewDidLoad(); + + incentivizedInterstitial = ALIncentivizedInterstitialAd(zoneIdentifier: "YOUR_ZONE_ID") + } + + @IBAction func showRewardedVideo() + { + // You need to preload each rewarded video before it can be displayed + if incentivizedInterstitial.isReadyForDisplay + { + incentivizedInterstitial.showAndNotify(self) + } + else + { + preloadRewardedVideo() + } + } + + // You need to preload each rewarded video before it can be displayed + @IBAction func preloadRewardedVideo() + { + logCallback() + incentivizedInterstitial.preloadAndNotify(self) + } +} + +extension ALDemoRewardedVideosZoneViewController : ALAdLoadDelegate +{ + // MARK: Ad Load Delegate + + func adService(_ adService: ALAdService, didLoad ad: ALAd) + { + logCallback() + } + + func adService(_ adService: ALAdService, didFailToLoadAdWithError code: Int32) + { + // Look at ALErrorCodes.h for list of error codes + logCallback() + } +} + +extension ALDemoRewardedVideosZoneViewController : ALAdRewardDelegate +{ + func rewardValidationRequest(for ad: ALAd, didSucceedWithResponse response: [AnyHashable: Any]) + { + /** + * AppLovin servers validated the reward. Refresh user balance from your server. We will also pass the number of coins + * awarded and the name of the currency. However, ideally, you should verify this with your server before granting it. + */ + + // "current" - "Coins", "Gold", whatever you set in the dashboard. + // "amount" - "5" or "5.00" if you've specified an amount in the UI. + if let amount = response["amount"] as? NSString, let currencyName = response["currency"] as? NSString + { + logCallback() + } + } + + func rewardValidationRequest(for ad: ALAd, didFailWithError responseCode: Int) + { + if responseCode == kALErrorCodeIncentivizedUserClosedVideo + { + // Your user exited the video prematurely. It's up to you if you'd still like to grant + // a reward in this case. Most developers choose not to. Note that this case can occur + // after a reward was initially granted (since reward validation happens as soon as a + // video is launched). + } + else if responseCode == kALErrorCodeIncentivizedValidationNetworkTimeout || responseCode == kALErrorCodeIncentivizedUnknownServerError + { + // Some server issue happened here. Don't grant a reward. By default we'll show the user + // a UIAlertView telling them to try again later, but you can change this in the + // Manage Apps UI. + } + else if responseCode == kALErrorCodeIncentiviziedAdNotPreloaded + { + // Indicates that you called for a rewarded video before one was available. + } + + logCallback() + } + + func rewardValidationRequest(for ad: ALAd, didExceedQuotaWithResponse response: [AnyHashable: Any]) + { + // Your user has already earned the max amount you allowed for the day at this point, so + // don't give them any more money. By default we'll show them a UIAlertView explaining this, + // though you can change that from the Manage Apps UI. + logCallback() + } + + func rewardValidationRequest(for ad: ALAd, wasRejectedWithResponse response: [AnyHashable: Any]) + { + // Your user couldn't be granted a reward for this view. This could happen if you've blacklisted + // them, for example. Don't grant them any currency. By default we'll show them a UIAlertView explaining this, + // though you can change that from the Manage Apps UI. + logCallback() + } +} + +extension ALDemoRewardedVideosZoneViewController : ALAdDisplayDelegate +{ + func ad(_ ad: ALAd, wasDisplayedIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasHiddenIn view: UIView) + { + logCallback() + } + + func ad(_ ad: ALAd, wasClickedIn view: UIView) + { + logCallback() + } +} + +extension ALDemoRewardedVideosZoneViewController : ALAdVideoPlaybackDelegate +{ + func videoPlaybackBegan(in ad: ALAd) + { + logCallback() + } + + func videoPlaybackEnded(in ad: ALAd, atPlaybackPercent percentPlayed: NSNumber, fullyWatched wasFullyWatched: Bool) + { + logCallback() + } +} diff --git a/DemoApp-Swift/DemoApp-Swift/ALBaseAdViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Base Classes/ALBaseAdViewController.swift similarity index 73% rename from DemoApp-Swift/DemoApp-Swift/ALBaseAdViewController.swift rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Base Classes/ALBaseAdViewController.swift index b1826693e1..59b03dcc7b 100644 --- a/DemoApp-Swift/DemoApp-Swift/ALBaseAdViewController.swift +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Base Classes/ALBaseAdViewController.swift @@ -15,6 +15,18 @@ class ALBaseAdViewController: UIViewController private var callbacks: [String] = [] + override func viewDidAppear(_ animated: Bool) + { + super.viewDidAppear(animated) + self.navigationController?.setToolbarHidden(self.hidesBottomBarWhenPushed, animated: true) + } + + override func viewWillDisappear(_ animated: Bool) + { + self.navigationController?.setToolbarHidden(true, animated: false) + super.viewWillDisappear(animated) + } + internal func logCallback(functionName: String = #function) { callbacks.append(functionName) diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Base Classes/ALHomeViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Base Classes/ALHomeViewController.swift new file mode 100644 index 0000000000..85a1ab4edd --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Base Classes/ALHomeViewController.swift @@ -0,0 +1,179 @@ +// +// ALHomeViewController.swift +// DemoApp-Swift +// +// Created by Andrew Tian on 9/20/19. +// Copyright © 2019 AppLovin. All rights reserved. +// + +import UIKit +import AppLovinSDK +import MessageUI +import SafariServices + +class ALHomeViewController: UITableViewController, MFMailComposeViewControllerDelegate +{ + let kSupportEmail = "support@applovin.com" + let kSupportLink = "https://support.applovin.com/support/home" + + let kRowIndexToHideForPhones = 3; + + @IBOutlet var muteToggle: UIBarButtonItem! + @IBOutlet weak var mediationDebuggerCell: UITableViewCell! + + override func viewDidLoad() + { + super.viewDidLoad() + self.navigationController?.setToolbarHidden(self.hidesBottomBarWhenPushed, animated: true) + addFooterLabel() + muteToggle.image = muteIconForCurrentSdkMuteSetting() + } + + override func viewWillDisappear(_ animated: Bool) + { + self.navigationController?.setToolbarHidden(true, animated: false) + super.viewWillDisappear(animated) + } + + // MARK: Table View Delegate + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) + { + tableView.deselectRow(at: indexPath, animated: true) + + if tableView.cellForRow(at: indexPath) == mediationDebuggerCell + { + ALSdk.shared()!.showMediationDebugger() + } + + if indexPath.section == 2 + { + if indexPath.row == 0 + { + openSupportSite() + } + else if indexPath.row == 1 + { + attemptSendEmail() + } + } + } + + override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) + { + if UIDevice.current.userInterfaceIdiom == .phone && indexPath.section == 0 && indexPath.row == kRowIndexToHideForPhones + { + cell.isHidden = true; + } + } + + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat + { + if UIDevice.current.userInterfaceIdiom == .phone && indexPath.section == 0 && indexPath.row == kRowIndexToHideForPhones + { + return 0; + } + + return super.tableView(tableView, heightForRowAt: indexPath) + } + + func addFooterLabel() + { + let footer = UILabel() + footer.font = UIFont.systemFont(ofSize: 14) + footer.numberOfLines = 0 + + let appVersion = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String + let sdkVersion = ALSdk.version() + let systemVersion = UIDevice.current.systemVersion + let text = "App Version: \(appVersion)\nSDK Version: \(sdkVersion)\niOS Version: \(systemVersion)\n\nLanguage: Swift" + + let style = NSMutableParagraphStyle() + style.alignment = .center + style.minimumLineHeight = 20 + footer.attributedText = NSAttributedString(string: text, attributes: [NSAttributedString.Key.paragraphStyle : style]) + + var frame = footer.frame + frame.size.height = footer.sizeThatFits(CGSize(width: footer.frame.width, height: CGFloat.greatestFiniteMagnitude)).height + 60 + footer.frame = frame + tableView.tableFooterView = footer + } + + // MARK: Sound Toggling + + @IBAction func toggleMute(_ sender: UIBarButtonItem!) + { + /** + * Toggling the sdk mute setting will affect whether your video ads begin in a muted state or not. + */ + let sdk = ALSdk.shared() + sdk?.settings.muted = !(sdk?.settings.muted)! + sender.image = muteIconForCurrentSdkMuteSetting() + } + + func muteIconForCurrentSdkMuteSetting() -> UIImage! + { + return ALSdk.shared()!.settings.muted ? UIImage(named: "mute") : UIImage(named: "unmute") + } + + // MARK: Table View Actions + + func openSupportSite() + { + guard let supportURL = URL(string: kSupportLink) else { return } + + if #available(iOS 9.0, *) + { + let safariController = SFSafariViewController(url: supportURL, entersReaderIfAvailable: true) + present(safariController, animated: true, completion: { + UIApplication.shared.statusBarStyle = .default + }) + } + else + { + UIApplication.shared.openURL(supportURL) + } + } + + func attemptSendEmail() + { + if MFMailComposeViewController.canSendMail() + { + let mailController = MFMailComposeViewController() + mailController.mailComposeDelegate = self + mailController.setSubject("iOS SDK Support") + mailController.setToRecipients([kSupportEmail]) + mailController.setMessageBody("\n\n---\nSDK Version: \(ALSdk.version())", isHTML: false) + mailController.navigationBar.tintColor = UIColor.white + + present(mailController, animated: true, completion: { + UIApplication.shared.statusBarStyle = .lightContent + }) + } + else + { + let message = "Your device is not configured for sending emails.\n\nPlease send emails to \(kSupportEmail)" + let alertVC = UIAlertController(title: "Email Unavailable", message: message, preferredStyle: .alert) + let okAction = UIAlertAction(title: "OK", style: .cancel) + alertVC.addAction(okAction) + present(alertVC, animated: true) + } + } + + func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) + { + switch ( result.rawValue ) + { + case ( MFMailComposeResult.sent.rawValue ): + let alertVC = UIAlertController(title: "Email Sent", message: "Thank you for your email, we will process it as soon as possible.", preferredStyle: .alert) + let okAction = UIAlertAction(title: "OK", style: .cancel) + alertVC.addAction(okAction) + present(alertVC, animated: true) + default: + break + } + + dismiss(animated: true, completion: nil) + } +} + diff --git a/DemoApp-Swift/DemoApp-Swift/Ads/ALAutoLayoutBannerAdViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.swift similarity index 96% rename from DemoApp-Swift/DemoApp-Swift/Ads/ALAutoLayoutBannerAdViewController.swift rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.swift index 6489c44d03..2ccb0c9f69 100644 --- a/DemoApp-Swift/DemoApp-Swift/Ads/ALAutoLayoutBannerAdViewController.swift +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXAutoLayoutBannerAdViewController.swift @@ -9,7 +9,7 @@ import UIKit import AppLovinSDK -class ALAutoLayoutBannerAdViewController: ALBaseAdViewController, MAAdViewAdDelegate +class ALMAXAutoLayoutBannerAdViewController: ALBaseAdViewController, MAAdViewAdDelegate { private let adView = MAAdView(adUnitIdentifier: "YOUR_AD_UNIT_ID") diff --git a/DemoApp-Swift/DemoApp-Swift/Ads/ALFrameLayoutBannerAdViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.swift similarity index 95% rename from DemoApp-Swift/DemoApp-Swift/Ads/ALFrameLayoutBannerAdViewController.swift rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.swift index 54209b86f6..310df64833 100644 --- a/DemoApp-Swift/DemoApp-Swift/Ads/ALFrameLayoutBannerAdViewController.swift +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXFrameLayoutBannerAdViewController.swift @@ -9,7 +9,7 @@ import UIKit import AppLovinSDK -class ALFrameLayoutBannerAdViewController: ALBaseAdViewController, MAAdViewAdDelegate +class ALMAXFrameLayoutBannerAdViewController: ALBaseAdViewController, MAAdViewAdDelegate { private let adView = MAAdView(adUnitIdentifier: "YOUR_AD_UNIT_ID") diff --git a/DemoApp-Swift/DemoApp-Swift/Ads/ALInterfaceBuilderBannerAdViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.swift similarity index 92% rename from DemoApp-Swift/DemoApp-Swift/Ads/ALInterfaceBuilderBannerAdViewController.swift rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.swift index 1471e80859..c82224d6d1 100644 --- a/DemoApp-Swift/DemoApp-Swift/Ads/ALInterfaceBuilderBannerAdViewController.swift +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Banners/ALMAXInterfaceBuilderBannerAdViewController.swift @@ -9,7 +9,7 @@ import UIKit import AppLovinSDK -class ALInterfaceBuilderBannerAdViewController: ALBaseAdViewController, MAAdViewAdDelegate +class ALMAXInterfaceBuilderBannerAdViewController: ALBaseAdViewController, MAAdViewAdDelegate { @IBOutlet weak var adView: MAAdView! diff --git a/DemoApp-Swift/DemoApp-Swift/Ads/ALInterstitialAdViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Interstitials/ALMAXInterstitialAdViewController.swift similarity index 78% rename from DemoApp-Swift/DemoApp-Swift/Ads/ALInterstitialAdViewController.swift rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Interstitials/ALMAXInterstitialAdViewController.swift index 732c7b0dc5..22e85493e2 100644 --- a/DemoApp-Swift/DemoApp-Swift/Ads/ALInterstitialAdViewController.swift +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Interstitials/ALMAXInterstitialAdViewController.swift @@ -9,9 +9,10 @@ import UIKit import AppLovinSDK -class ALInterstitialAdViewController: ALBaseAdViewController, MAAdViewAdDelegate +class ALMAXInterstitialAdViewController: ALBaseAdViewController, MAAdViewAdDelegate { private let interstitialAd = MAInterstitialAd(adUnitIdentifier: "YOUR_AD_UNIT_ID") + private var retryAttempt = 0.0 // MARK: View Lifecycle @@ -38,16 +39,23 @@ class ALInterstitialAdViewController: ALBaseAdViewController, MAAdViewAdDelegate { // Interstitial ad is ready to be shown. '[self.interstitialAd isReady]' will now return 'YES' logCallback() + + // Reset retry attempt + retryAttempt = 0 } func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withErrorCode errorCode: Int) { logCallback() - // Interstitial ad failed to load. We recommend re-trying in 3 seconds. - DispatchQueue.main.asyncAfter(deadline: .now() + Double(3 * Double(NSEC_PER_SEC)), execute: { + // Interstitial ad failed to load. We recommend retrying with exponentially higher delays. + + retryAttempt += 1 + let delaySec = pow(2.0, retryAttempt) + + DispatchQueue.main.asyncAfter(deadline: .now() + delaySec) { self.interstitialAd.load() - }) + } } func didDisplay(_ ad: MAAd) { logCallback() } diff --git a/DemoApp-Swift/DemoApp-Swift/Ads/ALRewardedAdViewController.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Rewarded/ALMAXRewardedAdViewController.swift similarity index 80% rename from DemoApp-Swift/DemoApp-Swift/Ads/ALRewardedAdViewController.swift rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Rewarded/ALMAXRewardedAdViewController.swift index 268a502c0c..e93bee9bf0 100644 --- a/DemoApp-Swift/DemoApp-Swift/Ads/ALRewardedAdViewController.swift +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/MAX/Rewarded/ALMAXRewardedAdViewController.swift @@ -9,9 +9,10 @@ import UIKit import AppLovinSDK -class ALRewardedAdViewController: ALBaseAdViewController, MARewardedAdDelegate +class ALMAXRewardedAdViewController: ALBaseAdViewController, MARewardedAdDelegate { private let rewardedAd = MARewardedAd.shared(withAdUnitIdentifier: "YOUR_AD_UNIT_ID") + private var retryAttempt = 0.0 // MARK: View Lifecycle @@ -38,16 +39,23 @@ class ALRewardedAdViewController: ALBaseAdViewController, MARewardedAdDelegate { // Rewarded ad is ready to be shown. '[self.rewardedAd isReady]' will now return 'YES' logCallback() + + // Reset retry attempt + retryAttempt = 0 } func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withErrorCode errorCode: Int) { logCallback() - // Rewarded ad failed to load. We recommend re-trying in 3 seconds. - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(3 * Double(NSEC_PER_SEC)), execute: { + // Rewarded ad failed to load. We recommend retrying with exponentially higher delays. + + retryAttempt += 1 + let delaySec = pow(2.0, retryAttempt) + + DispatchQueue.main.asyncAfter(deadline: .now() + delaySec) { self.rewardedAd.load() - }) + } } func didDisplay(_ ad: MAAd) { logCallback() } diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/Contents.json b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/Contents.json similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/Contents.json rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/Contents.json diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/bug.imageset/Contents.json b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/bug.imageset/Contents.json similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/bug.imageset/Contents.json rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/bug.imageset/Contents.json diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/bug.imageset/bug.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/bug.imageset/bug.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/bug.imageset/bug.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/bug.imageset/bug.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/logo.imageset/Contents.json b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/logo.imageset/Contents.json similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/logo.imageset/Contents.json rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/logo.imageset/Contents.json diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@2x.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@2x.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@2x.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@2x.png diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@3x.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@3x.png similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@3x.png rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/logo.imageset/square_logo_nontransparent@3x.png diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/mute.imageset/Contents.json b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/mute.imageset/Contents.json new file mode 100644 index 0000000000..502704ce90 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/mute.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "mute.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/mute.imageset/mute.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/mute.imageset/mute.png new file mode 100644 index 0000000000000000000000000000000000000000..0d76cad7ee7c90abc75b11951bb9f6055bd89f1f GIT binary patch literal 3677 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 literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/unmute.imageset/Contents.json b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/unmute.imageset/Contents.json new file mode 100644 index 0000000000..9e82e3cad9 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/unmute.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "unmute.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/unmute.imageset/unmute.png b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Assets.xcassets/unmute.imageset/unmute.png new file mode 100644 index 0000000000000000000000000000000000000000..f7d8e148467ca518e74a06ab0a577ff9230d4962 GIT binary patch literal 3074 zcma);2{_dGAIC?Na~-P#35~%&FfeG@gdbxE z4)pK>IkLHQkTnzqg@XxdAP@-8rA6X=h|Vi=*~ktY&F6D)Fj!(@A~ewo%H~GF5Lhf0 z21mk>NQle=!b@iH$w?3v&*TH-BZo-mQMn8bpTTB<yR*Uz5q|{omvMR1;asc$u|_2aZVRG7{)CXD*w$To?|S%cIMKv;%*PaTuu|m zVDb257L`sS5@aMOgF(aDpslTG6f_Njj6`A}C^`lO!II%*2tCpYi?TtGF<3Mm5Boy? zk2P4K)7aE_CY{A!q0?k^niUm8w~3@c=t#Jgj84Hq$OtMHLa{-jC|G(Vh6=}gq_2{H zrsH8>q`bV*KCJ)8dVJ~F0glCDoSdDU;Pz+)!U=_SlNMCYz=q9pg(axOIJDl z;FI)awN|ji|Ak#8{Q|p-%ZOsI$g#xFuH}pI|Cj4SbYH=&awXq$IGKrTImq$@L_pwS zU(BttmoagUTsoQ0=K8VOu>^a0UjTVUv-xaZG@Ao*bOf1ukf{uod~C5?y?@xRpztvH zL0xrlKXirVSy@{ksL7NcTMR+1QVWOz0F-q|M0>wL)msH2>G}Q}+M-%_>fZU?=YfxzITKuIZI~z(Z5l#;{kKB5z@l>hZ%@5Rs() zYUa~Zmulbk)ONg{MkMc&H7dxIaRJUoxe5S$F;@1M62WoKoDTL}YM4ntmZS=f=lXO+ zb}Nj&r{!K#kywcZ8e`6L2dC>%W96OUr4ws*o)gw3suyqS-HAw1E9&CpvPDFt=79)V zv;O{KC{V|>u)eEiKQe2>gsw63pItwOIHPv3y3W^gQ09|jq5d8 zJp~;=H%0K0`ZV>u_7In22p4Vy&MP-$#%Ya*Bn%_V^Ta{VV__Y;f$g{oMrV#?!gs>C znt<@_Nk&VF;_17)JtsOFUqGCGIvZB@@VshFR_^WI^eRbToT$jaKzTR{Y`?^e4F}dw zDQ+oQg74FfaV%)~d8joBJV{^h(tzsB8_h7(fhYyFqhenR?0DJr_$4HuZVy>kwMn*&_18R0aUMO@1l z+S|rCh~mHXYwtatdARD3DcQ|V^^z_qDK&>O|D@$nB*zy1dz;vsW4&)gDvmm2n-u(5 zM^$*BEOx7Q*KS;nje3p3oOK>0yItq+YtmQB1l5frge{g;4m*J7eq*^4X#Vd4!hsPM z-d3j{>_6h|OgRPH^zg(aq$4ftNd!M(n|HNoF}P~|Nc^vYKFYC7d|s&du|V;K2&3P# zE5o&=$BR+`0k$DeFzC)Sa&fyssk< zUbC$x`u>>gX8O1tv?q|)=)a1%+UW!6yWxq{6R8h> zKz3zl?zyDY6gKzOV&G4 zKmyF-BQ!Vh%o`i{y**jD{$``A82jL&`ciO}u`{b88x6^-dsS_$ zxHI+nfxTWa>M2vM)OYLPqI*)x=8EsjvuI|6?Dh6UySBdy0wme=1BoIHs&4iP{88k literal 0 HcmV?d00001 diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Banners.storyboard b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Banners.storyboard new file mode 100644 index 0000000000..a5107497dc --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Banners.storyboard @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Base.lproj/LaunchScreen.storyboard b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Base.lproj/LaunchScreen.storyboard rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Base.lproj/LaunchScreen.storyboard diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Base.lproj/Main.storyboard b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..31d0d07699 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Base.lproj/Main.storyboard @@ -0,0 +1,1208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Info.plist b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Info.plist similarity index 93% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/Info.plist rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Info.plist index 1bc49f9059..64ba8db4ca 100644 --- a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Info.plist +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Info.plist @@ -3,7 +3,7 @@ AppLovinSdkKey - SDK_KEY_HERE + 05TMDQ5tZabpXQ45_UTbmEGNUtVAzSTzT6KmWQc5_CuWdzccS4DCITZoL3yIWUG3bbq60QC_d4WF28tUC4gVTF CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Interstitials.storyboard b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Interstitials.storyboard new file mode 100644 index 0000000000..b7d6f4c70d --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Interstitials.storyboard @@ -0,0 +1,313 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Leaders.storyboard b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Leaders.storyboard new file mode 100644 index 0000000000..9bcfd3e588 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Leaders.storyboard @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/MRECs.storyboard b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/MRECs.storyboard new file mode 100644 index 0000000000..f57df07a3e --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/MRECs.storyboard @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Rewarded.storyboard b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Rewarded.storyboard new file mode 100644 index 0000000000..020e948508 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/Rewarded.storyboard @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/main.swift b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/main.swift similarity index 100% rename from DemoApp-Swift/DemoApp-Swift/Supporting Files/main.swift rename to AppLovin MAX Demo App - Swift/AppLovin MAX Demo App - Swift/Supporting Files/main.swift diff --git a/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App-Bridging-Header.h b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App-Bridging-Header.h new file mode 100644 index 0000000000..e6b1aecb10 --- /dev/null +++ b/AppLovin MAX Demo App - Swift/AppLovin MAX Demo App-Bridging-Header.h @@ -0,0 +1,11 @@ +// +// AppLovin MAX Demo App-Bridging-Header.h +// AppLovin MAX Demo App - Swift +// +// Created by Varsha Hanji on 4/1/20. +// Copyright © 2020 AppLovin. All rights reserved. +// + +#import "ALCarouselMediaView.h" +#import "ALDemoRSSFeedRetriever.h" +#import "ALDemoArticle.h" diff --git a/DemoApp-Swift/Podfile b/AppLovin MAX Demo App - Swift/Podfile similarity index 60% rename from DemoApp-Swift/Podfile rename to AppLovin MAX Demo App - Swift/Podfile index f06145ec59..d11c76f673 100644 --- a/DemoApp-Swift/Podfile +++ b/AppLovin MAX Demo App - Swift/Podfile @@ -1,6 +1,6 @@ use_frameworks! inhibit_all_warnings! -target 'MAX Demo App' do +target 'AppLovin MAX Demo App - Swift' do pod 'AppLovinSDK' end diff --git a/DemoApp-ObjC/DemoApp-ObjC.xcodeproj/project.pbxproj b/DemoApp-ObjC/DemoApp-ObjC.xcodeproj/project.pbxproj deleted file mode 100644 index 7c4266022d..0000000000 --- a/DemoApp-ObjC/DemoApp-ObjC.xcodeproj/project.pbxproj +++ /dev/null @@ -1,400 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - 1D992FE6231FA1C400C472F8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1D992FE4231FA1C400C472F8 /* Main.storyboard */; }; - 1D992FE8231FA1C500C472F8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1D992FE7231FA1C500C472F8 /* Assets.xcassets */; }; - 1D992FEB231FA1C500C472F8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1D992FE9231FA1C500C472F8 /* LaunchScreen.storyboard */; }; - 37C7E1EB2328904E002165B5 /* ALAutoLayoutBannerAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D0CB33523204D120076AAAA /* ALAutoLayoutBannerAdViewController.m */; }; - 37C7E1EC2328904E002165B5 /* ALFrameLayoutBannerAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 37C7E1E823285E94002165B5 /* ALFrameLayoutBannerAdViewController.m */; }; - 37C7E1ED2328904E002165B5 /* ALInterstitialAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D0CB32F23204CE70076AAAA /* ALInterstitialAdViewController.m */; }; - 37C7E1EE2328904E002165B5 /* ALRewardedAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D0CB33223204CF70076AAAA /* ALRewardedAdViewController.m */; }; - 37C7E1EF2328904E002165B5 /* ALInterfaceBuilderBannerAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 37C7E1E523285B4E002165B5 /* ALInterfaceBuilderBannerAdViewController.m */; }; - 37C7E1F3232892CF002165B5 /* ALAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D992FDF231FA1C400C472F8 /* ALAppDelegate.m */; }; - 37C7E1F523297423002165B5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D992FED231FA1C500C472F8 /* main.m */; }; - 37C7E1F62329742E002165B5 /* ALHomeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D992FE2231FA1C400C472F8 /* ALHomeViewController.m */; }; - C0DE8BB3234E8A86004B0CFC /* ALBaseAdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C0DE8BB2234E8A86004B0CFC /* ALBaseAdViewController.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 1D0CB32E23204CE70076AAAA /* ALInterstitialAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALInterstitialAdViewController.h; sourceTree = ""; }; - 1D0CB32F23204CE70076AAAA /* ALInterstitialAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALInterstitialAdViewController.m; sourceTree = ""; }; - 1D0CB33123204CF70076AAAA /* ALRewardedAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALRewardedAdViewController.h; sourceTree = ""; }; - 1D0CB33223204CF70076AAAA /* ALRewardedAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALRewardedAdViewController.m; sourceTree = ""; }; - 1D0CB33423204D120076AAAA /* ALAutoLayoutBannerAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALAutoLayoutBannerAdViewController.h; sourceTree = ""; }; - 1D0CB33523204D120076AAAA /* ALAutoLayoutBannerAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALAutoLayoutBannerAdViewController.m; sourceTree = ""; }; - 1D992FDB231FA1C300C472F8 /* MAX Demo App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MAX Demo App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 1D992FDE231FA1C300C472F8 /* ALAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALAppDelegate.h; sourceTree = ""; }; - 1D992FDF231FA1C400C472F8 /* ALAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALAppDelegate.m; sourceTree = ""; }; - 1D992FE1231FA1C400C472F8 /* ALHomeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALHomeViewController.h; sourceTree = ""; }; - 1D992FE2231FA1C400C472F8 /* ALHomeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALHomeViewController.m; sourceTree = ""; }; - 1D992FE5231FA1C400C472F8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 1D992FE7231FA1C500C472F8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 1D992FEA231FA1C500C472F8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 1D992FEC231FA1C500C472F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 1D992FED231FA1C500C472F8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 37C7E1E523285B4E002165B5 /* ALInterfaceBuilderBannerAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALInterfaceBuilderBannerAdViewController.m; sourceTree = ""; }; - 37C7E1E723285B6A002165B5 /* ALInterfaceBuilderBannerAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALInterfaceBuilderBannerAdViewController.h; sourceTree = ""; }; - 37C7E1E823285E94002165B5 /* ALFrameLayoutBannerAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALFrameLayoutBannerAdViewController.m; sourceTree = ""; }; - 37C7E1EA23285EDF002165B5 /* ALFrameLayoutBannerAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALFrameLayoutBannerAdViewController.h; sourceTree = ""; }; - C0DE8BB1234E8A86004B0CFC /* ALBaseAdViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALBaseAdViewController.h; sourceTree = ""; }; - C0DE8BB2234E8A86004B0CFC /* ALBaseAdViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALBaseAdViewController.m; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 1D992FD8231FA1C300C472F8 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 1D0CB33823204EBD0076AAAA /* Ads */ = { - isa = PBXGroup; - children = ( - 1D0CB33423204D120076AAAA /* ALAutoLayoutBannerAdViewController.h */, - 1D0CB33523204D120076AAAA /* ALAutoLayoutBannerAdViewController.m */, - 37C7E1EA23285EDF002165B5 /* ALFrameLayoutBannerAdViewController.h */, - 37C7E1E823285E94002165B5 /* ALFrameLayoutBannerAdViewController.m */, - 1D0CB32E23204CE70076AAAA /* ALInterstitialAdViewController.h */, - 1D0CB32F23204CE70076AAAA /* ALInterstitialAdViewController.m */, - 1D0CB33123204CF70076AAAA /* ALRewardedAdViewController.h */, - 1D0CB33223204CF70076AAAA /* ALRewardedAdViewController.m */, - 37C7E1E723285B6A002165B5 /* ALInterfaceBuilderBannerAdViewController.h */, - 37C7E1E523285B4E002165B5 /* ALInterfaceBuilderBannerAdViewController.m */, - ); - path = Ads; - sourceTree = ""; - }; - 1D992FD2231FA1C300C472F8 = { - isa = PBXGroup; - children = ( - 1D992FDD231FA1C300C472F8 /* DemoApp-ObjC */, - 1D992FDC231FA1C300C472F8 /* Products */, - ); - sourceTree = ""; - }; - 1D992FDC231FA1C300C472F8 /* Products */ = { - isa = PBXGroup; - children = ( - 1D992FDB231FA1C300C472F8 /* MAX Demo App.app */, - ); - name = Products; - sourceTree = ""; - }; - 1D992FDD231FA1C300C472F8 /* DemoApp-ObjC */ = { - isa = PBXGroup; - children = ( - 1D0CB33823204EBD0076AAAA /* Ads */, - 1D992FDE231FA1C300C472F8 /* ALAppDelegate.h */, - 1D992FDF231FA1C400C472F8 /* ALAppDelegate.m */, - 1D992FE1231FA1C400C472F8 /* ALHomeViewController.h */, - 1D992FE2231FA1C400C472F8 /* ALHomeViewController.m */, - 1D993015231FA39000C472F8 /* Supporting Files */, - C0DE8BB1234E8A86004B0CFC /* ALBaseAdViewController.h */, - C0DE8BB2234E8A86004B0CFC /* ALBaseAdViewController.m */, - ); - path = "DemoApp-ObjC"; - sourceTree = ""; - }; - 1D993015231FA39000C472F8 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 1D992FED231FA1C500C472F8 /* main.m */, - 1D992FEC231FA1C500C472F8 /* Info.plist */, - 1D992FE7231FA1C500C472F8 /* Assets.xcassets */, - 1D992FE9231FA1C500C472F8 /* LaunchScreen.storyboard */, - 1D992FE4231FA1C400C472F8 /* Main.storyboard */, - ); - path = "Supporting Files"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 1D992FDA231FA1C300C472F8 /* MAX Demo App */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1D992FF1231FA1C500C472F8 /* Build configuration list for PBXNativeTarget "MAX Demo App" */; - buildPhases = ( - 1D992FD7231FA1C300C472F8 /* Sources */, - 1D992FD8231FA1C300C472F8 /* Frameworks */, - 1D992FD9231FA1C300C472F8 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "MAX Demo App"; - productName = "DemoApp-ObjC"; - productReference = 1D992FDB231FA1C300C472F8 /* MAX Demo App.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 1D992FD3231FA1C300C472F8 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1030; - ORGANIZATIONNAME = "AppLovin Corporation"; - TargetAttributes = { - 1D992FDA231FA1C300C472F8 = { - CreatedOnToolsVersion = 10.3; - }; - }; - }; - buildConfigurationList = 1D992FD6231FA1C300C472F8 /* Build configuration list for PBXProject "DemoApp-ObjC" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 1D992FD2231FA1C300C472F8; - productRefGroup = 1D992FDC231FA1C300C472F8 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 1D992FDA231FA1C300C472F8 /* MAX Demo App */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 1D992FD9231FA1C300C472F8 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 1D992FEB231FA1C500C472F8 /* LaunchScreen.storyboard in Resources */, - 1D992FE8231FA1C500C472F8 /* Assets.xcassets in Resources */, - 1D992FE6231FA1C400C472F8 /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 1D992FD7231FA1C300C472F8 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C0DE8BB3234E8A86004B0CFC /* ALBaseAdViewController.m in Sources */, - 37C7E1F62329742E002165B5 /* ALHomeViewController.m in Sources */, - 37C7E1F523297423002165B5 /* main.m in Sources */, - 37C7E1F3232892CF002165B5 /* ALAppDelegate.m in Sources */, - 37C7E1EB2328904E002165B5 /* ALAutoLayoutBannerAdViewController.m in Sources */, - 37C7E1EC2328904E002165B5 /* ALFrameLayoutBannerAdViewController.m in Sources */, - 37C7E1ED2328904E002165B5 /* ALInterstitialAdViewController.m in Sources */, - 37C7E1EE2328904E002165B5 /* ALRewardedAdViewController.m in Sources */, - 37C7E1EF2328904E002165B5 /* ALInterfaceBuilderBannerAdViewController.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 1D992FE4231FA1C400C472F8 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 1D992FE5231FA1C400C472F8 /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 1D992FE9231FA1C500C472F8 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 1D992FEA231FA1C500C472F8 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 1D992FEF231FA1C500C472F8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.4; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - OTHER_LDFLAGS = "-ObjC"; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 1D992FF0231FA1C500C472F8 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.4; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - OTHER_LDFLAGS = "-ObjC"; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 1D992FF2231FA1C500C472F8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = LUDVB6Z3BS; - INFOPLIST_FILE = "$(SRCROOT)/DemoApp-ObjC/Supporting Files/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - OTHER_LDFLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = com.applovin.enterprise.apps.demoapp; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 1D992FF3231FA1C500C472F8 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = LUDVB6Z3BS; - INFOPLIST_FILE = "$(SRCROOT)/DemoApp-ObjC/Supporting Files/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - OTHER_LDFLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = com.applovin.enterprise.apps.demoapp; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1D992FD6231FA1C300C472F8 /* Build configuration list for PBXProject "DemoApp-ObjC" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1D992FEF231FA1C500C472F8 /* Debug */, - 1D992FF0231FA1C500C472F8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1D992FF1231FA1C500C472F8 /* Build configuration list for PBXNativeTarget "MAX Demo App" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1D992FF2231FA1C500C472F8 /* Debug */, - 1D992FF3231FA1C500C472F8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 1D992FD3231FA1C300C472F8 /* Project object */; -} diff --git a/DemoApp-ObjC/DemoApp-ObjC/ALHomeViewController.m b/DemoApp-ObjC/DemoApp-ObjC/ALHomeViewController.m deleted file mode 100644 index 866c20d52c..0000000000 --- a/DemoApp-ObjC/DemoApp-ObjC/ALHomeViewController.m +++ /dev/null @@ -1,29 +0,0 @@ -// -// ALHomeViewController.m -// DemoApp-ObjC -// -// Created by Thomas So on 9/4/19. -// Copyright © 2019 AppLovin Corporation. All rights reserved. -// - -#import "ALHomeViewController.h" -#import - -@interface ALHomeViewController() - -@end - -@implementation ALHomeViewController - -- (void)viewDidLoad -{ - [super viewDidLoad]; - -} - -- (IBAction)showMediationDebugger:(UIBarButtonItem *)sender -{ - [[ALSdk shared] showMediationDebugger]; -} - -@end diff --git a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Base.lproj/Main.storyboard b/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Base.lproj/Main.storyboard deleted file mode 100644 index b2eb1ca071..0000000000 --- a/DemoApp-ObjC/DemoApp-ObjC/Supporting Files/Base.lproj/Main.storyboard +++ /dev/null @@ -1,491 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DemoApp-Swift/DemoApp-Swift.xcodeproj/project.pbxproj b/DemoApp-Swift/DemoApp-Swift.xcodeproj/project.pbxproj deleted file mode 100644 index dbc3c6f2a8..0000000000 --- a/DemoApp-Swift/DemoApp-Swift.xcodeproj/project.pbxproj +++ /dev/null @@ -1,405 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - 3763B23723357C4B00E49783 /* ALAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763B23623357C4B00E49783 /* ALAppDelegate.swift */; }; - 3763B23923357C4B00E49783 /* ALHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763B23823357C4B00E49783 /* ALHomeViewController.swift */; }; - 3763B23C23357C4B00E49783 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3763B23A23357C4B00E49783 /* Main.storyboard */; }; - 3763B23E23357C4D00E49783 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3763B23D23357C4D00E49783 /* Assets.xcassets */; }; - 3763B24123357C4D00E49783 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3763B23F23357C4D00E49783 /* LaunchScreen.storyboard */; }; - 3763B24923357CEC00E49783 /* ALAutoLayoutBannerAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763B24823357CEC00E49783 /* ALAutoLayoutBannerAdViewController.swift */; }; - 3763B24C23357D1C00E49783 /* ALFrameLayoutBannerAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763B24B23357D1C00E49783 /* ALFrameLayoutBannerAdViewController.swift */; }; - 3763B24E23357D3000E49783 /* ALInterstitialAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763B24D23357D3000E49783 /* ALInterstitialAdViewController.swift */; }; - 3763B25023357D3A00E49783 /* ALRewardedAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763B24F23357D3A00E49783 /* ALRewardedAdViewController.swift */; }; - 3763B25223357D4A00E49783 /* ALInterfaceBuilderBannerAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763B25123357D4A00E49783 /* ALInterfaceBuilderBannerAdViewController.swift */; }; - 37C4D0E8233D834900096894 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C4D0E7233D834900096894 /* main.swift */; }; - C0DE8BAE234E80B6004B0CFC /* ALBaseAdViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DE8BAD234E80B6004B0CFC /* ALBaseAdViewController.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 3763B23323357C4B00E49783 /* MAX Demo App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MAX Demo App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 3763B23623357C4B00E49783 /* ALAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALAppDelegate.swift; sourceTree = ""; }; - 3763B23823357C4B00E49783 /* ALHomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALHomeViewController.swift; sourceTree = ""; }; - 3763B23B23357C4B00E49783 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 3763B23D23357C4D00E49783 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 3763B24023357C4D00E49783 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 3763B24223357C4D00E49783 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 3763B24823357CEC00E49783 /* ALAutoLayoutBannerAdViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALAutoLayoutBannerAdViewController.swift; sourceTree = ""; }; - 3763B24B23357D1C00E49783 /* ALFrameLayoutBannerAdViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALFrameLayoutBannerAdViewController.swift; sourceTree = ""; }; - 3763B24D23357D3000E49783 /* ALInterstitialAdViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALInterstitialAdViewController.swift; sourceTree = ""; }; - 3763B24F23357D3A00E49783 /* ALRewardedAdViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALRewardedAdViewController.swift; sourceTree = ""; }; - 3763B25123357D4A00E49783 /* ALInterfaceBuilderBannerAdViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALInterfaceBuilderBannerAdViewController.swift; sourceTree = ""; }; - 37C4D0E7233D834900096894 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; - C0DE8BAD234E80B6004B0CFC /* ALBaseAdViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALBaseAdViewController.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 3763B23023357C4B00E49783 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 3763B22A23357C4B00E49783 = { - isa = PBXGroup; - children = ( - 3763B23523357C4B00E49783 /* DemoApp-Swift */, - 3763B23423357C4B00E49783 /* Products */, - 41B2F44DF9EB96E1BC86CD12 /* Pods */, - ); - sourceTree = ""; - }; - 3763B23423357C4B00E49783 /* Products */ = { - isa = PBXGroup; - children = ( - 3763B23323357C4B00E49783 /* MAX Demo App.app */, - ); - name = Products; - sourceTree = ""; - }; - 3763B23523357C4B00E49783 /* DemoApp-Swift */ = { - isa = PBXGroup; - children = ( - 3763B24A23357CFB00E49783 /* Ads */, - 3763B23623357C4B00E49783 /* ALAppDelegate.swift */, - 3763B23823357C4B00E49783 /* ALHomeViewController.swift */, - 3763B25323357D7900E49783 /* Supporting Files */, - C0DE8BAD234E80B6004B0CFC /* ALBaseAdViewController.swift */, - ); - path = "DemoApp-Swift"; - sourceTree = ""; - }; - 3763B24A23357CFB00E49783 /* Ads */ = { - isa = PBXGroup; - children = ( - 3763B24823357CEC00E49783 /* ALAutoLayoutBannerAdViewController.swift */, - 3763B24B23357D1C00E49783 /* ALFrameLayoutBannerAdViewController.swift */, - 3763B24D23357D3000E49783 /* ALInterstitialAdViewController.swift */, - 3763B24F23357D3A00E49783 /* ALRewardedAdViewController.swift */, - 3763B25123357D4A00E49783 /* ALInterfaceBuilderBannerAdViewController.swift */, - ); - path = Ads; - sourceTree = ""; - }; - 3763B25323357D7900E49783 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 37C4D0E7233D834900096894 /* main.swift */, - 3763B24223357C4D00E49783 /* Info.plist */, - 3763B23D23357C4D00E49783 /* Assets.xcassets */, - 3763B23F23357C4D00E49783 /* LaunchScreen.storyboard */, - 3763B23A23357C4B00E49783 /* Main.storyboard */, - ); - path = "Supporting Files"; - sourceTree = ""; - }; - 41B2F44DF9EB96E1BC86CD12 /* Pods */ = { - isa = PBXGroup; - children = ( - ); - path = Pods; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 3763B23223357C4B00E49783 /* MAX Demo App */ = { - isa = PBXNativeTarget; - buildConfigurationList = 3763B24523357C4D00E49783 /* Build configuration list for PBXNativeTarget "MAX Demo App" */; - buildPhases = ( - 3763B22F23357C4B00E49783 /* Sources */, - 3763B23023357C4B00E49783 /* Frameworks */, - 3763B23123357C4B00E49783 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "MAX Demo App"; - productName = "DemoApp-Swift"; - productReference = 3763B23323357C4B00E49783 /* MAX Demo App.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 3763B22B23357C4B00E49783 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1030; - LastUpgradeCheck = 1030; - ORGANIZATIONNAME = AppLovin; - TargetAttributes = { - 3763B23223357C4B00E49783 = { - CreatedOnToolsVersion = 10.3; - LastSwiftMigration = 1030; - }; - }; - }; - buildConfigurationList = 3763B22E23357C4B00E49783 /* Build configuration list for PBXProject "DemoApp-Swift" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 3763B22A23357C4B00E49783; - productRefGroup = 3763B23423357C4B00E49783 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 3763B23223357C4B00E49783 /* MAX Demo App */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 3763B23123357C4B00E49783 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3763B24123357C4D00E49783 /* LaunchScreen.storyboard in Resources */, - 3763B23E23357C4D00E49783 /* Assets.xcassets in Resources */, - 3763B23C23357C4B00E49783 /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 3763B22F23357C4B00E49783 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3763B25223357D4A00E49783 /* ALInterfaceBuilderBannerAdViewController.swift in Sources */, - 3763B24C23357D1C00E49783 /* ALFrameLayoutBannerAdViewController.swift in Sources */, - 37C4D0E8233D834900096894 /* main.swift in Sources */, - 3763B24923357CEC00E49783 /* ALAutoLayoutBannerAdViewController.swift in Sources */, - 3763B25023357D3A00E49783 /* ALRewardedAdViewController.swift in Sources */, - 3763B23923357C4B00E49783 /* ALHomeViewController.swift in Sources */, - C0DE8BAE234E80B6004B0CFC /* ALBaseAdViewController.swift in Sources */, - 3763B24E23357D3000E49783 /* ALInterstitialAdViewController.swift in Sources */, - 3763B23723357C4B00E49783 /* ALAppDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 3763B23A23357C4B00E49783 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 3763B23B23357C4B00E49783 /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 3763B23F23357C4D00E49783 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 3763B24023357C4D00E49783 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 3763B24323357C4D00E49783 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.4; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 3763B24423357C4D00E49783 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.4; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 3763B24623357C4D00E49783 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = LUDVB6Z3BS; - INFOPLIST_FILE = "$(SRCROOT)/DemoApp-Swift/Supporting Files/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - OTHER_LDFLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = com.applovin.enterprise.apps.demoapp; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 3763B24723357C4D00E49783 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = LUDVB6Z3BS; - INFOPLIST_FILE = "$(SRCROOT)/DemoApp-Swift/Supporting Files/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - OTHER_LDFLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = com.applovin.enterprise.apps.demoapp; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 3763B22E23357C4B00E49783 /* Build configuration list for PBXProject "DemoApp-Swift" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3763B24323357C4D00E49783 /* Debug */, - 3763B24423357C4D00E49783 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 3763B24523357C4D00E49783 /* Build configuration list for PBXNativeTarget "MAX Demo App" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3763B24623357C4D00E49783 /* Debug */, - 3763B24723357C4D00E49783 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 3763B22B23357C4B00E49783 /* Project object */; -} diff --git a/DemoApp-Swift/DemoApp-Swift/ALHomeViewController.swift b/DemoApp-Swift/DemoApp-Swift/ALHomeViewController.swift deleted file mode 100644 index 421ae88992..0000000000 --- a/DemoApp-Swift/DemoApp-Swift/ALHomeViewController.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ALHomeViewController.swift -// DemoApp-Swift -// -// Created by Andrew Tian on 9/20/19. -// Copyright © 2019 AppLovin. All rights reserved. -// - -import UIKit -import AppLovinSDK - -class ALHomeViewController: UITableViewController -{ - override func viewDidLoad() - { - super.viewDidLoad() - } - - @IBAction func showMediationDebugger(_ sender: UIBarButtonItem!) - { - ALSdk.shared()!.showMediationDebugger() - } -} - diff --git a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Base.lproj/Main.storyboard b/DemoApp-Swift/DemoApp-Swift/Supporting Files/Base.lproj/Main.storyboard deleted file mode 100644 index c4017dad0b..0000000000 --- a/DemoApp-Swift/DemoApp-Swift/Supporting Files/Base.lproj/Main.storyboard +++ /dev/null @@ -1,461 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -