Swift Equivalent of Objective-C for Flutter Native Ads
I found the solution.
https://codelabs.developers.google.com/codelabs/admob-inline-ads-in-flutter#7
You can look this section: Implement NativeAdFactory for iOS (Swift)
ListTileNativeAdFactory.swift:
// TODO: Import google_mobile_ads
import google_mobile_ads
// TODO: Implement ListTileNativeAdFactory
class ListTileNativeAdFactory : FLTNativeAdFactory {
func createNativeAd(_ nativeAd: GADNativeAd,
customOptions: [AnyHashable : Any]? = nil) -> GADNativeAdView? {
let nibView = Bundle.main.loadNibNamed("ListTileNativeAdView", owner: nil, options: nil)!.first
let nativeAdView = nibView as! GADNativeAdView
(nativeAdView.headlineView as! UILabel).text = nativeAd.headline
(nativeAdView.bodyView as! UILabel).text = nativeAd.body
nativeAdView.bodyView!.isHidden = nativeAd.body == nil
(nativeAdView.iconView as! UIImageView).image = nativeAd.icon?.image
nativeAdView.iconView!.isHidden = nativeAd.icon == nil
nativeAdView.callToActionView?.isUserInteractionEnabled = false
nativeAdView.nativeAd = nativeAd
return nativeAdView
}
}
AppDelegate.swift:
import UIKit
import Flutter
// TODO: Import google_mobile_ads
import google_mobile_ads
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
// TODO: Register ListTileNativeAdFactory
let listTileFactory = ListTileNativeAdFactory()
FLTGoogleMobileAdsPlugin.registerNativeAdFactory(
self, factoryId: "listTile", nativeAdFactory: listTileFactory)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
For flutter implementation you can look further in this link.
https://codelabs.developers.google.com/codelabs/admob-inline-ads-in-flutter#7
Besides,
You can use xib file at official github
https://github.com/googleads/googleads-mobile-flutter/blob/main/packages/google_mobile_ads/example/ios/Runner/NativeAdView.xib
ListTileNativeAdView.xib:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment version="2048" identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="GADNativeAdView">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="iNa-bH-h1m">
<rect key="frame" x="15" y="15.5" width="40" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="ICz-3W-FQf"/>
<constraint firstAttribute="width" constant="40" id="vY6-8D-xIn"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Advertiser" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GTT-Yh-eSq">
<rect key="frame" x="63" y="38.5" width="66.5" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" systemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" placeholderIntrinsicWidth="100" placeholderIntrinsicHeight="17" translatesAutoresizingMaskIntoConstraints="NO" id="2Of-AP-0h9">
<rect key="frame" x="129.5" y="38.5" width="100" height="17"/>
<constraints>
<constraint firstAttribute="height" constant="17" id="jBW-Cz-Kyc"/>
<constraint firstAttribute="width" constant="100" id="sXk-zk-NI0"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="Body that is really really long and can take up to two lines or sometimes even more." textAlignment="justified" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PEQ-D9-2Vv">
<rect key="frame" x="15" y="63.5" width="350" height="33.5"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" systemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="E5w-YA-UY8">
<rect key="frame" x="318" y="259.5" width="47" height="34"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<state key="normal" title="Install">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Price" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ysb-of-cat">
<rect key="frame" x="230" y="268" width="33" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" systemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Store" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hwF-UL-Q8H">
<rect key="frame" x="273" y="268" width="35" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" systemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="Headline" textAlignment="justified" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="beR-eV-DX1">
<rect key="frame" x="63" y="10" width="297" height="20.5"/>
<constraints>
<constraint firstAttribute="height" constant="20.5" id="6r8-Hu-d0y"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" systemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="fNp-yu-K4i" customClass="GADMediaView">
<rect key="frame" x="62.5" y="102" width="250" height="150"/>
<constraints>
<constraint firstAttribute="height" priority="750" constant="150" id="71m-kn-7Ug"/>
<constraint firstAttribute="width" constant="250" id="e3T-fD-di4"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Ad" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lp1-oz-XOs">
<rect key="frame" x="0.0" y="0.0" width="15" height="15"/>
<color key="backgroundColor" red="1" green="0.80000001190000003" blue="0.40000000600000002" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="15" id="Twa-Vk-uWQ"/>
<constraint firstAttribute="height" constant="15" id="k8m-kJ-CF5"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="11"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="0.98303861469999998" blue="0.92887652860000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="GTT-Yh-eSq" firstAttribute="leading" secondItem="beR-eV-DX1" secondAttribute="leading" id="0sB-Mk-EU6"/>
<constraint firstItem="lp1-oz-XOs" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="3lA-qv-Nkc"/>
<constraint firstItem="Ysb-of-cat" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="3pc-w6-uy1"/>
<constraint firstItem="PEQ-D9-2Vv" firstAttribute="top" relation="greaterThanOrEqual" secondItem="iNa-bH-h1m" secondAttribute="bottom" id="4S3-p0-z6A"/>
<constraint firstAttribute="trailing" secondItem="PEQ-D9-2Vv" secondAttribute="trailing" constant="10" id="8U0-Fb-3R7"/>
<constraint firstItem="iNa-bH-h1m" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="15" id="9WK-zC-xET"/>
<constraint firstAttribute="trailing" secondItem="beR-eV-DX1" secondAttribute="trailing" constant="15" id="BcE-do-dNl"/>
<constraint firstItem="lp1-oz-XOs" firstAttribute="left" secondItem="iN0-l3-epB" secondAttribute="left" id="BpX-yC-PZG"/>
<constraint firstItem="PEQ-D9-2Vv" firstAttribute="top" secondItem="2Of-AP-0h9" secondAttribute="bottom" constant="8" symbolic="YES" id="CCg-xe-cKg"/>
<constraint firstItem="2Of-AP-0h9" firstAttribute="top" secondItem="beR-eV-DX1" secondAttribute="bottom" constant="8" symbolic="YES" id="ESC-Pe-TXR"/>
<constraint firstItem="iNa-bH-h1m" firstAttribute="bottom" secondItem="2Of-AP-0h9" secondAttribute="bottom" id="GwM-y0-1du"/>
<constraint firstItem="beR-eV-DX1" firstAttribute="leading" secondItem="iNa-bH-h1m" secondAttribute="trailing" constant="8" symbolic="YES" id="MRN-dd-Oip"/>
<constraint firstItem="2Of-AP-0h9" firstAttribute="leading" secondItem="GTT-Yh-eSq" secondAttribute="trailing" id="Med-Nd-wEo"/>
<constraint firstItem="beR-eV-DX1" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="10" id="Mvs-eV-Wzb"/>
<constraint firstItem="Ysb-of-cat" firstAttribute="centerY" secondItem="hwF-UL-Q8H" secondAttribute="centerY" id="Rud-i8-Myz"/>
<constraint firstItem="fNp-yu-K4i" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="TYN-lq-3DK"/>
<constraint firstItem="fNp-yu-K4i" firstAttribute="top" secondItem="PEQ-D9-2Vv" secondAttribute="bottom" constant="5" id="V0m-hf-6NS"/>
<constraint firstItem="GTT-Yh-eSq" firstAttribute="centerY" secondItem="2Of-AP-0h9" secondAttribute="centerY" id="YgR-kp-age"/>
<constraint firstItem="hwF-UL-Q8H" firstAttribute="leading" secondItem="Ysb-of-cat" secondAttribute="trailing" constant="10" id="aLb-sm-wAb"/>
<constraint firstAttribute="right" relation="greaterThanOrEqual" secondItem="lp1-oz-XOs" secondAttribute="right" constant="20" symbolic="YES" id="czi-qD-IaJ"/>
<constraint firstAttribute="trailing" secondItem="E5w-YA-UY8" secondAttribute="trailing" constant="10" id="eNM-dN-tvx"/>
<constraint firstItem="E5w-YA-UY8" firstAttribute="leading" secondItem="hwF-UL-Q8H" secondAttribute="trailing" constant="10" id="f39-vH-KWq"/>
<constraint firstItem="iNa-bH-h1m" firstAttribute="leading" secondItem="PEQ-D9-2Vv" secondAttribute="leading" id="mof-5F-8vM"/>
<constraint firstItem="E5w-YA-UY8" firstAttribute="centerY" secondItem="hwF-UL-Q8H" secondAttribute="centerY" id="rNj-VY-YrO"/>
<constraint firstItem="E5w-YA-UY8" firstAttribute="top" secondItem="fNp-yu-K4i" secondAttribute="bottom" constant="7.5" id="rup-e7-1CR"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="E5w-YA-UY8" secondAttribute="bottom" constant="20" symbolic="YES" id="uEI-XT-igi"/>
</constraints>
<connections>
<outlet property="advertiserView" destination="GTT-Yh-eSq" id="bY8-5O-6fF"/>
<outlet property="bodyView" destination="PEQ-D9-2Vv" id="Gpd-Q6-Byv"/>
<outlet property="callToActionView" destination="E5w-YA-UY8" id="RCf-yK-s1x"/>
<outlet property="headlineView" destination="beR-eV-DX1" id="d1E-ed-yel"/>
<outlet property="iconView" destination="iNa-bH-h1m" id="gIe-xy-iwm"/>
<outlet property="mediaView" destination="fNp-yu-K4i" id="624-ZP-L04"/>
<outlet property="priceView" destination="Ysb-of-cat" id="L6Q-hd-uaJ"/>
<outlet property="starRatingView" destination="2Of-AP-0h9" id="zCO-9D-S0V"/>
<outlet property="storeView" destination="hwF-UL-Q8H" id="hRl-23-ce1"/>
</connections>
<point key="canvasLocation" x="13.768115942028986" y="-5.6919642857142856"/>
</view>
</objects>
<resources>
<systemColor name="darkTextColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="darkTextColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="darkTextColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="darkTextColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="darkTextColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
Why there's no IBOutlet on GADNativeView when implementing Google Admob Native Ads on iOS?
After much looking around, I arrive at this comment on this answer to this question, which points to a google group discussion about the same thing in 2021. In a reply, Google developer said that they're aware of this issue, which is basically Apple's problem, and there's nothing they can do about it except wait for Apple to fix it. Read the whole thread for the entire story.
But to work around this issue, here are the steps:
- Open the file containing
GADNativeAdView
, which isGADNativeAd.h
. - Select all, copy to clipboard.
- Create a new objective C header file in your project, give name ex:
GADNativeAdCopy.h
. - Paste the contents from the clipboard.
- If you're using Swift, then import
GADNativeAdCopy.h
into bridging header. - Now the
IBOutlet
s ofGADNativeAdView
will automatically appear on Inspectors when you click on the view. - Add some components, like labels and images, and connect them to some IBOutlets like headline, body, or icon.
- After that, remove the
GADNativeAdCopy.h
from the bridging header import statement. --> you need to do this otherwise your project won't compile. - The
.xib
GADNativeAdView
view will still retain its outlets even after you remove the import statement. (the import statement only needed to make the outlets appear, so if the other unconnected outlets suddenly disappear, just re-add the bridging header import)
Note: You will still need to import google_mobile_ads
on ListTileNativeAdFactory.swift
.
This is a Question and Answer post to let other users know the solution, because the right search results don't appear easily when I scoured the web earlier.
How to implement a FlutterPlugin's method handler in Swift?
I've managed to figure it out by myself by reading some examples of Swift code calling Objective-C and inspecting the Objective-C sources.
Here's how I implemented it:
import Flutter
import UIKit
import Mobileapi
public class SwiftGohashMobilePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "gohash_mobile", binaryMessenger: registrar.messenger())
let instance = SwiftGohashMobilePlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getDb":
if let args = call.arguments as? [String] {
if args.count == 2 {
var error : NSError?
let db = MobileapiReadDatabase(args[0], args[1], &error)
if let errorMessage = error?.userInfo.description {
result(FlutterError.init(code: "NATIVE_ERR",
message: "Error: " + errorMessage,
details: nil))
} else {
// SUCCESS!!
result(db!)
}
} else {
result(FlutterError.init(code: "BAD_ARGS",
message: "Wrong arg count (getDb expects 2 args): " + args.count.description,
details: nil))
}
} else {
result(FlutterError.init(code: "BAD_ARGS",
message: "Wrong argument types",
details: nil))
}
default:
result(FlutterMethodNotImplemented)
}
}
}
Suggestions for improvements are welcome!
Related Topics
Scenekit Ar Game Fps Getting Low and The Device Getting Hot with Use
Swift - Nsdate - Remove Part of Date
Sound for Scene Transition, That Doesn't Stutter
How to Dynamically Hide Navigation Back Button in Swiftui
Swift 3 Custom Extension of Ns Measurement? Ex. Sheeps to Goats
Swift - Connect Delegate to Custom Xib Cell
Swift Preserve UIswitch State on UIlongpress
Swift: Check Which Value in Nsarray Is Closest to Another Given Value
Shortest Code to Create an Array of Random Numbers in Swift
Change Color of Row Programmatically in Watchkit
Generating Random Doable Math Problems Swift
Can't Set @Ibinspectable Computed Property in UIview
Swiftui Section from Attribute of a Struct
Calling Consecutive Animations in Swift with Completion Handler