User-XkQI1 month ago
import Foundation
import Flutter
import SwiftUI
import Octopus
import OctopusUI
import Combine
// MARK: - SwiftUI wrapper with callback
struct OctopusHomeScreenWithCallback: View {
let octopus: OctopusSDK
let navBarLeadingItem: OctopusHomeScreen.NavBarLeadingItemKind
let navBarPrimaryColor: Bool
let onNavigateToLogin: () -> Void
var body: some View {
OctopusHomeScreen(
octopus: octopus,
navBarLeadingItem: navBarLeadingItem,
navBarPrimaryColor: navBarPrimaryColor
)
.onReceive(NotificationCenter.default.publisher(
for: NSNotification.Name("OctopusNavigateToLogin"))
) { _ in
onNavigateToLogin()
}
}
}
// MARK: - Flutter View Factory
final class OctopusViewFactory: NSObject, FlutterPlatformViewFactory {
private let messenger: FlutterBinaryMessenger
init(messenger: FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
FlutterStandardMessageCodec.sharedInstance()
}
func create(
withFrame frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?
) -> FlutterPlatformView {
OctopusPlatformView(frame: frame, args: args)
}
}
// MARK: - Platform View Wrapper
final class OctopusPlatformView: NSObject, FlutterPlatformView {
private let container: SafeHostingContainerView
init(frame: CGRect, args: Any?) {
self.container = SafeHostingContainerView(args: args)
self.container.frame = frame
super.init()
}
func view() -> UIView { container }
}
// MARK: - Hosting Container (fixes iOS 18.0 crash)
private final class SafeHostingContainerView: UIView {
private var hostingController: UIHostingController<AnyView>?
private let args: Any?
init(args: Any?) {
self.args = args
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMoveToWindow() {
super.didMoveToWindow()
guard window != nil else { return }
// Avoid double initialization
guard hostingController == nil else { return }
// Delay one runloop to guarantee valid UIWindowScene (prevents iOS 18.0 bug)
// DispatchQueue.main.async { [weak self] in
self.initializeSwiftUIView()
// }
}
private func initializeSwiftUIView() {
guard let octopus = OctopusSdkFlutterPlugin.sharedOctopus else {
addErrorLabel()
return
}
// --- Parse arguments ---
let dict = args as? [String: Any]
let main = (dict?["primaryMain"] as? NSNumber).map { OctopusSdkFlutterPlugin.uiColorFromARGBInt($0.intValue) }
let low = (dict?["primaryLowContrast"] as? NSNumber).map { OctopusSdkFlutterPlugin.uiColorFromARGBInt($0.intValue) }
let high = (dict?["primaryHighContrast"] as? NSNumber).map { OctopusSdkFlutterPlugin.uiColorFromARGBInt($0.intValue) }
let onPrimary = (dict?["onPrimary"] as? NSNumber).map { OctopusSdkFlutterPlugin.uiColorFromARGBInt($0.intValue) }
let logoBase64 = dict?["logoBase64"] as? String
let navBarTitle = dict?["navBarTitle"] as? String
let navBarPrimaryColor = (dict?["navBarPrimaryColor"] as? Bool) ?? false
let themeMode = dict?["themeMode"] as? String
let fontSizeTitle1 = (dict?["fontSizeTitle1"] as? NSNumber)?.intValue ?? 26
let fontSizeTitle2 = (dict?["fontSizeTitle2"] as? NSNumber)?.intValue ?? 20
let fontSizeBody1 = (dict?["fontSizeBody1"] as? NSNumber)?.intValue ?? 17
let fontSizeBody2 = (dict?["fontSizeBody2"] as? NSNumber)?.intValue ?? 14
let fontSizeCaption1 = (dict?["fontSizeCaption1"] as? NSNumber)?.intValue ?? 12
let fontSizeCaption2 = (dict?["fontSizeCaption2"] as? NSNumber)?.intValue ?? 10
var theme: OctopusTheme? = nil
if main != nil || low != nil || high != nil || onPrimary != nil || logoBase64 != nil ||
fontSizeTitle1 != 26 || fontSizeTitle2 != 20 || fontSizeBody1 != 17 ||
fontSizeBody2 != 14 || fontSizeCaption1 != 12 || fontSizeCaption2 != 10 ||
themeMode != nil {
theme = OctopusSdkFlutterPlugin.buildTheme(
main: main ?? .systemBlue,
low: low ?? UIColor.systemBlue.withAlphaComponent(0.2),
high: high ?? .white,
onPrimary: onPrimary ?? .white,
logoBase64: logoBase64,
fontSizeTitle1: fontSizeTitle1,
fontSizeTitle2: fontSizeTitle2,
fontSizeBody1: fontSizeBody1,
fontSizeBody2: fontSizeBody2,
fontSizeCaption1: fontSizeCaption1,
fontSizeCaption2: fontSizeCaption2,
themeMode: themeMode
)
}