EchoLog アプリの構成を わかりやすく解説してみる
この記事では、SwiftUI を使って構築した EchoLog アプリのエントリーポイント周りのコードについて、文章とコードを交えて解説します。
🎯 アプリ全体の概要
このアプリは App プロトコルを採用しており、以下の状態オブジェクトをアプリ全体で扱います。
-
AuthViewModel— 認証状態を管理 -
SyncManager— データ同期の管理 -
NetworkMonitor— ネットワーク状態監視 -
AppState— 画面遷移などアプリ全体の状態管理
まずはアプリ入口となる EchoLogApp から見ていきます。
🛠 EchoLogApp — アプリのエントリーポイント
import SwiftUI
import UserNotifications
@main
struct EchoLogApp: App {
@StateObject private var authViewModel = AuthViewModel()
@StateObject private var syncManager = SyncManager.shared
@StateObject private var networkMonitor = NetworkMonitor.shared
@StateObject private var appState = AppState.shared
init() {
setupAppearance()
requestNotificationPermission()
}
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(authViewModel)
.environmentObject(syncManager)
.environmentObject(networkMonitor)
.environmentObject(appState)
.preferredColorScheme(.dark) // ダークモードオンリー
}
}
}
🎨 NavigationBar / TabBar の外観設定
private func setupAppearance() {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = UIColor(Color(hex: "0A0E14"))
appearance.titleTextAttributes = [.foregroundColor: UIColor(Color(hex: "E2E8F0"))]
appearance.largeTitleTextAttributes = [.foregroundColor: UIColor(Color(hex: "E2E8F0"))]
UINavigationBar.appearance().standardAppearance = appearance
UINavigationBar.appearance().scrollEdgeAppearance = appearance
UINavigationBar.appearance().compactAppearance = appearance
UINavigationBar.appearance().tintColor = UIColor(Color(hex: "00D9FF"))
let tabBarAppearance = UITabBarAppearance()
tabBarAppearance.configureWithOpaqueBackground()
tabBarAppearance.backgroundColor = UIColor(Color(hex: "0A0E14"))
UITabBar.appearance().standardAppearance = tabBarAppearance
UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
UITabBar.appearance().tintColor = UIColor(Color(hex: "00D9FF"))
UITabBar.appearance().unselectedItemTintColor = UIColor(Color(hex: "6B7280"))
}
🔔 通知許可
private func requestNotificationPermission() {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if granted {
print("✅ Notification permission granted")
} else if let error = error {
print("❌ Notification permission error: \(error)")
} else {
print("⚠️ Notification permission denied")
}
}
}
📱 ContentView — 認証状態に応じた画面切り替え
struct ContentView: View {
@EnvironmentObject var authViewModel: AuthViewModel
var body: some View {
Group {
if authViewModel.isAuthenticated {
MainTabView()
} else {
LoginView()
}
}
.animation(.easeInOut, value: authViewModel.isAuthenticated)
}
}
🗂 MainTabView — タブ UI
struct MainTabView: View {
@EnvironmentObject var appState: AppState
@State private var selectedTab = 0
var body: some View {
TabView(selection: $selectedTab) {
HomeView()
.tabItem {
Label("ホーム", systemImage: "house.fill")
}
.tag(0)
ChatLogView()
.tabItem {
Label("ログ", systemImage: "bubble.left.and.bubble.right.fill")
}
.tag(1)
EcomokoView()
.tabItem {
Label("エコモコ", systemImage: "heart.fill")
}
.tag(2)
}
.accentColor(Color.theme.primary)
.onChange(of: appState.shouldNavigateToHome) { shouldNavigate in
if shouldNavigate {
selectedTab = 0
appState.shouldNavigateToHome = false
}
}
}
}
🧪 Preview
#Preview {
ContentView()
.environmentObject(AuthViewModel())
.environmentObject(SyncManager.shared)
.environmentObject(NetworkMonitor.shared)
.environmentObject(AppState.shared)
}