Skip to content

Commit

Permalink
Revamp the project
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexey Naumov committed Dec 8, 2024
1 parent 94193d9 commit f98a6e0
Show file tree
Hide file tree
Showing 118 changed files with 3,166 additions and 5,804 deletions.
898 changes: 173 additions & 725 deletions CountriesSwiftUI.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.3">
LastUpgradeVersion = "1610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
Expand All @@ -14,7 +15,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F64495E02360D66400C9BB1F"
BlueprintIdentifier = "488734162CDCA2DB00B400A3"
BuildableName = "CountriesSwiftUI.app"
BlueprintName = "CountriesSwiftUI"
ReferencedContainer = "container:CountriesSwiftUI.xcodeproj">
Expand All @@ -27,23 +28,14 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F64495E02360D66400C9BB1F"
BuildableName = "CountriesSwiftUI.app"
BlueprintName = "CountriesSwiftUI"
ReferencedContainer = "container:CountriesSwiftUI.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F67833D12369CCBD0065272F"
BlueprintIdentifier = "488734282CDCA2DD00B400A3"
BuildableName = "UnitTests.xctest"
BlueprintName = "UnitTests"
ReferencedContainer = "container:CountriesSwiftUI.xcodeproj">
Expand All @@ -65,7 +57,7 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F64495E02360D66400C9BB1F"
BlueprintIdentifier = "488734162CDCA2DB00B400A3"
BuildableName = "CountriesSwiftUI.app"
BlueprintName = "CountriesSwiftUI"
ReferencedContainer = "container:CountriesSwiftUI.xcodeproj">
Expand All @@ -82,7 +74,7 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F64495E02360D66400C9BB1F"
BlueprintIdentifier = "488734162CDCA2DB00B400A3"
BuildableName = "CountriesSwiftUI.app"
BlueprintName = "CountriesSwiftUI"
ReferencedContainer = "container:CountriesSwiftUI.xcodeproj">
Expand Down
50 changes: 50 additions & 0 deletions CountriesSwiftUI/Core/App.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// CountriesApp.swift
// CountriesSwiftUI
//
// Created by Alexey on 7/11/24.
// Copyright © 2024 Alexey Naumov. All rights reserved.
//

import SwiftUI
import EnvironmentOverrides

@main
struct MainApp: App {

@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

var body: some Scene {
WindowGroup {
appDelegate.rootView
}
}
}

extension AppEnvironment {
var rootView: some View {
VStack {
if isRunningTests {
Text("Running unit tests")
} else {
CountriesList()
.modifier(RootViewAppearance())
.modelContainer(modelContainer)
.attachEnvironmentOverrides(onChange: onChangeHandler)
.inject(diContainer)
if modelContainer.isStub {
Text("⚠️ There is an issue with local database")
.font(.caption2)
}
}
}
}

private var onChangeHandler: (EnvironmentValues.Diff) -> Void {
return { diff in
if !diff.isDisjoint(with: [.locale, .sizeCategory]) {
self.diContainer.appState[\.routing] = AppState.ViewRouting()
}
}
}
}
73 changes: 73 additions & 0 deletions CountriesSwiftUI/Core/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// AppDelegate.swift
// CountriesSwiftUI
//
// Created by Alexey Naumov on 23.10.2019.
// Copyright © 2019 Alexey Naumov. All rights reserved.
//

import UIKit
import SwiftUI
import Combine
import Foundation

@MainActor
final class AppDelegate: UIResponder, UIApplicationDelegate {

private lazy var environment = AppEnvironment.bootstrap()
private var systemEventsHandler: SystemEventsHandler { environment.systemEventsHandler }

var rootView: some View {
environment.rootView
}

func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let config: UISceneConfiguration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
config.delegateClass = SceneDelegate.self
SceneDelegate.register(systemEventsHandler)
return config
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
systemEventsHandler.handlePushRegistration(result: .success(deviceToken))
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
systemEventsHandler.handlePushRegistration(result: .failure(error))
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) async -> UIBackgroundFetchResult {
return await systemEventsHandler
.appDidReceiveRemoteNotification(payload: userInfo)
}
}

// MARK: - SceneDelegate

