diff --git a/.gitignore b/.gitignore index 7ecebb4..d4b65e9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ pubspec.lock build/ +.idea diff --git a/CHANGELOG.md b/CHANGELOG.md index e16ea5f..2b1f2b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ -## [0.2.0] - 2019/04/04 +## [0.3.0] - 2019/05/28 +Add iOS support! +## [0.2.0] - 2019/04/04 Update to AndroidX. +> **Note:** This is a breaking change on Android! ## [0.1.2] - Dec 10 - * Release first version diff --git a/LICENSE b/LICENSE index e2b077f..3976614 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ ISC License Copyright (c) 2018, Youssef Kababe +Copyright (c) 2019, Kevin McGill Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/README.md b/README.md index bca172a..1a39d86 100644 --- a/README.md +++ b/README.md @@ -14,20 +14,19 @@ This plugin also has support for Interstitial and Reward ads. This is an early version of the plugin, my primary goal was to overcome the banner ads positioning limitations by implementing a platform views solution, and keeping things simple and stupid because I'm still learning about many Flutter related stuff. I will actively work on adding missing features, improving, and refactoring the code in my free time. -**Currently working on Android only, but iOS support will be available so soon.** - -## Installation +# Installation Add this to your pubspec.yml dependencies: ```yaml -admob_flutter: "^0.2.0" +admob_flutter: "^0.3.0" ``` -## Suport -- AndroidX +### Supported Platforms +- `0.3.0` >= iOS +- `0.2.0` >= AndroidX - FlutterSdk >=2.1.0 < 3.0.0 -## How to use +## Android ### Update your AndroidManifest.xml Add your AdMob App ID to your app's AndroidManifest.xml file by adding the tag shown below. You can find your App ID in the AdMob UI. For android:value insert your own AdMob App ID in quotes, as shown below. @@ -46,7 +45,19 @@ ca-app-pub-3940256099942544~3347511713 ``` +## iOS +Update your `Info.plist` per [Firebase instructions](https://developers.google.com/admob/ios/quick-start#update_your_infoplist). +```xml +GADApplicationIdentifier +ca-app-pub-3940256099942544~1458002511 +``` +and add +```xml +io.flutter.embedded_views_preview + +``` +# How to use ### Initialize the plugin First thing to do before attempting to show any ads is to initialize the plugin. You can do this in the earliest starting point of your app, your `main` function: @@ -55,7 +66,7 @@ First thing to do before attempting to show any ads is to initialize the plugin. import 'package:admob_flutter/admob_flutter.dart'; void main() { - Admob.initialize('ca-app-pub-3940256099942544~3347511713'); + Admob.initialize(getAppId()); runApp(MyApp()); } ``` @@ -66,7 +77,7 @@ This plugin uses Native Platform Views to show Admob banner ads thus, same as an ```dart AdmobBanner( - adUnitId: 'ca-app-pub-3940256099942544/6300978111', + adUnitId: getBannerAdUnitId(), adSize: AdmobBannerSize.BANNER, ) ``` @@ -88,7 +99,7 @@ You can attach a listener to your ads in order to customize their behavior like ```dart AdmobBanner( - adUnitId: 'ca-app-pub-3940256099942544/6300978111', + adUnitId: getBannerAdUnitId(), adSize: AdmobBannerSize.BANNER, listener: (AdmobAdEvent event, Map args) { switch (event) { @@ -117,7 +128,7 @@ AdmobBanner( ```dart // First, create an interstitial ad AdmobInterstitial interstitialAd = AdmobInterstitial( - adUnitId: 'ca-app-pub-3940256099942544/1033173712', + adUnitId: getInterstitialAdUnitId(), ); // Interstitials must be loaded before shown. @@ -148,7 +159,7 @@ You can do something like this (Don't do it!): AdmobInterstitial interstitialAd; interstitialAd = AdmobInterstitial( - adUnitId: 'ca-app-pub-3940256099942544/1033173712', + adUnitId: getInterstitialAdUnitId(), listener: (AdmobAdEvent event, Map args) { if (event == AdmobAdEvent.loaded) interstitialAd.show(); if (event == AdmobAdEvent.closed) interstitialAd.load(); @@ -165,7 +176,7 @@ interstitialAd = AdmobInterstitial( ```dart // First, create a reward ad AdmobReward rewardAd = AdmobReward( - adUnitId: 'ca-app-pub-3940256099942544/5224354917', + adUnitId: getRewardBasedVideoAdUnitId(), ); // Reward ads must be loaded before shown. @@ -188,7 +199,7 @@ Listening to reward ads events works the same as with banners and interstitials, ```dart AdmobReward rewardAd = AdmobReward( - adUnitId: 'ca-app-pub-3940256099942544/5224354917', + adUnitId: getRewardBasedVideoAdUnitId(), listener: (AdmobAdEvent event, Map args) { if (event == AdmobAdEvent.rewarded) { print('User was rewarded!'); @@ -200,9 +211,7 @@ AdmobReward rewardAd = AdmobReward( ``` ### Showing Native Ads - **Coming soon!** ### Using targeting info - **Coming soon!** diff --git a/example/ios/Podfile b/example/ios/Podfile index 64eddc6..6297c51 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' + platform :ios, '8.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 6761a6a..bda5c16 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -19,6 +18,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + E7A1118201BCA9D718CC3ED4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24D7FA5CEB975A68FF05067C /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -39,9 +39,10 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; + 24D7FA5CEB975A68FF05067C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 60E4F89FB3CA779FE21111C3 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -53,6 +54,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9F4D6C33C6C390F42E506F21 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + A09C8DAD180952151FDA7CAA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,16 +65,27 @@ files = ( 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + E7A1118201BCA9D718CC3ED4 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 462490AC19C85F5B12038C68 /* Pods */ = { + isa = PBXGroup; + children = ( + 60E4F89FB3CA779FE21111C3 /* Pods-Runner.debug.xcconfig */, + 9F4D6C33C6C390F42E506F21 /* Pods-Runner.release.xcconfig */, + A09C8DAD180952151FDA7CAA /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEBA1CF902C7004384FC /* Flutter.framework */, @@ -88,6 +102,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 462490AC19C85F5B12038C68 /* Pods */, + 9AC3B6257E801133BCE0A190 /* Frameworks */, ); sourceTree = ""; }; @@ -122,6 +138,14 @@ name = "Supporting Files"; sourceTree = ""; }; + 9AC3B6257E801133BCE0A190 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 24D7FA5CEB975A68FF05067C /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -129,12 +153,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 0C24A2A7747A18E0525C5E7D /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 7FDF1B0DB9C66D50FD638659 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -187,7 +213,6 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -195,6 +220,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 0C24A2A7747A18E0525C5E7D /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -209,6 +256,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; }; + 7FDF1B0DB9C66D50FD638659 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -512,7 +581,6 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..949b678 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + BuildSystemType + Original + + diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 37bec47..d541d6d 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -2,6 +2,12 @@ + io.flutter.embedded_views_preview + + LSApplicationCategoryType + + GADApplicationIdentifier + ca-app-pub-3940256099942544~1458002511 CFBundleDevelopmentRegion en CFBundleExecutable diff --git a/example/lib/main.dart b/example/lib/main.dart index 02343d8..2cacfb1 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,9 +1,11 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:admob_flutter/admob_flutter.dart'; void main() { - Admob.initialize('ca-app-pub-3940256099942544~3347511713'); + Admob.initialize(getAppId()); runApp(MyApp()); } @@ -24,7 +26,7 @@ class _MyAppState extends State { bannerSize = AdmobBannerSize.BANNER; interstitialAd = AdmobInterstitial( - adUnitId: 'ca-app-pub-3940256099942544/1033173712', + adUnitId: getInterstitialAdUnitId(), listener: (AdmobAdEvent event, Map args) { if (event == AdmobAdEvent.closed) interstitialAd.load(); handleEvent(event, args, 'Interstitial'); @@ -32,7 +34,7 @@ class _MyAppState extends State { ); rewardAd = AdmobReward( - adUnitId: 'ca-app-pub-3940256099942544/5224354917', + adUnitId: getRewardBasedVideoAdUnitId(), listener: (AdmobAdEvent event, Map args) { if (event == AdmobAdEvent.closed) rewardAd.load(); handleEvent(event, args, 'Reward'); @@ -199,7 +201,7 @@ class _MyAppState extends State { Container( margin: EdgeInsets.only(bottom: 20.0), child: AdmobBanner( - adUnitId: 'ca-app-pub-3940256099942544/6300978111', + adUnitId: getBannerAdUnitId(), adSize: bannerSize, listener: (AdmobAdEvent event, Map args) { @@ -233,3 +235,62 @@ class _MyAppState extends State { super.dispose(); } } + +/* +Test Id's from: +https://developers.google.com/admob/ios/banner +https://developers.google.com/admob/android/banner + +App Id +Android: ca-app-pub-3940256099942544~3347511713 +iOS: ca-app-pub-3940256099942544~1458002511 + +Banner +Android: ca-app-pub-3940256099942544/6300978111 +iOS: ca-app-pub-3940256099942544/2934735716 + +Interstitial +Android: ca-app-pub-3940256099942544/1033173712 +iOS: ca-app-pub-3940256099942544/4411468910 + +Reward Video +Android: ca-app-pub-3940256099942544/5224354917 +iOS: ca-app-pub-3940256099942544/1712485313 +*/ + +String getAppId() { + if (Platform.isIOS) { + return 'ca-app-pub-3940256099942544~1458002511'; + } else if (Platform.isAndroid) { + return 'ca-app-pub-3940256099942544~3347511713'; + } + return null; +} + +String getBannerAdUnitId() { + if (Platform.isIOS) { + return 'ca-app-pub-3940256099942544/2934735716'; + } else if (Platform.isAndroid) { + return 'ca-app-pub-3940256099942544/6300978111'; + } + return null; +} + +String getInterstitialAdUnitId() { + if (Platform.isIOS) { + return 'ca-app-pub-3940256099942544/4411468910'; + } else if (Platform.isAndroid) { + return 'ca-app-pub-3940256099942544/1033173712'; + } + return null; +} + +String getRewardBasedVideoAdUnitId() { + if (Platform.isIOS) { + return 'ca-app-pub-3940256099942544/1712485313'; + } else if (Platform.isAndroid) { + return 'ca-app-pub-3940256099942544/5224354917'; + } + return null; +} + diff --git a/ios/Classes/AdmobBanner.swift b/ios/Classes/AdmobBanner.swift index 328bb0d..95d7a35 100644 --- a/ios/Classes/AdmobBanner.swift +++ b/ios/Classes/AdmobBanner.swift @@ -1,8 +1,134 @@ -// -// AdmobBanner.swift -// admob_flutter -// -// Created by Youssef Kababe on 12/9/18. -// +/* + ISC License + + Copyright (c) 2019 Kevin McGill + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ import Foundation +import GoogleMobileAds + +class AdmobBanner : NSObject, FlutterPlatformView { + + private let channel: FlutterMethodChannel + private let messeneger: FlutterBinaryMessenger + private let frame: CGRect + private let viewId: Int64 + private let args: [String: Any] + private var adView: GADBannerView? + + init(frame: CGRect, viewId: Int64, args: [String: Any], messeneger: FlutterBinaryMessenger) { + self.args = args + self.messeneger = messeneger + self.frame = frame + self.viewId = viewId + channel = FlutterMethodChannel(name: "admob_flutter/banner_\(viewId)", binaryMessenger: messeneger) + } + + func view() -> UIView { + return getBannerAdView() ?? UIView() + } + + fileprivate func dispose() { + adView?.removeFromSuperview() + adView = nil + channel.setMethodCallHandler(nil) + } + + fileprivate func getBannerAdView() -> GADBannerView? { + if adView == nil { + adView = GADBannerView() + adView!.rootViewController = UIApplication.shared.keyWindow?.rootViewController + adView!.frame = self.frame.width == 0 ? CGRect(x: 0, y: 0, width: 1, height: 1) : self.frame + adView!.adUnitID = self.args["adUnitId"] as? String ?? "ca-app-pub-3940256099942544/2934735716" + channel.setMethodCallHandler { [weak self] (flutterMethodCall: FlutterMethodCall, flutterResult: FlutterResult) in + switch flutterMethodCall.method { + case "setListener": + self?.adView?.delegate = self + break + case "dispose": + self?.dispose() + break + default: + flutterResult(FlutterMethodNotImplemented) + } + } + requestAd() + } + + return adView + } + + fileprivate func requestAd() { + if let ad = getBannerAdView() { + let request = GADRequest() + request.testDevices = [kGADSimulatorID] + ad.load(request) + } + } + + fileprivate func getSize() -> GADAdSize { + let size = args["adSize"] as? [String: Any] + let width = size!["width"] as? Int ?? 0 + let height = size!["height"] as? Int ?? 0 + let name = size!["name"] as! String + + switch name { + case "BANNER": + return kGADAdSizeBanner + case "LARGE_BANNER": + return kGADAdSizeLargeBanner + case "MEDIUM_RECTANGLE": + return kGADAdSizeMediumRectangle + case "FULL_BANNER": + return kGADAdSizeFullBanner + case "LEADERBOARD": + return kGADAdSizeLeaderboard + case "SMART_BANNER": + // TODO: Do we need Landscape too? + return kGADAdSizeSmartBannerPortrait + default: + return GADAdSize.init(size: CGSize(width: width, height: height), flags: 0) + } + } + +} + +extension AdmobBanner : GADBannerViewDelegate { + func adViewDidReceiveAd(_ bannerView: GADBannerView) { + channel.invokeMethod("loaded", arguments: nil) + } + + func adView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: GADRequestError) { + channel.invokeMethod("failedToLoad", arguments: error) + } + + /// Tells the delegate that a full screen view will be presented in response to the user clicking on + /// an ad. The delegate may want to pause animations and time sensitive interactions. + func adViewWillPresentScreen(_ bannerView: GADBannerView) { + channel.invokeMethod("clicked", arguments: nil) + channel.invokeMethod("opened", arguments: nil) + } + + // TODO: not sure this exists on iOS. + // channel.invokeMethod("impression", null) + + func adViewWillLeaveApplication(_ bannerView: GADBannerView) { + channel.invokeMethod("leftApplication", arguments: nil) + } + + func adViewDidDismissScreen(_ bannerView: GADBannerView) { + channel.invokeMethod("closed", arguments: nil) + } +} diff --git a/ios/Classes/AdmobBannerFactory.swift b/ios/Classes/AdmobBannerFactory.swift index 932b07b..4f5af53 100644 --- a/ios/Classes/AdmobBannerFactory.swift +++ b/ios/Classes/AdmobBannerFactory.swift @@ -1,8 +1,41 @@ -// -// AdmobBannerFactory.swift -// admob_flutter -// -// Created by Youssef Kababe on 12/9/18. -// +/* + ISC License + + Copyright (c) 2019 Kevin McGill + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +import Flutter import Foundation + +class AdmobBannerFactory : NSObject, FlutterPlatformViewFactory { + let messeneger: FlutterBinaryMessenger + + init(messeneger: FlutterBinaryMessenger) { + self.messeneger = messeneger + } + + func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { + return AdmobBanner( + frame: frame, + viewId: viewId, + args: args as? [String : Any] ?? [:], + messeneger: messeneger + ) + } + + func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { + return FlutterStandardMessageCodec.sharedInstance() + } +} diff --git a/ios/Classes/AdmobFlutterPlugin.h b/ios/Classes/AdmobFlutterPlugin.h index 5d9cf35..11d17e4 100644 --- a/ios/Classes/AdmobFlutterPlugin.h +++ b/ios/Classes/AdmobFlutterPlugin.h @@ -1,3 +1,21 @@ +/* + ISC License + + Copyright (c) 2019 Kevin McGill + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + #import @interface AdmobFlutterPlugin : NSObject diff --git a/ios/Classes/AdmobFlutterPlugin.m b/ios/Classes/AdmobFlutterPlugin.m index d2fe408..53525c2 100644 --- a/ios/Classes/AdmobFlutterPlugin.m +++ b/ios/Classes/AdmobFlutterPlugin.m @@ -1,8 +1,29 @@ +/* + ISC License + + Copyright (c) 2019 Kevin McGill + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + #import "AdmobFlutterPlugin.h" #import + @implementation AdmobFlutterPlugin + (void)registerWithRegistrar:(NSObject*)registrar { - [SwiftAdmobFlutterPlugin registerWithRegistrar:registrar]; + [SwiftAdmobFlutterPlugin registerWithRegistrar: registrar]; + [AdmobIntersitialPlugin registerWithRegistrar: registrar]; + [AdmobRewardPlugin registerWithRegistrar: registrar]; } @end diff --git a/ios/Classes/AdmobInterstitialPlugin.swift b/ios/Classes/AdmobInterstitialPlugin.swift new file mode 100644 index 0000000..25dc631 --- /dev/null +++ b/ios/Classes/AdmobInterstitialPlugin.swift @@ -0,0 +1,140 @@ +/* + ISC License + + Copyright (c) 2019 Kevin McGill + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +import Flutter +import Foundation +import GoogleMobileAds + +public class AdmobIntersitialPlugin: NSObject, FlutterPlugin { + + fileprivate var allIds: [Int: GADInterstitial] = [:] + fileprivate var delegates: [Int: GADInterstitialDelegate] = [:] + fileprivate var pluginRegistrar: FlutterPluginRegistrar? + + fileprivate var interstantialAdUnitId: String? + fileprivate var interstitialChannel: FlutterMethodChannel? + + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = AdmobIntersitialPlugin() + instance.pluginRegistrar = registrar + let channel = FlutterMethodChannel(name: "admob_flutter/interstitial", binaryMessenger: registrar.messenger()) + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String : Any] else { + result(FlutterError(code: "Missing args!", message: "Unable to convert args to [String : Any]", details: nil)) + return + } + let id = args["id"] as? Int ?? 0 + let adUnitId = args["adUnitId"] as? String ?? "ca-app-pub-3940256099942544/1033173712" + + switch call.method { + case "setListener": + let channel = FlutterMethodChannel(name: "admob_flutter/interstitial_\(id)", binaryMessenger: pluginRegistrar!.messenger()) + delegates[id] = AdmobIntersitialPluginDelegate(channel: channel) + let interstantialAd = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) + interstantialAd.delegate = delegates[id] + break + case "load": + allIds[id] = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) + loadInterstantialAd(id: id, interstantialAdUnitId: adUnitId) + result(nil) + break + case "isLoaded": + let interstitial = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) + result(interstitial.isReady && !interstitial.hasBeenUsed) + break + case "show": + let interstitial = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) + if interstitial.isReady && !interstitial.hasBeenUsed, let rootViewController = UIApplication.shared.keyWindow?.rootViewController { + interstitial.present(fromRootViewController: rootViewController) + } else { + result(FlutterError(code: "Interstitial Error", message: "Failed to present interstitial", details: nil)) + } + break + case "dispose": + allIds.removeValue(forKey: id) + delegates.removeValue(forKey: id) + break + default: + result(FlutterMethodNotImplemented) + } + } + + private func loadInterstantialAd(id: Int, interstantialAdUnitId: String) { + let interstantial = getInterstitialAd(id: id, interstantialAdUnitId: interstantialAdUnitId) + let request = GADRequest() + request.testDevices = [kGADSimulatorID] + interstantial.load(request) + } + + private func getInterstitialAd(id: Int, interstantialAdUnitId: String) -> GADInterstitial { + if let interstantialAd = allIds[id] { + if (interstantialAd.hasBeenUsed) { + let interstantialAd = GADInterstitial(adUnitID: interstantialAdUnitId) + allIds[id] = interstantialAd + } + } else { + let interstantialAd = GADInterstitial(adUnitID: interstantialAdUnitId) + allIds[id] = interstantialAd + } + + return allIds[id]! + } +} + +class AdmobIntersitialPluginDelegate: NSObject, GADInterstitialDelegate { + let channel: FlutterMethodChannel + + init(channel: FlutterMethodChannel) { + self.channel = channel + } + + // TODO: not sure this exists on iOS. + // channel.invokeMethod("impression", null) + + func interstitialWillPresentScreen(_ ad: GADInterstitial) { + channel.invokeMethod("clicked", arguments: nil) + channel.invokeMethod("opened", arguments: nil) + } + + func interstitialWillDismissScreen(_ ad: GADInterstitial) { + // Unused + } + + func interstitialDidDismissScreen(_ ad: GADInterstitial) { + channel.invokeMethod("closed", arguments: nil) + } + + func interstitialWillLeaveApplication(_ ad: GADInterstitial) { + channel.invokeMethod("leftApplication", arguments: nil) + } + + func interstitialDidReceiveAd(_ ad: GADInterstitial) { + channel.invokeMethod("loaded", arguments: nil) + } + + func interstitial(_ ad: GADInterstitial, didFailToReceiveAdWithError error: GADRequestError) { + channel.invokeMethod("failedToLoad", arguments: ["errorCode": error]) + } + + func interstitialDidFail(toPresentScreen ad: GADInterstitial) { + channel.invokeMethod("failedToLoad", arguments: ["errorCode": ad.isReady && ad.hasBeenUsed]) + } +} diff --git a/ios/Classes/AdmobRewardPlugin.swift b/ios/Classes/AdmobRewardPlugin.swift new file mode 100644 index 0000000..ecc50a5 --- /dev/null +++ b/ios/Classes/AdmobRewardPlugin.swift @@ -0,0 +1,149 @@ +/* + ISC License + + Copyright (c) 2019 Kevin McGill + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +import Flutter +import Foundation +import GoogleMobileAds + +public class AdmobRewardPlugin: NSObject, FlutterPlugin { + + fileprivate var allIds: [Int: GADRewardBasedVideoAd] = [:] + fileprivate var delegates: [Int: GADRewardBasedVideoAdDelegate] = [:] + fileprivate var pluginRegistrar: FlutterPluginRegistrar? + + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = AdmobRewardPlugin() + instance.pluginRegistrar = registrar + let rewardChannel = FlutterMethodChannel(name: "admob_flutter/reward", binaryMessenger: registrar.messenger()) + registrar.addMethodCallDelegate(instance, channel: rewardChannel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String : Any] else { + result(FlutterError( + code: "Missing args!", + message: "Unable to convert args to [String : Any]", + details: nil) + ) + return + } + let id = args["id"] as? Int ?? 0 + let adUnitId = args["adUnitId"] as? String ?? "ca-app-pub-3940256099942544/1712485313" + + switch call.method { + case "setListener": + let channel = FlutterMethodChannel( + name: "admob_flutter/reward_\(id)", + binaryMessenger: pluginRegistrar!.messenger() + ) + delegates[id] = AdmobRewardPluginDelegate(channel: channel) + let rewardVideo = getRewardBasedVideoAd(id: id) + rewardVideo.delegate = delegates[id] + break + case "load": + loadRewardBasedVideoAd(id: id, rewardBasedVideoAdUnitId: adUnitId) + result(nil) + break + case "isLoaded": + let isReady = getRewardBasedVideoAd(id: id).isReady + result(isReady) + break + case "show": + let rewardVideo = getRewardBasedVideoAd(id: id) + if rewardVideo.isReady, let rootViewController = UIApplication.shared.keyWindow?.rootViewController { + rewardVideo.present(fromRootViewController: rootViewController) + } else { + result(FlutterError( + code: "GADRewardBasedVideoAd Error", + message: "Failed to present reward video", + details: nil) + ) + } + break + case "dispose": + allIds.removeValue(forKey: id) + delegates.removeValue(forKey: id) + break + default: + result(FlutterMethodNotImplemented) + } + } + + private func loadRewardBasedVideoAd(id: Int, rewardBasedVideoAdUnitId: String) { + let interstantial = getRewardBasedVideoAd(id: id) + let request = GADRequest() + request.testDevices = [kGADSimulatorID] + interstantial.load(request, withAdUnitID: rewardBasedVideoAdUnitId) + } + + private func getRewardBasedVideoAd(id: Int) -> GADRewardBasedVideoAd { + if allIds[id] == nil { + let rewardBadedVideoAd = GADRewardBasedVideoAd.sharedInstance() + allIds[id] = rewardBadedVideoAd + } + + return allIds[id]! + } +} + +class AdmobRewardPluginDelegate: NSObject, GADRewardBasedVideoAdDelegate { + let channel: FlutterMethodChannel + + init(channel: FlutterMethodChannel) { + self.channel = channel + } + + func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd, didRewardUserWith reward: GADAdReward) { + channel.invokeMethod("rewarded", arguments: [ + "type": reward.type, + "amount": reward.amount + ]) + } + + func rewardBasedVideoAdDidOpen(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("opened", arguments: nil) + } + + func rewardBasedVideoAdDidClose(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("closed", arguments: nil) + } + + func rewardBasedVideoAdDidReceive(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("loaded", arguments: nil) + } + + func rewardBasedVideoAdDidStartPlaying(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("started", arguments: nil) + } + + func rewardBasedVideoAdMetadataDidChange(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + // UNUSED + } + + func rewardBasedVideoAdDidCompletePlaying(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("completed", arguments: nil) + } + + func rewardBasedVideoAdWillLeaveApplication(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("leftApplication", arguments: nil) + } + + func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd, didFailToLoadWithError error: Error) { + channel.invokeMethod("failedToLoad", arguments: ["errorCode": error.localizedDescription]) + } +} diff --git a/ios/Classes/SwiftAdmobFlutterPlugin.swift b/ios/Classes/SwiftAdmobFlutterPlugin.swift index d65bf45..c2d2ca4 100644 --- a/ios/Classes/SwiftAdmobFlutterPlugin.swift +++ b/ios/Classes/SwiftAdmobFlutterPlugin.swift @@ -1,14 +1,45 @@ -import Flutter +/* + ISC License + + Copyright (c) 2019 Kevin McGill + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + import UIKit +import GoogleMobileAds public class SwiftAdmobFlutterPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "admob_flutter", binaryMessenger: registrar.messenger()) let instance = SwiftAdmobFlutterPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) + + let defaultChannel = FlutterMethodChannel(name: "admob_flutter", binaryMessenger: registrar.messenger()) + registrar.addMethodCallDelegate(instance, channel: defaultChannel) + + registrar.register( + AdmobBannerFactory(messeneger: registrar.messenger()), + withId: "admob_flutter/banner" + ) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - result("iOS " + UIDevice.current.systemVersion) + switch call.method { + case "initialize": + GADMobileAds.sharedInstance().start(completionHandler: nil) + break + default: + result(FlutterMethodNotImplemented) + } } } diff --git a/ios/admob_flutter.podspec b/ios/admob_flutter.podspec index 13c8c82..7970498 100644 --- a/ios/admob_flutter.podspec +++ b/ios/admob_flutter.podspec @@ -3,19 +3,22 @@ # Pod::Spec.new do |s| s.name = 'admob_flutter' - s.version = '0.0.1' - s.summary = 'A new flutter plugin project.' + s.version = '0.3.0' + s.summary = 'Admob plugin that shows banner ads using native platform views.' s.description = <<-DESC -A new flutter plugin project. +Admob plugin that shows banner ads using native platform views. DESC - s.homepage = 'http://example.com' + s.homepage = 'https://github.com/YoussefKababe/admob_flutter' s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } + s.author = { 'Kevin McGill' => 'kevin@mcgilldevtech.com' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' + s.dependency 'Firebase/Core' + s.dependency 'Firebase/AdMob' s.ios.deployment_target = '8.0' + s.static_framework = true end diff --git a/pubspec.yaml b/pubspec.yaml index 6458d7d..b74f39b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: admob_flutter description: Admob plugin that shows banner ads using native platform views. -version: 0.2.0 +version: 0.3.0 author: Youssef Kababe homepage: https://github.com/YoussefKababe/admob_flutter