diff --git a/appdb.xcodeproj/project.pbxproj b/appdb.xcodeproj/project.pbxproj index 52a19ae2..a1d5ed88 100644 --- a/appdb.xcodeproj/project.pbxproj +++ b/appdb.xcodeproj/project.pbxproj @@ -46,6 +46,10 @@ 8126201E215444A400B06F88 /* String+Detection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81262018215444A400B06F88 /* String+Detection.swift */; }; 812C3542207D235300D39748 /* Swifter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 812C3541207D235300D39748 /* Swifter.framework */; }; 812C3544207D237300D39748 /* ConfigServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 812C3543207D237300D39748 /* ConfigServer.swift */; }; + 812D1A5522CBA53500035DCC /* GoogleMobileAds.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 812D1A5122CBA53400035DCC /* GoogleMobileAds.framework */; }; + 812D1A5622CBA53500035DCC /* GoogleAppMeasurement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 812D1A5222CBA53400035DCC /* GoogleAppMeasurement.framework */; }; + 812D1A5722CBA53500035DCC /* GoogleUtilities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 812D1A5322CBA53400035DCC /* GoogleUtilities.framework */; }; + 812D1A5822CBA53500035DCC /* nanopb.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 812D1A5422CBA53500035DCC /* nanopb.framework */; }; 81324068205BD86000945CE9 /* Settings+StaticCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81324067205BD86000945CE9 /* Settings+StaticCells.swift */; }; 8132406B205BE50000945CE9 /* News+Detail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8132406A205BE50000945CE9 /* News+Detail.swift */; }; 8133C45C208B629600F2F9F5 /* SeeAll.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8133C45B208B629600F2F9F5 /* SeeAll.swift */; }; @@ -134,6 +138,7 @@ 818A55A920584D9100162E51 /* LNZSnapToCenterCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818A55A720584D9100162E51 /* LNZSnapToCenterCollectionViewLayout.swift */; }; 818A55AA20584D9100162E51 /* LNZInfiniteCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818A55A820584D9100162E51 /* LNZInfiniteCollectionViewLayout.swift */; }; 818A55AC20584F2B00162E51 /* BannerImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818A55AB20584F2B00162E51 /* BannerImage.swift */; }; + 818D75F222CCF32900A69C56 /* AdHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818D75F122CCF32900A69C56 /* AdHelper.swift */; }; 818DE6361E87F4E700E41B51 /* Details+FullScreenshotsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818DE6351E87F4E700E41B51 /* Details+FullScreenshotsCell.swift */; }; 81939B471E3BA98D00179615 /* Themes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81939B461E3BA98D00179615 /* Themes.swift */; }; 819AE78C1E63135A009D6AF3 /* Details+Changelog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 819AE78B1E63135A009D6AF3 /* Details+Changelog.swift */; }; @@ -263,6 +268,14 @@ 81262018215444A400B06F88 /* String+Detection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Detection.swift"; sourceTree = ""; }; 812C3541207D235300D39748 /* Swifter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Swifter.framework; path = Carthage/Build/iOS/Swifter.framework; sourceTree = ""; }; 812C3543207D237300D39748 /* ConfigServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigServer.swift; sourceTree = ""; }; + 812D1A4922CBA3D500035DCC /* GoogleUtilities.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleUtilities.framework; path = "../../../Downloads/GoogleMobileAdsSdkiOS-7.46.0/GoogleUtilities.framework"; sourceTree = ""; }; + 812D1A4A22CBA3D500035DCC /* GoogleAppMeasurement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleAppMeasurement.framework; path = "../../../Downloads/GoogleMobileAdsSdkiOS-7.46.0/GoogleAppMeasurement.framework"; sourceTree = ""; }; + 812D1A4B22CBA3D500035DCC /* nanopb.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = nanopb.framework; path = "../../../Downloads/GoogleMobileAdsSdkiOS-7.46.0/nanopb.framework"; sourceTree = ""; }; + 812D1A4C22CBA3D500035DCC /* GoogleMobileAds.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleMobileAds.framework; path = "../../../Downloads/GoogleMobileAdsSdkiOS-7.46.0/GoogleMobileAds.framework"; sourceTree = ""; }; + 812D1A5122CBA53400035DCC /* GoogleMobileAds.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = GoogleMobileAds.framework; sourceTree = ""; }; + 812D1A5222CBA53400035DCC /* GoogleAppMeasurement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = GoogleAppMeasurement.framework; sourceTree = ""; }; + 812D1A5322CBA53400035DCC /* GoogleUtilities.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = GoogleUtilities.framework; sourceTree = ""; }; + 812D1A5422CBA53500035DCC /* nanopb.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = nanopb.framework; sourceTree = ""; }; 81324067205BD86000945CE9 /* Settings+StaticCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Settings+StaticCells.swift"; sourceTree = ""; }; 8132406A205BE50000945CE9 /* News+Detail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "News+Detail.swift"; sourceTree = ""; }; 8133C45B208B629600F2F9F5 /* SeeAll.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeeAll.swift; sourceTree = ""; }; @@ -343,8 +356,6 @@ 8188CE9C1F347F1E001D1DB8 /* ElasticLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElasticLabel.swift; sourceTree = ""; }; 8188E8D71DB198B800462256 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = ""; }; 8188E8D81DB198B800462256 /* Cartography.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cartography.framework; path = Carthage/Build/iOS/Cartography.framework; sourceTree = ""; }; - 8188E8DB1DB198B800462256 /* Realm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Realm.framework; path = Carthage/Build/iOS/Realm.framework; sourceTree = ""; }; - 8188E8DC1DB198B800462256 /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RealmSwift.framework; path = Carthage/Build/iOS/RealmSwift.framework; sourceTree = ""; }; 8188E8E91DB23B6E00462256 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/iOS/SwiftyJSON.framework; sourceTree = ""; }; 81892559227CC9690096F661 /* AdaptiveUIAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveUIAlertController.swift; sourceTree = ""; }; 818979402284853A00EC87B2 /* DownloadingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadingCell.swift; sourceTree = ""; }; @@ -359,6 +370,7 @@ 818A55A820584D9100162E51 /* LNZInfiniteCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LNZInfiniteCollectionViewLayout.swift; sourceTree = ""; }; 818A55AB20584F2B00162E51 /* BannerImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerImage.swift; sourceTree = ""; }; 818CB5D41E787AB400A69213 /* Details+FullScreenshots.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "Details+FullScreenshots.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 818D75F122CCF32900A69C56 /* AdHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdHelper.swift; sourceTree = ""; }; 818DE6351E87F4E700E41B51 /* Details+FullScreenshotsCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "Details+FullScreenshotsCell.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 81939B461E3BA98D00179615 /* Themes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Themes.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 819AE78B1E63135A009D6AF3 /* Details+Changelog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "Details+Changelog.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -375,7 +387,6 @@ 819EC27920A76EAD007138CF /* ThemeChooser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeChooser.swift; sourceTree = ""; }; 819EC2D11E41013D00BA7A50 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; usesTabs = 1; }; 819EC2D31E41016D00BA7A50 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; usesTabs = 1; }; - 81AA80B920D026DF00CC15AD /* Ferrara.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Ferrara.framework; path = Carthage/Build/iOS/Ferrara.framework; sourceTree = ""; }; 81AB37AD1DAC30BD003A586F /* TabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = TabBarController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 81AF3531215115B200A798D7 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; 81BE066B1E6C2EA500AD9827 /* Details+ExternalLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "Details+ExternalLink.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -443,8 +454,11 @@ files = ( 815234CF2278479000B1AE65 /* ZIPFoundation.framework in Frameworks */, 817D2EDA1E8EBBE500B8AE0D /* Alamofire.framework in Frameworks */, + 812D1A5822CBA53500035DCC /* nanopb.framework in Frameworks */, + 812D1A5622CBA53500035DCC /* GoogleAppMeasurement.framework in Frameworks */, 817D2EDD1E8EBBE500B8AE0D /* Cartography.framework in Frameworks */, 816B343C20B727500019DE5C /* DeepDiff.framework in Frameworks */, + 812D1A5722CBA53500035DCC /* GoogleUtilities.framework in Frameworks */, 8138538C1F2B7EA300DC7C54 /* Kanna.framework in Frameworks */, 81FF1C531F9211B00086EEE0 /* AlamofireNetworkActivityIndicator.framework in Frameworks */, 81FF1C541F9211B00086EEE0 /* SwiftTheme.framework in Frameworks */, @@ -456,6 +470,7 @@ 81876E35227B725C00E27CB8 /* SwiftMessages.framework in Frameworks */, 817D2EE11E8EBBE500B8AE0D /* SwiftyJSON.framework in Frameworks */, 814B76F8205ADB63004D46DD /* Static.framework in Frameworks */, + 812D1A5522CBA53500035DCC /* GoogleMobileAds.framework in Frameworks */, 812C3542207D235300D39748 /* Swifter.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1032,9 +1047,16 @@ 81AB37A31DAC2ED7003A586F /* Frameworks */ = { isa = PBXGroup; children = ( + 812D1A4A22CBA3D500035DCC /* GoogleAppMeasurement.framework */, + 812D1A5222CBA53400035DCC /* GoogleAppMeasurement.framework */, + 812D1A4C22CBA3D500035DCC /* GoogleMobileAds.framework */, + 812D1A4922CBA3D500035DCC /* GoogleUtilities.framework */, + 812D1A4B22CBA3D500035DCC /* nanopb.framework */, + 812D1A5122CBA53400035DCC /* GoogleMobileAds.framework */, + 812D1A5322CBA53400035DCC /* GoogleUtilities.framework */, + 812D1A5422CBA53500035DCC /* nanopb.framework */, 81876E34227B725C00E27CB8 /* SwiftMessages.framework */, 815234CE2278479000B1AE65 /* ZIPFoundation.framework */, - 81AA80B920D026DF00CC15AD /* Ferrara.framework */, 817236A520BB1F8E0014F61A /* BLTNBoard.framework */, 816B343B20B727500019DE5C /* DeepDiff.framework */, 812C3541207D235300D39748 /* Swifter.framework */, @@ -1051,8 +1073,6 @@ 8188E8E91DB23B6E00462256 /* SwiftyJSON.framework */, 8188E8D71DB198B800462256 /* Alamofire.framework */, 8188E8D81DB198B800462256 /* Cartography.framework */, - 8188E8DB1DB198B800462256 /* Realm.framework */, - 8188E8DC1DB198B800462256 /* RealmSwift.framework */, ); name = Frameworks; sourceTree = ""; @@ -1218,6 +1238,7 @@ 81EC00661DAD76BE009EEFFA /* Startup */ = { isa = PBXGroup; children = ( + 818D75F122CCF32900A69C56 /* AdHelper.swift */, 8167A6121DAC246000F96AC2 /* AppDelegate.swift */, 8142FCD021987131003CD6DE /* BadgeManager.swift */, 81EC00691DAD7704009EEFFA /* Colors.swift */, @@ -1516,6 +1537,7 @@ 8142FCD32198E1FE003CD6DE /* Updates+Extension.swift in Sources */, 8126201B215444A400B06F88 /* NSScanner+Swift.swift in Sources */, 817418A52165368A00BB70EB /* LoadingCollectionView.swift in Sources */, + 818D75F222CCF32900A69C56 /* AdHelper.swift in Sources */, 81FC8B4122735E5E003788F3 /* API+MyAppstore.swift in Sources */, 814F4FB92196F5F9005A8111 /* API+Updates.swift in Sources */, 8105175B20D0283300B3E1FB /* Identifiable.swift in Sources */, @@ -1761,7 +1783,10 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - OTHER_LDFLAGS = "-lxml2"; + OTHER_LDFLAGS = ( + "-lxml2", + "-ObjC", + ); PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = iphoneos; @@ -1824,7 +1849,10 @@ HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = "-lxml2"; + OTHER_LDFLAGS = ( + "-lxml2", + "-ObjC", + ); PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = iphoneos; @@ -1851,6 +1879,7 @@ "$(inherited)", "$(PROJECT_DIR)/appdb", "$(PROJECT_DIR)/Carthage/Build/iOS", + "$(PROJECT_DIR)", ); HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; INFOPLIST_FILE = appdb/Other/Info.plist; @@ -1882,6 +1911,7 @@ "$(inherited)", "$(PROJECT_DIR)/appdb", "$(PROJECT_DIR)/Carthage/Build/iOS", + "$(PROJECT_DIR)", ); HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; INFOPLIST_FILE = appdb/Other/Info.plist; diff --git a/appdb/Other/Info.plist b/appdb/Other/Info.plist index d0dee37d..3d590928 100644 --- a/appdb/Other/Info.plist +++ b/appdb/Other/Info.plist @@ -100,6 +100,8 @@ UIViewControllerBasedStatusBarAppearance + GADApplicationIdentifier + YOUR_APP_ID UTImportedTypeDeclarations diff --git "a/appdb/Resources/\342\200\242 LoadingTableView/LoadingCollectionView.swift" "b/appdb/Resources/\342\200\242 LoadingTableView/LoadingCollectionView.swift" index 341fa87a..a5bc1a8d 100644 --- "a/appdb/Resources/\342\200\242 LoadingTableView/LoadingCollectionView.swift" +++ "b/appdb/Resources/\342\200\242 LoadingTableView/LoadingCollectionView.swift" @@ -45,6 +45,8 @@ class LoadingCollectionView: UICollectionViewController { return secondaryErrorMessage }() + var adChangeObservation: DefaultsObservation? + var hasSegment: Bool = false override func viewDidLoad() { @@ -61,6 +63,13 @@ class LoadingCollectionView: UICollectionViewController { collectionView.alwaysBounceVertical = true setConstraints() + + adMobAdjustContentInsetsIfNeeded() + + adChangeObservation = defaults.observe(.adBannerHeight) { [weak self] _ in + guard let self = self else { return } + self.adMobAdjustContentInsetsIfNeeded() + } } private func setConstraints() { diff --git "a/appdb/Resources/\342\200\242 LoadingTableView/LoadingTableView.swift" "b/appdb/Resources/\342\200\242 LoadingTableView/LoadingTableView.swift" index 39216df4..5da8ab1e 100644 --- "a/appdb/Resources/\342\200\242 LoadingTableView/LoadingTableView.swift" +++ "b/appdb/Resources/\342\200\242 LoadingTableView/LoadingTableView.swift" @@ -72,6 +72,8 @@ class LoadingTableView: UITableViewController { return refreshButton }() + var adChangeObservation: DefaultsObservation? + override func viewDidLoad() { super.viewDidLoad() @@ -85,6 +87,13 @@ class LoadingTableView: UITableViewController { refreshButton.isHidden = true setConstraints(.loading) + + adMobAdjustContentInsetsIfNeeded() + + adChangeObservation = defaults.observe(.adBannerHeight) { [weak self] _ in + guard let self = self else { return } + self.adMobAdjustContentInsetsIfNeeded() + } } private func animate() { diff --git "a/appdb/Resources/\342\200\242 MessagesFactory/MessagesFactory.swift" "b/appdb/Resources/\342\200\242 MessagesFactory/MessagesFactory.swift" index 9c16ba89..b3057d9e 100644 --- "a/appdb/Resources/\342\200\242 MessagesFactory/MessagesFactory.swift" +++ "b/appdb/Resources/\342\200\242 MessagesFactory/MessagesFactory.swift" @@ -20,7 +20,7 @@ struct Messages { func getConfig(_ context: SwiftMessages.PresentationContext? = nil) -> SwiftMessages.Config { var config = SwiftMessages.Config() - config.presentationStyle = .bottom + config.presentationStyle = Preferences.adBannerHeight > 0 ? .top : .bottom config.presentationContext = context ?? .automatic config.dimMode = .none return config diff --git a/appdb/Startup/AdHelper.swift b/appdb/Startup/AdHelper.swift new file mode 100644 index 00000000..1774906d --- /dev/null +++ b/appdb/Startup/AdHelper.swift @@ -0,0 +1,83 @@ +// +// AdHelper.swift +// appdb +// +// Created by ned on 03/07/2019. +// Copyright © 2019 ned. All rights reserved. +// + +import GoogleMobileAds +import Static + +enum AdHelper { + + static let bannerUnitID: String = "YOUR_BANNER_ID" + static let interstitialUnitID: String = "YOUR_INTERSTITIAL_ID" + + static var adSize: GADAdSize { + return UIApplication.shared.statusBarOrientation.isLandscape ? kGADAdSizeSmartBannerLandscape : kGADAdSizeSmartBannerPortrait + } + + static var adAwareContentInsets: UIEdgeInsets { + return UIEdgeInsets(top: 0, left: 0, bottom: CGFloat(Preferences.adBannerHeight), right: 0) + } + + static func generateBanner(on viewController: UIViewController) -> GADBannerView? { + guard !Preferences.pro else { return nil } + let banner = GADBannerView(adSize: AdHelper.adSize) + banner.delegate = viewController as? GADBannerViewDelegate + banner.adUnitID = AdHelper.bannerUnitID + banner.rootViewController = viewController + let request = GADRequest() + #if DEBUG + request.testDevices = [kGADSimulatorID] + #endif + banner.load(request) + return banner + } + + static func generateInterstitial(on viewController: UIViewController) -> GADInterstitial? { + guard !Preferences.pro else { return nil } + let interstitial = GADInterstitial(adUnitID: AdHelper.interstitialUnitID) + interstitial.delegate = viewController as? GADInterstitialDelegate + let request = GADRequest() + #if DEBUG + request.testDevices = [kGADSimulatorID] + #endif + interstitial.load(request) + return interstitial + } +} + +protocol AdAware: class { + func adMobAdjustContentInsetsIfNeeded() +} + +extension UITableViewController: AdAware { + func adMobAdjustContentInsetsIfNeeded() { + guard !Preferences.pro else { return } + guard let tableView = tableView else { return } + guard !(UIApplication.topNavigation(UIApplication.topViewController()) is DismissableModalNavController) else { return } + tableView.contentInset = AdHelper.adAwareContentInsets + tableView.scrollIndicatorInsets = AdHelper.adAwareContentInsets + } +} + +extension UICollectionViewController: AdAware { + func adMobAdjustContentInsetsIfNeeded() { + guard !Preferences.pro else { return } + guard let collectionView = collectionView else { return } + guard !(UIApplication.topNavigation(UIApplication.topViewController()) is DismissableModalNavController) else { return } + collectionView.contentInset = AdHelper.adAwareContentInsets + collectionView.scrollIndicatorInsets = AdHelper.adAwareContentInsets + } +} + +extension TableViewController: AdAware { + func adMobAdjustContentInsetsIfNeeded() { + guard !Preferences.pro else { return } + guard !(UIApplication.topNavigation(UIApplication.topViewController()) is DismissableModalNavController) else { return } + tableView.contentInset = AdHelper.adAwareContentInsets + tableView.scrollIndicatorInsets = AdHelper.adAwareContentInsets + } +} diff --git a/appdb/Startup/AppDelegate.swift b/appdb/Startup/AppDelegate.swift index 75d8fbe4..8f0ed72a 100644 --- a/appdb/Startup/AppDelegate.swift +++ b/appdb/Startup/AppDelegate.swift @@ -9,6 +9,7 @@ import UIKit import SwiftTheme import AlamofireNetworkActivityIndicator +import GoogleMobileAds @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, UITabBarControllerDelegate { @@ -29,6 +30,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UITabBarControllerDelegat Global.deleteEventualKeychainData() Global.restoreLanguage() Themes.restoreLastTheme() + Preferences.set(.adBannerHeight, to: 0) // Set main tint color self.window?.theme_backgroundColor = Color.tableViewBackgroundColor @@ -60,6 +62,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UITabBarControllerDelegat NetworkActivityIndicatorManager.shared.startDelay = 0.3 NetworkActivityIndicatorManager.shared.isEnabled = true + // Initialize mobile ads + if !Preferences.pro { + GADMobileAds.sharedInstance().start(completionHandler: nil) + } + return true } diff --git a/appdb/Startup/Preferences.swift b/appdb/Startup/Preferences.swift index 222323aa..1c480263 100644 --- a/appdb/Startup/Preferences.swift +++ b/appdb/Startup/Preferences.swift @@ -17,6 +17,7 @@ extension Defaults.Keys { static let askForInstallationOptions = Key("askForInstallationOptions", default: false) static let showBadgeForUpdates = Key("showBadgeForUpdates", default: true) static let changeBundleBeforeUpload = Key("changeBundleBeforeUpload", default: false) + static let adBannerHeight = Key("adBannerHeight", default: 0) static let ignoredUpdateableApps = Key<[IgnoredApp]>("ignoredUpdateableApps", default: []) static let genres = Key<[Genre]>("genres", default: []) } @@ -78,6 +79,10 @@ enum Preferences { return defaults[.appsync] } + static var adBannerHeight: Int { + return defaults[.adBannerHeight] + } + static var ignoresCompatibility: Bool { return defaults[.ignoreCompatibility] } @@ -138,6 +143,7 @@ extension Preferences { UserDefaults.standard.removeObject(forKey: Defaults.Keys.appsync.name) UserDefaults.standard.removeObject(forKey: Defaults.Keys.askForInstallationOptions.name) UserDefaults.standard.removeObject(forKey: Defaults.Keys.ignoreCompatibility.name) + UserDefaults.standard.removeObject(forKey: Defaults.Keys.adBannerHeight.name) UserDefaults.standard.removeObject(forKey: Defaults.Keys.showBadgeForUpdates.name) UserDefaults.standard.removeObject(forKey: Defaults.Keys.changeBundleBeforeUpload.name) UserDefaults.standard.removeObject(forKey: Defaults.Keys.ignoredUpdateableApps.name) diff --git a/appdb/Startup/TabBarController.swift b/appdb/Startup/TabBarController.swift index c76cbb8f..b8952ffa 100644 --- a/appdb/Startup/TabBarController.swift +++ b/appdb/Startup/TabBarController.swift @@ -7,9 +7,15 @@ // import UIKit +import GoogleMobileAds +import Cartography class TabBarController: UITabBarController { + private var interstitialView: GADInterstitial? + private var bannerView: GADBannerView? + private var bannerGroup = ConstraintGroup() + override func viewDidLoad() { super.viewDidLoad() @@ -29,6 +35,9 @@ class TabBarController: UITabBarController { updatesNav.tabBarItem = UITabBarItem(title: "Updates".localized(), image: #imageLiteral(resourceName: "updates"), tag: 4) viewControllers = [featuredNav, searchNav, downloadsNav, settingsNav, updatesNav] + + bannerView = AdHelper.generateBanner(on: self) + interstitialView = AdHelper.generateInterstitial(on: self) } // Bounce animation @@ -44,3 +53,58 @@ class TabBarController: UITabBarController { } } } + +extension TabBarController: GADBannerViewDelegate { + + func adView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: GADRequestError) { + guard self.bannerView != nil else { return } + Preferences.set(.adBannerHeight, to: 0) + } + + func adViewDidReceiveAd(_ bannerView: GADBannerView) { + guard self.bannerView != nil else { return } + bannerView.alpha = 0 + view.addSubview(bannerView) + constrainBanner() + UIView.animate(withDuration: 0.3, animations: { + bannerView.alpha = 1 + }) + Preferences.set(.adBannerHeight, to: Int(bannerView.frame.size.height)) + } + + func constrainBanner() { + guard let bannerView = bannerView else { return } + constrain(bannerView, replace: bannerGroup) { banner in + banner.leading ~== banner.superview!.leading + banner.trailing ~== banner.superview!.trailing + banner.bottom ~== banner.superview!.bottom - tabBar.frame.height + } + } + + // React to orientation changes + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + + coordinator.animate(alongsideTransition: { (_: UIViewControllerTransitionCoordinatorContext!) -> Void in + guard let bannerView = self.bannerView else { return } + if bannerView.isDescendant(of: self.view) { + bannerView.adSize = AdHelper.adSize + self.constrainBanner() + } + }, completion: nil) + } +} + +extension TabBarController: GADInterstitialDelegate { + + func showInterstitialIfReady() { + guard let interstitialView = interstitialView else { return } + if interstitialView.isReady, Bool.random() { + interstitialView.present(fromRootViewController: self) + } + } + + func interstitialDidDismissScreen(_ ad: GADInterstitial) { + interstitialView = AdHelper.generateInterstitial(on: self) + } +} diff --git a/appdb/Tabs/Downloads/Library/Library+Extension.swift b/appdb/Tabs/Downloads/Library/Library+Extension.swift index 187bce02..2c64a0c8 100644 --- a/appdb/Tabs/Downloads/Library/Library+Extension.swift +++ b/appdb/Tabs/Downloads/Library/Library+Extension.swift @@ -101,6 +101,9 @@ extension Library { Messages.shared.showError(message: error.prettified) } else { Messages.shared.showSuccess(message: "File uploaded successfully".localized()) + delay(1) { + (self.tabBarController as? TabBarController)?.showInterstitialIfReady() + } } }) } diff --git a/appdb/Tabs/Featured/Details/Details.swift b/appdb/Tabs/Featured/Details/Details.swift index 72c3bc71..56ccafe0 100644 --- a/appdb/Tabs/Featured/Details/Details.swift +++ b/appdb/Tabs/Featured/Details/Details.swift @@ -372,6 +372,11 @@ class Details: LoadingTableView { extension Details: SwitchDetailsSegmentDelegate { func segmentSelected(_ state: DetailsSelectedSegmentState) { indexForSegment = state + + if indexForSegment == .download { + (tabBarController as? TabBarController)?.showInterstitialIfReady() + } + tableView.reloadData() } } @@ -456,6 +461,9 @@ extension Details: IPAWebViewControllerDelegate { func didDismiss() { delay(0.8) { Messages.shared.showSuccess(message: "File download has started".localized(), context: Global.isIpad ? .viewController(self) : nil) + delay(1) { + (self.tabBarController as? TabBarController)?.showInterstitialIfReady() + } } } } diff --git a/appdb/Tabs/Search/Search.swift b/appdb/Tabs/Search/Search.swift index 1a128bf9..f0d18efe 100644 --- a/appdb/Tabs/Search/Search.swift +++ b/appdb/Tabs/Search/Search.swift @@ -122,6 +122,9 @@ class Search: LoadingCollectionView, UISearchBarDelegate { setFooter() getTrending() + + collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 50, right: 0) + collectionView.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 50, right: 0) } // Fetch trending apps diff --git a/appdb/Tabs/Settings/Credits/Credits.swift b/appdb/Tabs/Settings/Credits/Credits.swift index 8dc21edc..9d3bcb80 100644 --- a/appdb/Tabs/Settings/Credits/Credits.swift +++ b/appdb/Tabs/Settings/Credits/Credits.swift @@ -87,6 +87,8 @@ class Credits: TableViewController { } dataSource.sections = sections + + adMobAdjustContentInsetsIfNeeded() } private func handleTap(for handle: Handle) { diff --git a/appdb/Tabs/Settings/Language Chooser/LanguageChooser.swift b/appdb/Tabs/Settings/Language Chooser/LanguageChooser.swift index 2c0f07da..f8181665 100644 --- a/appdb/Tabs/Settings/Language Chooser/LanguageChooser.swift +++ b/appdb/Tabs/Settings/Language Chooser/LanguageChooser.swift @@ -53,6 +53,8 @@ class LanguageChooser: UITableViewController { let dismissButton = UIBarButtonItem(title: "Dismiss".localized(), style: .done, target: self, action: #selector(self.dismissAnimated)) navigationItem.rightBarButtonItems = [dismissButton] } + + adMobAdjustContentInsetsIfNeeded() } @objc private func dismissAnimated() { dismiss(animated: true) } diff --git a/appdb/Tabs/Settings/Settings.swift b/appdb/Tabs/Settings/Settings.swift index b494cb6c..ebaba5a8 100644 --- a/appdb/Tabs/Settings/Settings.swift +++ b/appdb/Tabs/Settings/Settings.swift @@ -39,6 +39,8 @@ class Settings: TableViewController { var urlSchemeLinkCodeBulletinManager: BLTNItemManager? + var adChangeObservation: DefaultsObservation? + deinit { NotificationCenter.default.removeObserver(self) } convenience init() { @@ -93,6 +95,13 @@ class Settings: TableViewController { } }) } + + adMobAdjustContentInsetsIfNeeded() + + adChangeObservation = defaults.observe(.adBannerHeight) { [weak self] _ in + guard let self = self else { return } + self.adMobAdjustContentInsetsIfNeeded() + } } // Deauthorize app (clean link code and token) @@ -109,6 +118,11 @@ class Settings: TableViewController { // Push device status controller func pushDeviceStatus() { let deviceStatusController = DeviceStatus() + + delay(1) { + (self.tabBarController as? TabBarController)?.showInterstitialIfReady() + } + if Global.isIpad { let nav = DismissableModalNavController(rootViewController: deviceStatusController) nav.modalPresentationStyle = .formSheet