mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-09 22:36:01 +05:45
* Revert to previous nil check on experiment JSON * Add Swift extensions podspec * Add viewmodifier for displaying custom in-app messages w/ SwiftUI * Add Multiplatform project to integration test SwiftUI pod * We don't support macOS yet, comment that out from the Podfile * Fix variable renaming bug * View modifier implementation that works, but design is questionable * Super basic integration * Split view modifiers based on in-app message subtype * Fix versioning in podspec * Configure Firebase in test app * Remove trailing whitespace * Clean up SDK fetching code, sample app * Updates to sample code, fully functional modal message now * Update implementing app example * Better code readability for view modifiers * Add unit tests for DelegateBridge * Add test target to pod spec * Add copyright notices * Remove whitespace from pod spec * Add auto-complete documentation for in-app message extensions * Lowered min target * Update Package.swift * First pass at CI updates * @available(13) for SwiftUI structs * Use singleton DelegateBridge * Update inappmessaging.yml * Run scripts/style.sh * Update build.sh * Modify build.sh * Update CHANGELOG, try something new with GHA configuration * Let's try this for GHA * Fully qualify FIAM message enums * Remove redundant type declaration * Let's try direct instantiation of messages in unit tests * Test CI build works without actual test cases * Let's try this, making init available. * Try exposing initializer * Expose simpler initializer for in-app message superclass * Fix up UI test app initializers * Update CHANGELOG, GHA workflows * Cleaner GHA test flow * Clean up custom in-app message view modifier implementation * Configuration cleanup * Add some tests to ensure FIAM SwiftUI API compiles * Add SpecsDev to Podfile of integration testing app. Make @State var private
192 lines
6.5 KiB
Swift
192 lines
6.5 KiB
Swift
// Copyright 2021 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import FirebaseInAppMessaging
|
|
import SwiftUI
|
|
|
|
// MARK: Image-only messages.
|
|
|
|
@available(iOS 13, tvOS 13, *)
|
|
struct ImageOnlyInAppMessageDisplayViewModifier<DisplayMessage: View>: ViewModifier {
|
|
var closure: (InAppMessagingImageOnlyDisplay, InAppMessagingDisplayDelegate) -> DisplayMessage
|
|
@ObservedObject var delegateBridge = DelegateBridge.shared
|
|
|
|
func body(content: Content) -> some View {
|
|
return content.overlay(overlayView())
|
|
}
|
|
|
|
@ViewBuilder
|
|
func overlayView() -> some View {
|
|
if let (message, delegate) = delegateBridge.inAppMessageData,
|
|
let imageOnlyMessage = message as? InAppMessagingImageOnlyDisplay {
|
|
closure(imageOnlyMessage, delegate)
|
|
.onAppear { delegate.impressionDetected?(for: imageOnlyMessage) }
|
|
} else {
|
|
EmptyView()
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(iOS 13, tvOS 13, *)
|
|
public extension View {
|
|
/// Overrides the default display of an image only in-app message as defined on the Firebase console.
|
|
func imageOnlyInAppMessage<Content: View>(closure: @escaping (InAppMessagingImageOnlyDisplay,
|
|
InAppMessagingDisplayDelegate)
|
|
-> Content)
|
|
-> some View {
|
|
modifier(ImageOnlyInAppMessageDisplayViewModifier(closure: closure))
|
|
}
|
|
}
|
|
|
|
// MARK: Banner messages.
|
|
|
|
@available(iOS 13, tvOS 13, *)
|
|
struct BannerInAppMessageDisplayViewModifier<DisplayMessage: View>: ViewModifier {
|
|
var closure: (InAppMessagingBannerDisplay, InAppMessagingDisplayDelegate) -> DisplayMessage
|
|
@ObservedObject var delegateBridge = DelegateBridge.shared
|
|
|
|
func body(content: Content) -> some View {
|
|
return content.overlay(overlayView())
|
|
}
|
|
|
|
@ViewBuilder
|
|
func overlayView() -> some View {
|
|
if let (message, delegate) = delegateBridge.inAppMessageData,
|
|
let bannerMessage = message as? InAppMessagingBannerDisplay {
|
|
closure(bannerMessage, delegate).onAppear { delegate.impressionDetected?(for: bannerMessage) }
|
|
} else {
|
|
EmptyView()
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(iOS 13, tvOS 13, *)
|
|
public extension View {
|
|
/// Overrides the default display of a banner in-app message as defined on the Firebase console.
|
|
func bannerInAppMessage<Content: View>(closure: @escaping (InAppMessagingBannerDisplay,
|
|
InAppMessagingDisplayDelegate)
|
|
-> Content)
|
|
-> some View {
|
|
modifier(BannerInAppMessageDisplayViewModifier(closure: closure))
|
|
}
|
|
}
|
|
|
|
// MARK: Modal messages.
|
|
|
|
@available(iOS 13, tvOS 13, *)
|
|
struct ModalInAppMessageDisplayViewModifier<DisplayMessage: View>: ViewModifier {
|
|
var closure: (InAppMessagingModalDisplay, InAppMessagingDisplayDelegate) -> DisplayMessage
|
|
@ObservedObject var delegateBridge = DelegateBridge.shared
|
|
|
|
func body(content: Content) -> some View {
|
|
return content.overlay(overlayView())
|
|
}
|
|
|
|
@ViewBuilder
|
|
func overlayView() -> some View {
|
|
if let (message, delegate) = delegateBridge.inAppMessageData,
|
|
let modalMessage = message as? InAppMessagingModalDisplay {
|
|
closure(modalMessage, delegate).onAppear { delegate.impressionDetected?(for: modalMessage) }
|
|
} else {
|
|
EmptyView()
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(iOS 13, tvOS 13, *)
|
|
public extension View {
|
|
/// Overrides the default display of a modal in-app message as defined on the Firebase console.
|
|
func modalInAppMessage<Content: View>(closure: @escaping (InAppMessagingModalDisplay,
|
|
InAppMessagingDisplayDelegate)
|
|
-> Content)
|
|
-> some View {
|
|
modifier(ModalInAppMessageDisplayViewModifier(closure: closure))
|
|
}
|
|
}
|
|
|
|
// MARK: Card messages.
|
|
|
|
@available(iOS 13, tvOS 13, *)
|
|
struct CardInAppMessageDisplayViewModifier<DisplayMessage: View>: ViewModifier {
|
|
var closure: (InAppMessagingCardDisplay, InAppMessagingDisplayDelegate) -> DisplayMessage
|
|
@ObservedObject var delegateBridge = DelegateBridge.shared
|
|
|
|
func body(content: Content) -> some View {
|
|
return content.overlay(overlayView())
|
|
}
|
|
|
|
@ViewBuilder
|
|
func overlayView() -> some View {
|
|
if let (message, delegate) = delegateBridge.inAppMessageData,
|
|
let cardMessage = message as? InAppMessagingCardDisplay {
|
|
closure(cardMessage, delegate).onAppear { delegate.impressionDetected?(for: cardMessage) }
|
|
} else {
|
|
EmptyView()
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(iOS 13, tvOS 13, *)
|
|
public extension View {
|
|
/// Overrides the default display of a card in-app message as defined on the Firebase console.
|
|
func cardInAppMessage<Content: View>(closure: @escaping (InAppMessagingCardDisplay,
|
|
InAppMessagingDisplayDelegate)
|
|
-> Content)
|
|
-> some View {
|
|
modifier(CardInAppMessageDisplayViewModifier(closure: closure))
|
|
}
|
|
}
|
|
|
|
// MARK: Bridge to Firebase In-App Messaging SDK.
|
|
|
|
/**
|
|
* A singleton that acts as the bridge between view modifiers for displaying custom in-app messages and the
|
|
* in-app message fetch/display/interaction flow.
|
|
*/
|
|
@available(iOS 13, tvOS 13, *)
|
|
class DelegateBridge: NSObject, InAppMessagingDisplay, InAppMessagingDisplayDelegate,
|
|
ObservableObject {
|
|
@Published var inAppMessageData: (InAppMessagingDisplayMessage,
|
|
InAppMessagingDisplayDelegate)? = nil
|
|
|
|
static let shared = DelegateBridge()
|
|
|
|
override init() {
|
|
super.init()
|
|
InAppMessaging.inAppMessaging().messageDisplayComponent = self
|
|
InAppMessaging.inAppMessaging().delegate = self
|
|
}
|
|
|
|
func displayMessage(_ messageForDisplay: InAppMessagingDisplayMessage,
|
|
displayDelegate: InAppMessagingDisplayDelegate) {
|
|
DispatchQueue.main.async {
|
|
self.inAppMessageData = (messageForDisplay, displayDelegate)
|
|
}
|
|
}
|
|
|
|
func messageClicked(_ inAppMessage: InAppMessagingDisplayMessage,
|
|
with action: InAppMessagingAction) {
|
|
DispatchQueue.main.async {
|
|
self.inAppMessageData = nil
|
|
}
|
|
}
|
|
|
|
func messageDismissed(_ inAppMessage: InAppMessagingDisplayMessage,
|
|
dismissType: FIRInAppMessagingDismissType) {
|
|
DispatchQueue.main.async {
|
|
self.inAppMessageData = nil
|
|
}
|
|
}
|
|
}
|