Skip to content

App Themes #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Packages/DomainModels/Sources/DomainModels/AppTheme.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// File.swift
//
//
// Created by Oscar Gonzalez on 09/03/23.
//

import Foundation

public enum AppTheme: Equatable {
case free
case pro
}
29 changes: 29 additions & 0 deletions Packages/Logic/Sources/Logic/AppThemeLogic.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// File.swift
//
//
// Created by Oscar Gonzalez on 09/03/23.
//

import Foundation
import DomainModels
import Combine
import Repositories

public class AppThemeLogic {

@Published public private(set) var currentTheme: AppTheme

private let appThemeRepository: AppThemeRepository

public init(appThemeRepository: AppThemeRepository) {
self.appThemeRepository = appThemeRepository

currentTheme = appThemeRepository.currentTheme
appThemeRepository.$currentTheme.dropFirst().assign(to: &$currentTheme)
}

public func changeAppTheme(to theme: AppTheme) {
return appThemeRepository.setTheme(theme)
}
}
2 changes: 2 additions & 0 deletions Packages/Logic/Sources/Logic/DI/Container+BusinessLogic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Repositories

public extension Container {
func injectBusinessLogicLogic() -> Container {
self.autoregister(AppThemeLogic.self, initializer: AppThemeLogic.init).inObjectScope(.transient)
self.autoregister(CountryListLogic.self, initializer: CountryListLogic.init).inObjectScope(.transient)
self.autoregister(CountryDetailsLogic.self, initializer: CountryDetailsLogic.init).inObjectScope(.transient)
self.autoregister(ServerStatusLogic.self, initializer: ServerStatusLogic.init).inObjectScope(.transient)
Expand All @@ -21,6 +22,7 @@ public extension Container {
}

func injectBusinessLogicRepositories() -> Container {
self.autoregister(AppThemeRepository.self, initializer: AppThemeRepository.init).inObjectScope(.container)
self.autoregister(CountryListRepository.self, initializer: CountryListRepository.init).inObjectScope(.container)
self.autoregister(CountryDetailsRepository.self, initializer: CountryDetailsRepository.init).inObjectScope(.container)
self.autoregister(ServerStatusPushBasedRepository.self, initializer: ServerStatusPushBasedRepository.init).inObjectScope(.container)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// File.swift
//
//
// Created by Oscar Gonzalez on 09/03/23.
//

import Foundation
import DomainModels

public class AppThemeRepository {
@Published public private(set) var currentTheme = AppTheme.free

public init() {}

public func setTheme(_ appTheme: AppTheme) {
currentTheme = appTheme
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import SwinjectAutoregistration

public extension Container {
func injectBusinessLogicRepositories() -> Container {
self.autoregister(AppThemeRepository.self, initializer: AppThemeRepository.init).inObjectScope(.container)
self.autoregister(CountryListRepository.self, initializer: CountryListRepository.init).inObjectScope(.container)
self.autoregister(CountryDetailsRepository.self, initializer: CountryDetailsRepository.init).inObjectScope(.container)
self.autoregister(ServerStatusPushBasedRepository.self, initializer: ServerStatusPushBasedRepository.init).inObjectScope(.container)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// SwiftUIView.swift
//
//
// Created by Oscar Gonzalez on 08/03/23.
//

import SwiftUI

struct ColorPalette {

struct malachite {
static let brightness500 = Color(hex: 0x06D16F)
static let brightness700 = Color(hex: 0x00A756)
}

struct bearHoney {
static let brightness500 = Color(hex: 0xE9A142)
static let brightness700 = Color(hex: 0xDA7C00)
}

struct orange {
static let brightness500 = Color(hex: 0xEC7C3C)
static let brightness700 = Color(hex: 0xBB4C0C)
}

static let freeAccent = Color(hex: 0x007AFF)
static let proAccent = Color(hex: 0xFA0002)
static let latexPurple = Color(hex: 0x7708E7)
static let darkGray = Color(hex: 0x1E1E1E)
static let lightGray = Color(hex: 0xD2D2D2)
static let midgray = Color(hex: 0x4D4D4D)
static let white = Color.white
static let black = Color.black
static let green = Color(hex: 0x00FF00)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// File.swift
//
//
// Created by Oscar Gonzalez on 09/03/23.
//

import Foundation
import DomainModels
import ViewModels
import SwiftUI
import Utils

@propertyWrapper
public struct InjectTheme: DynamicProperty {

@InjectStateObject private var themeViewModel: AppThemeViewModel

public init() {}

public var wrappedValue: Theme {
get { return ThemeFactory.build(theme: themeViewModel.currentTheme) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// File.swift
//
//
// Created by Oscar Gonzalez on 08/03/23.
//

import Foundation
import SwiftUI

public struct FreeTheme: Theme {
public struct Color: ThemeColor {
public let accent = ColorPalette.freeAccent
public let content = ColorPalette.black
public let scheme: ColorScheme = .light
}

public let color: ThemeColor = FreeTheme.Color()

public init() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// File.swift
//
//
// Created by Oscar Gonzalez on 08/03/23.
//

import Foundation
import SwiftUI

public struct ProTheme: Theme {
public struct Color: ThemeColor {
public let accent = ColorPalette.proAccent
public let content = ColorPalette.white
public let scheme: ColorScheme = .dark
}

public let color: ThemeColor = ProTheme.Color()

public init() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// File.swift
//
//
// Created by Oscar Gonzalez on 08/03/23.
//

import Foundation
import SwiftUI
import DomainModels

public protocol Theme {
var color: ThemeColor { get }
}

public protocol ThemeColor {
var accent: Color { get }
var content: Color { get }
var scheme: ColorScheme { get }
}

public struct ThemeFactory {
public static func build(theme: AppTheme) -> Theme {
switch theme {
case .free:
return FreeTheme()
case .pro:
return ProTheme()
}
}
}
21 changes: 21 additions & 0 deletions Packages/UIComponents/Sources/UIComponents/Utils/Color+Hex.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// File.swift
//
//
// Created by Oscar Gonzalez on 08/03/23.
//

import Foundation
import SwiftUI

extension Color {
init(hex: UInt, alpha: Double = 1) {
self.init(
.sRGB,
red: Double((hex >> 16) & 0xff) / 255,
green: Double((hex >> 08) & 0xff) / 255,
blue: Double((hex >> 00) & 0xff) / 255,
opacity: alpha
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// File.swift
//
//
// Created by Oscar Gonzalez on 09/03/23.
//

import Foundation
import SwiftUI

private struct ThemeKey: EnvironmentKey {
static var defaultValue: Theme = FreeTheme()
}

public extension EnvironmentValues {
var theme: Theme {
get { self[ThemeKey.self] }
set { self[ThemeKey.self] = newValue }
}
}
33 changes: 33 additions & 0 deletions Packages/ViewModels/Sources/ViewModels/AppThemeViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// File.swift
//
//
// Created by Oscar Gonzalez on 09/03/23.
//

import Foundation
import DomainModels
import Combine
import Logic

public class AppThemeViewModel: ObservableObject {
@Published public private(set) var currentTheme: AppTheme

private var appThemeLogic: AppThemeLogic

init(appThemeLogic: AppThemeLogic) {
self.appThemeLogic = appThemeLogic

currentTheme = appThemeLogic.currentTheme
appThemeLogic.$currentTheme.dropFirst().assign(to: &$currentTheme)
}

public func toggleTheme() {
switch currentTheme {
case .free:
appThemeLogic.changeAppTheme(to: .pro)
case .pro:
appThemeLogic.changeAppTheme(to: .free)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Logic
public extension Container {
func injectBusinessLogicViewModels() -> Container {
self.autoregister(AboutViewModel.self, initializer: AboutViewModel.init).inObjectScope(.transient)
self.autoregister(AppThemeViewModel.self, initializer: AppThemeViewModel.init).inObjectScope(.transient)
self.autoregister(CountryListViewModel.self, initializer: CountryListViewModel.init).inObjectScope(.transient)
self.register(CountryDetailsViewModel.self) { resolver, country in
CountryDetailsViewModel(country: country,
Expand Down
8 changes: 8 additions & 0 deletions Shared/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@
import SwiftUI
import UIComponents
import Utils
import ViewModels

struct ContentView: View {

@InjectTheme private var theme: Theme

var body: some View {
TravelAdvisoriesNavHost()
.accentColor(theme.color.accent)
.foregroundColor(theme.color.content)
.preferredColorScheme(theme.color.scheme)
.environment(\.theme, theme)
}
}

Expand Down
11 changes: 11 additions & 0 deletions Shared/TravelAdvisoriesNavHost.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import ViewModels
import Swinject

public struct TravelAdvisoriesNavHost: View {
@Environment(\.theme) var theme
@InjectStateObject private var themeViewModel: AppThemeViewModel

private let resolver: Swinject.Resolver

@State var destination: Destinations?
Expand All @@ -41,6 +44,14 @@ public struct TravelAdvisoriesNavHost: View {

self.buildBaseView()
}
.toolbar {
Button {
themeViewModel.toggleTheme()
} label: {
Text("Change theme")
.foregroundColor(theme.color.accent)
}
}
}
}

Expand Down