Slide 1

Slide 1 text

Copyright ©︎ 2025 Takashi Ushikoshi overlayPreferenceValue Ͱ࣮ݱ͢Δ ϐϡΞ SwiftUI ͳ AdMob ωΠςΟϒ޿ࠂ extension DC 2025 Day 3

Slide 2

Slide 2 text

Copyright ©︎ 2025 Takashi Ushikoshi ڇӽɹਹ (Takashi Ushikoshi) @Ukokkei95Toyama ݸਓࣄۀओ (2025೥2݄ - ݱࡏ) - iOS / Android ΤϯδχΞ աڈͷ࡞඼
 ূ໌ࣸਅΞϓϦ ࡳຈੜ·Εࡳຈҭͪ, ΠϯυωγΞ޷͖

Slide 3

Slide 3 text

AdMob ͸ϐϡΞ SwiftUI ʹඇରԠ

Slide 4

Slide 4 text

Copyright ©︎ 2025 Takashi Ushikoshi SwiftUI ϦϦʔε͔Β6೥ɺࠓ΋ UIViewRepresent a ble Λ࢖ͬ ͨ࡞ΓํͰҊ಺͞Ε͍ͯΔ https://developers.google.com/admob/ios/native/advanced?hl=ja#display_the_ad

Slide 5

Slide 5 text

͡Ό͋ϥΠϒϥϦ࡞ͬͨΖ👀

Slide 6

Slide 6 text

Copyright ©︎ 2025 Takashi Ushikoshi ໨ࢦͨ͠APIσβΠϯ NativeAdvertisement(adUnitId: "ca-pub-xxxx") { loadedAd in VStack { if let headline = loadedAd?.headline { Text(headline) } if let icon = loadedAd?.icon { Image(uiImage: icon.image) } } }

Slide 7

Slide 7 text

NativeAdvertisement(adUnitId: "ca-pub-xxxx") { loadedAd in VStack { if let headline = loadedAd?.headline { Text(headline) } if let icon = loadedAd?.icon { Image(uiImage: icon.image) } } } Copyright ©︎ 2025 Takashi Ushikoshi ໨ࢦͨ͠APIσβΠϯ ޿ࠂϢχοτIDΛୈҰҾ਺ʹͱΔ

Slide 8

Slide 8 text

NativeAdvertisement(adUnitId: "ca-pub-xxxx") { loadedAd in VStack { if let headline = loadedAd?.headline { Text(headline) } if let icon = loadedAd?.icon { Image(uiImage: icon.image) } } } Copyright ©︎ 2025 Takashi Ushikoshi ໨ࢦͨ͠APIσβΠϯ ୈೋҾ਺ͷΫϩʔδϟͰɺ޿ࠂͷ ςϯϓϨʔτΛߏ੒

Slide 9

Slide 9 text

SwiftUI ͸ࢠ View ͷΠϯελϯεʹ ௚઀ΞΫηεͰ͖ͳ͍

Slide 10

Slide 10 text

