initial version
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import UIKit
|
||||
import UserNotifications
|
||||
import FirebaseCore
|
||||
import FirebaseAnalytics
|
||||
import GoogleMobileAds
|
||||
|
||||
class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||||
) -> Bool {
|
||||
// Initialize Firebase (only if GoogleService-Info.plist exists)
|
||||
if Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist") != nil {
|
||||
FirebaseApp.configure()
|
||||
} else {
|
||||
print("Warning: GoogleService-Info.plist not found. Firebase disabled.")
|
||||
}
|
||||
|
||||
// Initialize Google Mobile Ads
|
||||
MobileAds.shared.start()
|
||||
|
||||
// Request notification permissions
|
||||
requestNotificationPermissions()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private func requestNotificationPermissions() {
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
|
||||
if let error = error {
|
||||
print("Notification permission error: \(error)")
|
||||
}
|
||||
print("Notification permission granted: \(granted)")
|
||||
}
|
||||
}
|
||||
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
|
||||
) {
|
||||
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
|
||||
print("Device token: \(token)")
|
||||
}
|
||||
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didFailToRegisterForRemoteNotificationsWithError error: Error
|
||||
) {
|
||||
print("Failed to register for remote notifications: \(error)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
@EnvironmentObject var iapService: IAPService
|
||||
@EnvironmentObject var adMobService: AdMobService
|
||||
@EnvironmentObject var tabSelection: TabSelectionStore
|
||||
@AppStorage("onboardingCompleted") private var onboardingCompleted = false
|
||||
@AppStorage("faceIdEnabled") private var faceIdEnabled = false
|
||||
@AppStorage("pinEnabled") private var pinEnabled = false
|
||||
@AppStorage("lockOnLaunch") private var lockOnLaunch = true
|
||||
@AppStorage("lockOnBackground") private var lockOnBackground = false
|
||||
@Environment(\.scenePhase) private var scenePhase
|
||||
@State private var isUnlocked = false
|
||||
|
||||
private var lockEnabled: Bool {
|
||||
faceIdEnabled || pinEnabled
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Group {
|
||||
if !onboardingCompleted {
|
||||
OnboardingView(onboardingCompleted: $onboardingCompleted)
|
||||
} else {
|
||||
mainContent
|
||||
}
|
||||
}
|
||||
|
||||
if onboardingCompleted && lockEnabled && !isUnlocked {
|
||||
AppLockView(isUnlocked: $isUnlocked)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if !lockEnabled {
|
||||
isUnlocked = true
|
||||
} else {
|
||||
isUnlocked = !lockOnLaunch
|
||||
}
|
||||
}
|
||||
.onChange(of: lockEnabled) { _, enabled in
|
||||
if !enabled {
|
||||
isUnlocked = true
|
||||
} else {
|
||||
isUnlocked = !lockOnLaunch
|
||||
}
|
||||
}
|
||||
.onChange(of: onboardingCompleted) { _, completed in
|
||||
if completed && lockEnabled {
|
||||
isUnlocked = !lockOnLaunch
|
||||
}
|
||||
}
|
||||
.onChange(of: scenePhase) { _, phase in
|
||||
guard onboardingCompleted, lockEnabled else { return }
|
||||
if phase == .background && lockOnBackground {
|
||||
isUnlocked = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var mainContent: some View {
|
||||
ZStack {
|
||||
TabView(selection: $tabSelection.selectedTab) {
|
||||
DashboardView()
|
||||
.tabItem {
|
||||
Label("Home", systemImage: "house.fill")
|
||||
}
|
||||
.tag(0)
|
||||
|
||||
SourceListView(iapService: iapService)
|
||||
.tabItem {
|
||||
Label("Sources", systemImage: "list.bullet")
|
||||
}
|
||||
.tag(1)
|
||||
|
||||
ChartsContainerView(iapService: iapService)
|
||||
.tabItem {
|
||||
Label("Charts", systemImage: "chart.xyaxis.line")
|
||||
}
|
||||
.tag(2)
|
||||
|
||||
JournalView()
|
||||
.tabItem {
|
||||
Label("Journal", systemImage: "book.closed")
|
||||
}
|
||||
.tag(3)
|
||||
|
||||
SettingsView()
|
||||
.tabItem {
|
||||
Label("Settings", systemImage: "gearshape.fill")
|
||||
}
|
||||
.tag(4)
|
||||
}
|
||||
}
|
||||
// Banner ad at bottom for free users
|
||||
.safeAreaInset(edge: .bottom, spacing: 0) {
|
||||
if !iapService.isPremium {
|
||||
BannerAdView()
|
||||
.frame(height: AppConstants.UI.bannerAdHeight)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color(.systemBackground))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
.environmentObject(IAPService())
|
||||
.environmentObject(AdMobService())
|
||||
.environmentObject(AccountStore(iapService: IAPService()))
|
||||
.environmentObject(TabSelectionStore())
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct PortfolioJournalApp: App {
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
@StateObject private var iapService: IAPService
|
||||
@StateObject private var adMobService: AdMobService
|
||||
@StateObject private var accountStore: AccountStore
|
||||
@StateObject private var tabSelection = TabSelectionStore()
|
||||
@Environment(\.scenePhase) private var scenePhase
|
||||
|
||||
let coreDataStack = CoreDataStack.shared
|
||||
|
||||
init() {
|
||||
let iap = IAPService()
|
||||
_iapService = StateObject(wrappedValue: iap)
|
||||
_adMobService = StateObject(wrappedValue: AdMobService())
|
||||
_accountStore = StateObject(wrappedValue: AccountStore(iapService: iap))
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
.environment(\.managedObjectContext, coreDataStack.viewContext)
|
||||
.environmentObject(iapService)
|
||||
.environmentObject(adMobService)
|
||||
.environmentObject(accountStore)
|
||||
.environmentObject(tabSelection)
|
||||
}
|
||||
.onChange(of: scenePhase) { _, newPhase in
|
||||
if newPhase == .active {
|
||||
coreDataStack.refreshWidgetData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user