Swift Equivalent of Objective-C for Flutter Native Ads

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:

  1. Open the file containing GADNativeAdView, which is GADNativeAd.h.
  2. Select all, copy to clipboard.
  3. Create a new objective C header file in your project, give name ex: GADNativeAdCopy.h.
  4. Paste the contents from the clipboard.
  5. If you're using Swift, then import GADNativeAdCopy.h into bridging header.
  6. Now the IBOutlets of GADNativeAdView will automatically appear on Inspectors when you click on the view.
  7. Add some components, like labels and images, and connect them to some IBOutlets like headline, body, or icon.
  8. After that, remove the GADNativeAdCopy.h from the bridging header import statement. --> you need to do this otherwise your project won't compile.
  9. 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



Leave a reply



Submit