Copyright ©︎ 2025 Takashi Ushikoshi private struct NativeAdViewContainer: UIViewRepresentable { typealias UIViewType = NativeAdView // Observer to update the UIView when the native ad value changes. @ObservedObject var nativeViewModel: NativeAdViewModel func makeUIView(context: Context) -> NativeAdView { return Bundle.main.loadNibNamed( "NativeAdView", owner: nil, options: nil)?.first as! NativeAdView } func updateUIView(_ nativeAdView: NativeAdView, context: Context) { guard let nativeAd = nativeViewModel.nativeAd else { return } // Each UI property is configurable using your native ad. (nativeAdView.headlineView as? UILabel)?.text = nativeAd.headline nativeAdView.mediaView?.mediaContent = nativeAd.mediaContent (nativeAdView.bodyView as? UILabel)?.text = nativeAd.body (nativeAdView.iconView as? UIImageView)?.image = nativeAd.icon?.image (nativeAdView.starRatingView as? UIImageView)?.image = imageOfStars(from: nativeAd.starRating) (nativeAdView.storeView as? UILabel)?.text = nativeAd.store (nativeAdView.priceView as? UILabel)?.text = nativeAd.price (nativeAdView.advertiserView as? UILabel)?.text = nativeAd.advertiser (nativeAdView.callToActionView as? UIButton)?.setTitle(nativeAd.callToAction, for: .normal) // For the SDK to process touch events properly, user interaction should be disabled. nativeAdView.callToActionView?.isUserInteractionEnabled = false // Associate the native ad view with the native ad object. This is required to make the ad // clickable. // Note: this should always be done after populating the ad views. nativeAdView.nativeAd = nativeAd } } ࢠViewΛҾͬுͬͯ͜͜ʹೖΕࠐΉɺ͕Ͱ͖ͳ͍ https://developers.google.com/admob/ios/native/advanced?hl=ja#display_the_ad

Slide 11

Slide 11 text

🤔

Slide 12

Slide 12 text

ͳΒ͹޿ࠂͷ UIView Λ SwiftUI Ϗϡʔʹ ॏͶͯ͠·͑͹͍͍

Slide 13

Slide 13 text

overlayPreferenceValue ͱ͸

Slide 14

Slide 14 text

Copyright ©︎ 2025 Takashi Ushikoshi overlayPreferenceValue ͱ͸ - overlay ͱ onPreferenceChange ͕߹Θͬͨ͞Α͏ͳAPI - PreferenceKey ͷ࢓૊ΈΛ࢖͍ɺࢠ View ্͕͖͛ͯͨαΠζ΍࠲ඪͷ஋Λ਌ଆͰಡΈऔΓɺ ॏͶ߹ΘͤΔ͜ͱ͕Ͱ͖ΔAPI

Slide 15

Slide 15 text

࣮ࡍͷ׆༻ࣄྫ

Slide 16

Slide 16 text

Copyright ©︎ 2025 Takashi Ushikoshi [SwiftUI] ViewʹεϙοτϥΠτΛ౰ͯΔ - https://zenn.dev/ueshun/ a rticles/3ee837c881905e

Slide 17

Slide 17 text

ग़དྷ্͕ͬͨϥΠϒϥϦ

Slide 18

Slide 18 text

Copyright ©︎ 2025 Takashi Ushikoshi

Slide 19

Slide 19 text

Copyright ©︎ 2025 Takashi Ushikoshi struct ContentView: View { var body: some View { NativeAdvertisement(adUnitId: "ca-pub-xxxx") { loadedAd in VStack { if let headline = loadedAd?.headline { Text(headline) } if let icon = loadedAd?.icon { Image(uiImage: icon.image) } } } } }

Slide 20

Slide 20 text

Copyright ©︎ 2025 Takashi Ushikoshi struct ContentView: View { var body: some View { NativeAdvertisement(adUnitId: "ca-pub-xxxx") { loadedAd in VStack { if let headline = loadedAd?.headline { Text(headline) .nativeAdElement("headline") } if let icon = loadedAd?.icon { Image(uiImage: icon.image) .nativeAdElement("icon") } } } } }

Slide 21

Slide 21 text

Copyright ©︎ 2025 Takashi Ushikoshi public struct NativeAdvertisement: View { @StateObject private var nativeAdLoader: NativeAdLoader private let adUnitId: String @ViewBuilder private let adContent: (_ advertisementPhase: NativeAdvertisementPhase) -> AdContent // தུ public var body: some View { let loadedAd = nativeAdLoader.loadedAd adContent(nativeAdLoader.nativeAdvertisementPhase) .overlayPreferenceValue(TypedAnchorBoundsPreferenceKey.self, alignment: .center) { namedAnchors in GeometryReader { overlayGeometry in let elementFrames: [ElementFrame] = namedAnchors.map { .init( elementType: $0.viewType, frame: overlayGeometry[$0.anchor] ) } _RepresentedUINativeAdView( nativeAd: loadedAd, elementFrames: elementFrames ) .frame(width: overlayGeometry.size.width, height: overlayGeometry.size.height) } } .onAppear { nativeAdLoader.loadAd() } } }

Slide 22

Slide 22 text

Copyright ©︎ 2025 Takashi Ushikoshi adContent(nativeAdLoader.nativeAdvertisementPhase) .overlayPreferenceValue(TypedAnchorBoundsPreferenceKey.self, alignment: .center) { namedAnchors in GeometryReader { overlayGeometry in let elementFrames: [ElementFrame] = namedAnchors.map { .init( elementType: $0.viewType, frame: overlayGeometry[$0.anchor] ) } _RepresentedUINativeAdView( nativeAd: loadedAd, elementFrames: elementFrames ) .frame(width: overlayGeometry.size.width, height: overlayGeometry.size.height) } } .onAppear { nativeAdLoader.loadAd() }

Slide 23

Slide 23 text

Copyright ©︎ 2025 Takashi Ushikoshi adContent(nativeAdLoader.nativeAdvertisementPhase) .overlay(alignment: .center) { _RepresentedUINativeAdView( nativeAd: loadedAd, elementFrames: [] ) .frame(width: overlayGeometry.size.width, height: overlayGeometry.size.height) } .onAppear { nativeAdLoader.loadAd() } }

Slide 24

Slide 24 text

Copyright ©︎ 2025 Takashi Ushikoshi adContent(nativeAdLoader.nativeAdvertisementPhase) .overlayPreferenceValue(TypedAnchorBoundsPreferenceKey.self, alignment: .center) { namedAnchors in GeometryReader { overlayGeometry in let elementFrames: [ElementFrame] = namedAnchors.map { .init( elementType: $0.viewType, frame: overlayGeometry[$0.anchor] ) } _RepresentedUINativeAdView( nativeAd: loadedAd, elementFrames: elementFrames ) .frame(width: overlayGeometry.size.width, height: overlayGeometry.size.height) } } .onAppear { nativeAdLoader.loadAd() }

Slide 25

Slide 25 text

Copyright ©︎ 2025 Takashi Ushikoshi adContent(nativeAdLoader.nativeAdvertisementPhase) .overlayPreferenceValue(TypedAnchorBoundsPreferenceKey.self, alignment: .center) { namedAnchors in GeometryReader { overlayGeometry in let elementFrames: [ElementFrame] = namedAnchors.map { .init( elementType: $0.viewType, frame: overlayGeometry[$0.anchor] ) } _RepresentedUINativeAdView( nativeAd: loadedAd, elementFrames: elementFrames ) .frame(width: overlayGeometry.size.width, height: overlayGeometry.size.height) } } .onAppear { nativeAdLoader.loadAd() }

Slide 26

Slide 26 text

Copyright ©︎ 2025 Takashi Ushikoshi adContent(nativeAdLoader.nativeAdvertisementPhase) .overlayPreferenceValue(TypedAnchorBoundsPreferenceKey.self, alignment: .center) { namedAnchors in GeometryReader { overlayGeometry in let elementFrames: [ElementFrame] = namedAnchors.map { .init( elementType: $0.viewType, frame: overlayGeometry[$0.anchor] ) } _RepresentedUINativeAdView( nativeAd: loadedAd, elementFrames: elementFrames ) .frame(width: overlayGeometry.size.width, height: overlayGeometry.size.height) } } .onAppear { nativeAdLoader.loadAd() } → CGRect

Slide 27

Slide 27 text

Copyright ©︎ 2025 Takashi Ushikoshi adContent(nativeAdLoader.nativeAdvertisementPhase) .overlayPreferenceValue(TypedAnchorBoundsPreferenceKey.self, alignment: .center) { namedAnchors in GeometryReader { overlayGeometry in let elementFrames: [ElementFrame] = namedAnchors.map { .init( elementType: $0.viewType, frame: overlayGeometry[$0.anchor] ) } _RepresentedUINativeAdView( nativeAd: loadedAd, elementFrames: elementFrames ) .frame(width: overlayGeometry.size.width, height: overlayGeometry.size.height) } } .onAppear { nativeAdLoader.loadAd() }

Slide 28

Slide 28 text

Copyright ©︎ 2025 Takashi Ushikoshi adContent(nativeAdLoader.nativeAdvertisementPhase) .overlayPreferenceValue(TypedAnchorBoundsPreferenceKey.self, alignment: .center) { namedAnchors in GeometryReader { overlayGeometry in let elementFrames: [ElementFrame] = namedAnchors.map { .init( elementType: $0.viewType, frame: overlayGeometry[$0.anchor] ) } _RepresentedUINativeAdView( nativeAd: loadedAd, elementFrames: elementFrames ) .frame(width: overlayGeometry.size.width, height: overlayGeometry.size.height) } } .onAppear { nativeAdLoader.loadAd() }

Slide 29

Slide 29 text

AdMobUI github.com/Uhucream/AdMobUI