SwiftUIバージョンの実装は以下を参考に、今回はUIKitでの実装です。
全パターンを網羅するのは手間なので今回はモーダルパターンのみの実装です。
[Swift/Firebase] In-App Messagingのカスタマイズ (SwiftUI)Contents 非表示
実装
Firebase InAppの準備
以下を参考に、検証用のIDが必要です。
[Swift/Firebase] Firebase Installation IDのログを出す方法カスタマイズ用のInAppモーダル管理ファイル
InAppMessagingCustomComponent.swift
import Firebase
import FirebaseInAppMessaging
private enum IAMDisplay {
case unknown
case card(InAppMessagingCardDisplay)
case modal(InAppMessagingModalDisplay)
case banner(InAppMessagingBannerDisplay)
case imageOnly(InAppMessagingImageOnlyDisplay)
init(_ messageForDisplay: InAppMessagingDisplayMessage) {
switch messageForDisplay.type {
case .card:
self = (messageForDisplay as? InAppMessagingCardDisplay).map { .card($0) } ?? .unknown
case .modal:
self = (messageForDisplay as? InAppMessagingModalDisplay).map { .modal($0) } ?? .unknown
case .banner:
self = (messageForDisplay as? InAppMessagingBannerDisplay).map { .banner($0) } ?? .unknown
case .imageOnly:
self = (messageForDisplay as? InAppMessagingImageOnlyDisplay).map { .imageOnly($0) } ?? .unknown
@unknown default:
self = .unknown
}
}
}
final class InAppMessagingCustomComponent: InAppMessagingDisplay {
func displayMessage(_ messageForDisplay: InAppMessagingDisplayMessage, displayDelegate: InAppMessagingDisplayDelegate) {
DispatchQueue.main.async {
displayDelegate.impressionDetected?(for: messageForDisplay)
guard let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene else {
return
}
guard let window = scene.windows.first(where: { $0.isKeyWindow }) else {
return
}
// モーダルタイプの配信時にフロントで用意しているカスタムViewControllerを使用するようにする
switch IAMDisplay(messageForDisplay) {
case let .modal(modal):
InAppMessagingModal.show(
on: window.rootViewController ?? UIViewController(),
title: modal.title,
body: modal.bodyText,
imageURL: URL(string: modal.imageData?.imageURL ?? ""),
buttonTitle: modal.actionButton?.buttonText,
buttonURL: modal.actionURL
)
case .card, .banner, .imageOnly, .unknown:
// モーダルタイプ以外のタイプでの配信は何も表示しない
return
}
}
}
}
InAppMessagingModal.swift
import Foundation
import UIKit
struct InAppMessagingContents {
let title: String
let body: String?
let imageURL: URL?
let buttonTitle: String?
let buttonURL: URL?
}
final class InAppMessagingModal {
static func show(
on viewController: UIViewController,
title: String,
body: String?,
imageURL: URL?,
buttonTitle: String?,
buttonURL: URL?
) {
let contents = InAppMessagingContents(
title: title,
body: body,
imageURL: imageURL,
buttonTitle: buttonTitle,
buttonURL: buttonURL
)
let inAppMessagingModalViewController = InAppMessagingModalViewController(inAppMessagingContents: contents)
viewController.present(inAppMessagingModalViewController, animated: true)
}
}
InAppMessagingModalViewController.xib (Storyboardでも可)
- 背面全体に閉じる用のButton
- 右上の閉じるボタン (デザインは好きなものに)
- タイトル用Label
- ImageView
- Body用Label
- リンク遷移用のButton
InAppMessagingModalViewController.swift
import UIKit
import SafariServices
final class InAppMessagingModalViewController: UIViewController {
// MARK: - Properties
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var imageView: UIImageView!
@IBOutlet private weak var bodyLabel: UILabel!
@IBOutlet private weak var messagingButton: DesignableButton!
@IBOutlet private weak var rightTopCloseButton: UIButton!
@IBOutlet private weak var backgroundCloseButton: UIButton!
@IBOutlet private weak var inAppMessagingImageConstraint: NSLayoutConstraint!
private let inAppMessagingContents: InAppMessagingContents
// MARK: - Initialize
init(inAppMessagingContents: InAppMessagingContents) {
self.inAppMessagingContents = inAppMessagingContents
super.init(nibName: Self.className, bundle: Self.bundle)
self.modalPresentationStyle = .overFullScreen
self.modalTransitionStyle = .crossDissolve
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
titleLabel.text = inAppMessagingContents.title
bodyLabel.text = inAppMessagingContents.body
bodyLabel.isHidden = inAppMessagingContents.body == nil
messagingButton.setTitle(inAppMessagingContents.buttonTitle, for: .normal)
messagingButton.isHidden = inAppMessagingContents.buttonTitle == nil
if let url = inAppMessagingContents.imageURL {
setImage(from: url, to: imageView)
}
}
@IBAction func backgroundButton(_ sender: Any) {
dismiss(animated: true)
}
@IBAction func rightCloseButton(_ sender: Any) {
dismiss(animated: true)
}
@IBAction func detailButton(_ sender: Any) {
guard let url = inAppMessagingContents.imageURL else { return }
let safariViewController = SFSafariViewController(url: url)
present(safariViewController, animated: true)
}
func setImage(from url: URL, to imageView: UIImageView) {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print("Error downloading image: \(error)")
return
}
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
print("Invalid response from server")
return
}
guard let data = data, let image = UIImage(data: data) else {
print("Invalid image data")
return
}
DispatchQueue.main.async {
imageView.image = image
}
}
task.resume()
}
}
// MARK: - SFSafariViewControllerDelegate
extension InAppMessagingModalViewController: SFSafariViewControllerDelegate {
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
dismiss(animated: true)
}
}
AppDelegate
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
InAppMessaging.inAppMessaging().messageDisplayComponent = InAppMessagingCustomComponent()
Installations.installations().installationID { id, error in
if let error = error {
print("Error retrieving installation ID: \(error.localizedDescription)")
} else if let id = id {
print("Firebase Installation ID: \(id)")
}
}
return true
}
これで取得したIDを入れて配信すると表示されるようになります。