diff --git a/RubyEvents.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RubyEvents.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9f1703e..bf4de75 100644 --- a/RubyEvents.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RubyEvents.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/hotwired/hotwire-native-ios", "state" : { - "revision" : "56196ac91a63a619ef13e8d2c135b6346b541192", - "version" : "1.1.3" + "revision" : "f18b87938f55cbe8976e96702b5512fa9afcd674", + "version" : "1.2.0" } }, { diff --git a/RubyEvents/App.swift b/RubyEvents/App.swift index 0731079..e0a1b68 100644 --- a/RubyEvents/App.swift +++ b/RubyEvents/App.swift @@ -11,29 +11,15 @@ import UIKit class App { static var instance = App() - + var isTabbed: Bool = true - var sceneDelegate: SceneDelegate? - - lazy var navigator = Navigator(delegate: self) - lazy var tabBarController = TabBarController(app: self) - - var navigators: [Navigator] { - if isTabbed { - return tabBarController.navigators - } - return [navigator] - } - - var viewControllers: [UIViewController] { - navigators.map(\.rootViewController) - } - + lazy var tabBarController = HotwireTabBarController(navigatorDelegate: self) + var window: UIWindow? { sceneDelegate?.window } - + var isDebug: Bool { #if DEBUG return true @@ -41,61 +27,52 @@ class App { return false #endif } - + var isTestFlight: Bool { Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" } - + var environment: Environment { if isDebug { return .development } - + if isTestFlight { return .staging } - + return .production } - + func start(sceneDelegate: SceneDelegate) { self.sceneDelegate = sceneDelegate - self.tabBarController.setupTabs() - - switchToTabController() + window?.rootViewController = tabBarController + Appearance.configure() + tabBarController.load(HotwireTab.all) } - - func switchToNavigationController() { - sceneDelegate?.window?.rootViewController = navigator.rootViewController - self.isTabbed = false + + func hideNavigationBar() { + tabBarController.activeNavigator.rootViewController.navigationBar.isHidden = true } - - func switchToTabController() { - sceneDelegate?.window?.rootViewController = tabBarController - self.isTabbed = true - } - - func navigatorFor(title: String) -> Navigator? { - tabBarController.navigatorFor(title: title) + + func showNavigationBar() { + tabBarController.activeNavigator.rootViewController.navigationBar.isHidden = false } } extension App: NavigatorDelegate { - func handle(proposal: VisitProposal) -> ProposalResult { + func handle(proposal: VisitProposal, from navigator: Navigator) -> ProposalResult { switch proposal.viewController { case "home": let viewController = UIHostingController( rootView: HomeView( - navigator: App.instance.navigatorFor(title: "Home") + navigator: tabBarController.activeNavigator ) ) - - App.instance.tabBarController.hideNavigationBarFor(title: "Home") - + hideNavigationBar() return .acceptCustom(viewController) default: - App.instance.tabBarController.showNavigationBarFor(title: "Home") - + showNavigationBar() return .accept } } diff --git a/RubyEvents/AppDelegate.swift b/RubyEvents/AppDelegate.swift index da3323d..5f746c6 100644 --- a/RubyEvents/AppDelegate.swift +++ b/RubyEvents/AppDelegate.swift @@ -16,7 +16,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let versionNumber = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String let uniqueDeviceId = UIDevice.current.identifierForVendor?.uuidString ?? "" - Hotwire.config.applicationUserAgentPrefix = "Hotwire Native iOS; app_version: \(versionNumber); unique_device_id: \(uniqueDeviceId);" + Hotwire.config.applicationUserAgentPrefix = "app_version: \(versionNumber); unique_device_id: \(uniqueDeviceId);" Hotwire.registerBridgeComponents([ ButtonComponent.self diff --git a/RubyEvents/Appearance.swift b/RubyEvents/Appearance.swift new file mode 100644 index 0000000..4aac730 --- /dev/null +++ b/RubyEvents/Appearance.swift @@ -0,0 +1,31 @@ +import UIKit + +struct Appearance { + static func configure() { + configureNavigationBar() + configureTabBar() + } + + private static func configureNavigationBar() { + let navigationBarAppearance = UINavigationBarAppearance() + navigationBarAppearance.configureWithDefaultBackground() + UINavigationBar.appearance().standardAppearance = navigationBarAppearance + UINavigationBar.appearance().scrollEdgeAppearance = navigationBarAppearance + UINavigationBar.appearance().isOpaque = false + UINavigationBar.appearance().isTranslucent = true + navigationBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.black] + + } + + private static func configureTabBar() { + let tabBarAppearance = UITabBarAppearance() + tabBarAppearance.configureWithDefaultBackground() + tabBarAppearance.backgroundColor = UIColor.white + UITabBar.appearance().standardAppearance = tabBarAppearance + UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance + UITabBar.appearance().isOpaque = true + UITabBar.appearance().isTranslucent = false + UITabBar.appearance().tintColor = .red + UITabBar.appearance().unselectedItemTintColor = .black + } +} diff --git a/RubyEvents/bridge/ButtonComponent.swift b/RubyEvents/bridge/ButtonComponent.swift index 5350e56..be6dbce 100644 --- a/RubyEvents/bridge/ButtonComponent.swift +++ b/RubyEvents/bridge/ButtonComponent.swift @@ -11,7 +11,7 @@ final class ButtonComponent: BridgeComponent { } private var viewController: UIViewController? { - delegate.destination as? UIViewController + delegate?.destination as? UIViewController } private func addButton(via message: Message, to viewController: UIViewController) { diff --git a/RubyEvents/bridge/LargeTitleComponent.swift b/RubyEvents/bridge/LargeTitleComponent.swift index a6aa66d..db2d91c 100644 --- a/RubyEvents/bridge/LargeTitleComponent.swift +++ b/RubyEvents/bridge/LargeTitleComponent.swift @@ -18,7 +18,7 @@ final class LargeTitleComponent: BridgeComponent { } private var viewController: UIViewController? { - delegate.destination as? UIViewController + delegate?.destination as? UIViewController } private func addTitle(via message: Message, to viewController: UIViewController) { diff --git a/RubyEvents/controllers/TabBarController.swift b/RubyEvents/controllers/TabBarController.swift deleted file mode 100644 index 6b76bc3..0000000 --- a/RubyEvents/controllers/TabBarController.swift +++ /dev/null @@ -1,196 +0,0 @@ -// -// TabBarController.swift -// RubyEvents -// -// Created by Marco Roth on 11.01.2025. -// - -import HotwireNative -import UIKit - -struct TabBarConfigurationItem { - let title: String - let icon: String - let url: URL - let position: Int -} - -struct TabBarConfiguration { - let items: [TabBarConfigurationItem] -} - -class TabBarController: UITabBarController { - var app: App - var configuration: TabBarConfiguration? - var navigators: [Navigator] = [] - var isSetup: Bool = false - - init(app: App) { - self.app = app - super.init(nibName: nil, bundle: nil) - } - - func fixedTabBarItemFrom(item: TabBarConfigurationItem) -> FixedTabBarItem { - FixedTabBarItem( - title: item.title, - image: UIImage(systemName: item.icon), - tag: item.position - ) - } - - func reloadAllTabs() { - self.navigators.forEach { navigator in - reloadTab(navigator: navigator) - } - } - - func reloadTab(title: String) { - if let navigator = app.navigatorFor(title: title) { - reloadTab(navigator: navigator) - } - } - - func reloadTab(navigator: Navigator) { - navigator.rootViewController.popToRootViewController(animated: false) - navigator.activeWebView.reload() - } - - func navigatorFor(title: String) -> Navigator? { - let index = configuration?.items.firstIndex { $0.self.title == title } - - guard let index else { return nil } - - return navigators[index] - } - - var currentNavigator: Navigator? { - navigatorFor(title: currentTabTitle ?? "") - } - - var currentTabTitle: String? { - self.tabBar.selectedItem?.title - } - - func hideNavigationBarFor(title: String) { - let navigator = self.navigatorFor(title: title) - navigator?.rootViewController.navigationBar.isHidden = true - } - - func showNavigationBarFor(title: String) { - let navigator = self.navigatorFor(title: title) - navigator?.rootViewController.navigationBar.isHidden = false - } - - func hideTabBar() { - self.tabBar.isHidden = true - } - - func showTabBar() { - self.tabBar.isHidden = false - } - - func setupTabs() { - setup( - configuration: TabBarConfiguration( - items: [ - TabBarConfigurationItem( - title: "Home", - icon: "house", - url: Router.instance.home_url(), - position: 0 - ), - TabBarConfigurationItem( - title: "Events", - icon: "calendar", - url: Router.instance.events_url(), - position: 1 - ), - TabBarConfigurationItem( - title: "Talks", - icon: "music.mic", - url: Router.instance.talks_url(), - position: 2 - ), - TabBarConfigurationItem( - title: "Speakers", - icon: "person.fill", - url: Router.instance.speakers_url(), - position: 3 - ) - ] - ) - ) - } - - func setup(configuration: TabBarConfiguration) { - guard !isSetup else { return } - - self.configuration = configuration - - var tabBarItems: [FixedTabBarItem] = [] - - configuration.items.forEach { item in - let navigator = Navigator(delegate: app) - self.navigators.append(navigator) - - navigator.route(item.url) - - tabBarItems.append(fixedTabBarItemFrom(item: item)) - } - - viewControllers = navigators.map(\.rootViewController) - - tabBarItems.enumerated().forEach { index, item in - if let viewController = self.viewControllers?[index] { -// viewController.tabBarController?.tabBar.tintColor = .white -// viewController.tabBarController?.tabBar.barTintColor = .black - viewController.tabBarItem = item - } - } - - // NavBar styling - let navigationBarAppearance = UINavigationBarAppearance() - navigationBarAppearance.configureWithDefaultBackground() - UINavigationBar.appearance().standardAppearance = navigationBarAppearance - UINavigationBar.appearance().scrollEdgeAppearance = navigationBarAppearance - UINavigationBar.appearance().isOpaque = false - UINavigationBar.appearance().isTranslucent = true - navigationBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.black] - - // TabBar styling - let tabBarAppearance = UITabBarAppearance() - tabBarAppearance.configureWithDefaultBackground() - tabBarAppearance.backgroundColor = UIColor.white - UITabBar.appearance().standardAppearance = tabBarAppearance - UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance - UITabBar.appearance().isOpaque = true - UITabBar.appearance().isTranslucent = false - UITabBar.appearance().tintColor = .red - UITabBar.appearance().unselectedItemTintColor = .black - - self.isSetup = true - } - - func reset() { - self.isSetup = false - self.configuration = nil - self.navigators = [] - self.viewControllers = nil - self.selectedIndex = 0 - } - - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - -// self.tabBar.backgroundColor = .white -// self.tabBar.barStyle = .default -// self.tabBar.isTranslucent = false -// self.tabBar.isOpaque = false -// self.tabBar.barTintColor = .white -// self.tabBar.tintColor = .gray - } -} diff --git a/RubyEvents/lib/Tabs.swift b/RubyEvents/lib/Tabs.swift new file mode 100644 index 0000000..7905bc4 --- /dev/null +++ b/RubyEvents/lib/Tabs.swift @@ -0,0 +1,39 @@ +import Foundation +import HotwireNative + +extension HotwireTab { + static let all: [HotwireTab] = { + let tabs: [HotwireTab] = [ + .home, + .events, + .talks, + .speakers + ] + + return tabs + }() + + static let home = HotwireTab( + title: "Home", + image: .init(systemName: "house")!, + url: Router.instance.home_url() + ) + + static let events = HotwireTab( + title: "Events", + image: .init(systemName: "calendar")!, + url: Router.instance.events_url() + ) + + static let talks = HotwireTab( + title: "Talks", + image: .init(systemName: "music.mic")!, + url: Router.instance.talks_url() + ) + + static let speakers = HotwireTab( + title: "Speakers", + image: .init(systemName: "person.fill")!, + url: Router.instance.speakers_url() + ) +} diff --git a/RubyEvents/views/HomeView.swift b/RubyEvents/views/HomeView.swift index 3bf2c96..52fd8b1 100644 --- a/RubyEvents/views/HomeView.swift +++ b/RubyEvents/views/HomeView.swift @@ -81,7 +81,7 @@ struct HomeView: View { } } .onAppear { - App.instance.tabBarController.hideNavigationBarFor(title: "Home") + App.instance.hideNavigationBar() if !hasLoadedInitialData { fetchData() hasLoadedInitialData = true diff --git a/RubyEvents/views/HomeViewSkeleton.swift b/RubyEvents/views/HomeViewSkeleton.swift index bda39da..1631e6f 100644 --- a/RubyEvents/views/HomeViewSkeleton.swift +++ b/RubyEvents/views/HomeViewSkeleton.swift @@ -49,7 +49,7 @@ struct HomeViewSkeleton: View { .edgesIgnoringSafeArea(.top) } .onAppear { - App.instance.tabBarController.hideNavigationBarFor(title: "Home") + App.instance.hideNavigationBar() isAnimating = true } }