@MainActor
final class SceneDelegate: UIResponder, UIWindowSceneDelegate, ObservableObject {

private static var systemEventsHandler: SystemEventsHandler?
private var systemEventsHandler: SystemEventsHandler? { Self.systemEventsHandler }

static func register(_ systemEventsHandler: SystemEventsHandler?) {
Self.systemEventsHandler = systemEventsHandler
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
systemEventsHandler?.sceneOpenURLContexts(URLContexts)
}

func sceneDidBecomeActive(_ scene: UIScene) {
systemEventsHandler?.sceneDidBecomeActive()
}

func sceneWillResignActive(_ scene: UIScene) {
systemEventsHandler?.sceneWillResignActive()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,11 @@ import SwiftUI
import Combine

struct AppState: Equatable {
var userData = UserData()
var routing = ViewRouting()
var system = System()
var permissions = Permissions()
}

extension AppState {
struct UserData: Equatable {
/*
The list of countries (Loadable<[Country]>) used to be stored here.
It was removed for performing countries' search by name inside a database,
which made the resulting variable used locally by just one screen (CountriesList)
Otherwise, the list of countries could have remained here, available for the entire app.
*/
}
}

extension AppState {
struct ViewRouting: Equatable {
var countriesList = CountriesList.Routing()
Expand All @@ -45,7 +33,7 @@ extension AppState {
struct Permissions: Equatable {
var push: Permission.Status = .unknown
}

static func permissionKeyPath(for permission: Permission) -> WritableKeyPath<AppState, Permission.Status> {
let pathToPermissions = \AppState.permissions
switch permission {
Expand All @@ -56,18 +44,7 @@ extension AppState {
}

func == (lhs: AppState, rhs: AppState) -> Bool {
return lhs.userData == rhs.userData &&
lhs.routing == rhs.routing &&
lhs.system == rhs.system &&
lhs.permissions == rhs.permissions
}

#if DEBUG
extension AppState {
static var preview: AppState {
var state = AppState()
state.system.isActive = true
return state
}
return lhs.routing == rhs.routing
&& lhs.system == rhs.system
&& lhs.permissions == rhs.permissions
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import Foundation

enum DeepLink: Equatable {

case showCountryFlag(alpha3Code: Country.Code)
case showCountryFlag(alpha3Code: String)

init?(url: URL) {
guard
let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
Expand All @@ -20,7 +20,7 @@ enum DeepLink: Equatable {
else { return nil }
if let item = query.first(where: { $0.name == "alpha3code" }),
let alpha3Code = item.value {
self = .showCountryFlag(alpha3Code: Country.Code(alpha3Code))
self = .showCountryFlag(alpha3Code: alpha3Code)
return
}
return nil
Expand All @@ -29,6 +29,7 @@ enum DeepLink: Equatable {

// MARK: - DeepLinksHandler

@MainActor
protocol DeepLinksHandler {
func open(deepLink: DeepLink)
}
Expand All @@ -46,7 +47,7 @@ struct RealDeepLinksHandler: DeepLinksHandler {
case let .showCountryFlag(alpha3Code):
let routeToDestination = {
self.container.appState.bulkUpdate {
$0.routing.countriesList.countryDetails = alpha3Code
$0.routing.countriesList.countryCode = alpha3Code
$0.routing.countryDetails.detailsSheet = true
}
}
Expand All @@ -58,7 +59,8 @@ struct RealDeepLinksHandler: DeepLinksHandler {
let defaultRouting = AppState.ViewRouting()
if container.appState.value.routing != defaultRouting {
self.container.appState[\.routing] = defaultRouting
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5, execute: routeToDestination)
let delay: DispatchTime = .now() + (ProcessInfo.processInfo.isRunningTests ? 0 : 1.5)
DispatchQueue.main.asyncAfter(deadline: delay, execute: routeToDestination)
} else {
routeToDestination()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ extension RealPushNotificationsHandler: UNUserNotificationCenterDelegate {
}

func handleNotification(userInfo: [AnyHashable: Any], completionHandler: @escaping () -> Void) {
guard let payload = userInfo["aps"] as? NotificationPayload,
let countryCode = payload["country"] as? Country.Code else {
guard let payload = userInfo["aps"] as? [AnyHashable: Any],
let countryCode = payload["country"] as? String else {
completionHandler()
return
}
deepLinksHandler.open(deepLink: .showCountryFlag(alpha3Code: countryCode))
completionHandler()
Task { @MainActor in
deepLinksHandler.open(deepLink: .showCountryFlag(alpha3Code: countryCode))
completionHandler()
}
}
}
Loading

0 comments on commit f98a6e0

Please sign in to comment.