Skip to content
Permalink
Browse files

Connects ReSwift With SwiftUI

- Add the store to the SwiftUI
- Change Identifiable ID to String
  • Loading branch information
thiagoricieri committed Jan 8, 2020
1 parent c8c6732 commit ffffadee005e415cfa6eedf639b32a21f0134d8d
@@ -12,6 +12,7 @@
B005F58123BE152400CE9BE8 /* EntryFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B005F58023BE152400CE9BE8 /* EntryFormView.swift */; };
B01A65C423BBF69200DC7A0E /* expenses.json in Resources */ = {isa = PBXBuildFile; fileRef = B01A65C323BBF69200DC7A0E /* expenses.json */; };
B01A65C623BBF80C00DC7A0E /* loadMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01A65C523BBF80C00DC7A0E /* loadMock.swift */; };
B03F1D4E23C615AB00D031A9 /* dataActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03F1D4D23C615AB00D031A9 /* dataActions.swift */; };
B045F5BE23C4C2D900C220FC /* String+Money.swift in Sources */ = {isa = PBXBuildFile; fileRef = B045F5BD23C4C2D900C220FC /* String+Money.swift */; };
B06614C723BD3AA0006D3AAD /* Date+Formatted.swift in Sources */ = {isa = PBXBuildFile; fileRef = B06614C623BD3AA0006D3AAD /* Date+Formatted.swift */; };
B06614C923BD3F7F006D3AAD /* OneNumberStatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B06614C823BD3F7F006D3AAD /* OneNumberStatView.swift */; };
@@ -41,6 +42,7 @@
B0CBF69723AEA09B004ECC91 /* dataReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0CBF69623AEA09B004ECC91 /* dataReducer.swift */; };
B0CBF69923AEA0A1004ECC91 /* uiReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0CBF69823AEA0A1004ECC91 /* uiReducer.swift */; };
B0D600DB23C5F44E00CEAC46 /* ExpenseListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D600DA23C5F44E00CEAC46 /* ExpenseListViewModel.swift */; };
B0D600DD23C5FB6200CEAC46 /* ExpenseRowViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D600DC23C5FB6200CEAC46 /* ExpenseRowViewModel.swift */; };
B0E073D223C3837C00E72D6E /* EntryFormViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E073D123C3837C00E72D6E /* EntryFormViewModel.swift */; };
B0E1EEE123AEA3BB00EF9F7E /* mockCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E1EEE023AEA3BB00EF9F7E /* mockCreator.swift */; };
B0E1EEE323AEE77800EF9F7E /* domainSpecs.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E1EEE223AEE77800EF9F7E /* domainSpecs.swift */; };
@@ -86,6 +88,7 @@
B005F58023BE152400CE9BE8 /* EntryFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryFormView.swift; sourceTree = "<group>"; };
B01A65C323BBF69200DC7A0E /* expenses.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = expenses.json; sourceTree = "<group>"; };
B01A65C523BBF80C00DC7A0E /* loadMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = loadMock.swift; sourceTree = "<group>"; };
B03F1D4D23C615AB00D031A9 /* dataActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dataActions.swift; sourceTree = "<group>"; };
B045F5BD23C4C2D900C220FC /* String+Money.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Money.swift"; sourceTree = "<group>"; };
B06614C623BD3AA0006D3AAD /* Date+Formatted.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Formatted.swift"; sourceTree = "<group>"; };
B06614C823BD3F7F006D3AAD /* OneNumberStatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneNumberStatView.swift; sourceTree = "<group>"; };
@@ -116,6 +119,7 @@
B0CBF69623AEA09B004ECC91 /* dataReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dataReducer.swift; sourceTree = "<group>"; };
B0CBF69823AEA0A1004ECC91 /* uiReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = uiReducer.swift; sourceTree = "<group>"; };
B0D600DA23C5F44E00CEAC46 /* ExpenseListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpenseListViewModel.swift; sourceTree = "<group>"; };
B0D600DC23C5FB6200CEAC46 /* ExpenseRowViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpenseRowViewModel.swift; sourceTree = "<group>"; };
B0E073D123C3837C00E72D6E /* EntryFormViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryFormViewModel.swift; sourceTree = "<group>"; };
B0E1EEE023AEA3BB00EF9F7E /* mockCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = mockCreator.swift; sourceTree = "<group>"; };
B0E1EEE223AEE77800EF9F7E /* domainSpecs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = domainSpecs.swift; sourceTree = "<group>"; };
@@ -234,6 +238,7 @@
isa = PBXGroup;
children = (
B06FE60823AE4E59004782B3 /* actionSpecs.swift */,
B03F1D4D23C615AB00D031A9 /* dataActions.swift */,
);
path = actions;
sourceTree = "<group>";
@@ -359,8 +364,9 @@
B0E073CE23C3835600E72D6E /* viewModels */ = {
isa = PBXGroup;
children = (
B0E073D123C3837C00E72D6E /* EntryFormViewModel.swift */,
B0D600DA23C5F44E00CEAC46 /* ExpenseListViewModel.swift */,
B0D600DC23C5FB6200CEAC46 /* ExpenseRowViewModel.swift */,
B0E073D123C3837C00E72D6E /* EntryFormViewModel.swift */,
);
path = viewModels;
sourceTree = "<group>";
@@ -727,6 +733,7 @@
B06FE60923AE4E59004782B3 /* actionSpecs.swift in Sources */,
B09CAB1623BBDFDE00485A78 /* Currency.swift in Sources */,
B005F58123BE152400CE9BE8 /* EntryFormView.swift in Sources */,
B03F1D4E23C615AB00D031A9 /* dataActions.swift in Sources */,
B09CAB1223BBD91700485A78 /* ExpenseRowView.swift in Sources */,
B09B058123B3D6560086B30A /* USDExchangeTable.swift in Sources */,
B0E1EEEC23AEED1000EF9F7E /* Money.swift in Sources */,
@@ -753,6 +760,7 @@
B0CBF69723AEA09B004ECC91 /* dataReducer.swift in Sources */,
B06614C723BD3AA0006D3AAD /* Date+Formatted.swift in Sources */,
B0E073D223C3837C00E72D6E /* EntryFormViewModel.swift in Sources */,
B0D600DD23C5FB6200CEAC46 /* ExpenseRowViewModel.swift in Sources */,
B09B057723B3C1240086B30A /* Exchange.swift in Sources */,
B06FE61B23AE9BCA004782B3 /* AppState.swift in Sources */,
B06FE61923AE9A90004782B3 /* Expense.swift in Sources */,
@@ -10,11 +10,17 @@ import struct Foundation.Date
import struct Foundation.UUID

struct Expense: Model, HasName, MonetaryRecurrent, Codable, Identifiable {
var id: UUID = UUID()
var id: String = UUID().uuidString
var name: String
var value: Double = 0.0
var currency: Currency
var entries: [ExpenseEntry] = []
var recurrence: Recurrence = .notRecurrent
var reportingStartedAt: Date = Date()
}

extension Expense: Hashable {
static func == (lhs: Expense, rhs: Expense) -> Bool {
return lhs.id == rhs.id
}
}
@@ -10,7 +10,7 @@ import struct Foundation.Date
import struct Foundation.UUID

struct ExpenseEntry: Model, IsDated, Monetary, Codable, Identifiable {
var id: UUID = UUID()
var id: String = UUID().uuidString
var value: Double
var currency: Currency
var createdAt: Date = Date()
@@ -19,3 +19,7 @@ struct ExpenseEntry: Model, IsDated, Monetary, Codable, Identifiable {
extension ExpenseEntry {
static let empty = ExpenseEntry(value: 0.0, currency: .none)
}

extension ExpenseEntry: Hashable {

}
@@ -0,0 +1,10 @@
//
// dataActions.swift
// Yearvu
//
// Created by Thiago Ricieri on 08/01/20.
// Copyright Ā© 2020 Thiago Ricieri. All rights reserved.
//
struct LoadExpenses: AnyDataAction {}
struct LoadCurrencies: AnyDataAction {}
@@ -16,11 +16,11 @@ typealias Percentage = Float
// MARK: - Models
protocol Model: Identifiable {
var id: UUID { get }
var id: String { get }
}

protocol ViewModel: Identifiable {
var id: UUID { get }
var id: String { get }
}

protocol HasName {
@@ -10,6 +10,12 @@ import protocol ReSwift.Action

func dataReducer(_ action: Action, _ state: DataState) -> DataState {
var state = state


switch action {
case _ as LoadExpenses: state.expenses = load("expenses.json")
case _ as LoadCurrencies: state.currencies = ["USD", "BRL", "EUR", "GBP"]
default: break
}

return state
}
@@ -11,7 +11,7 @@ import protocol ReSwift.StateType
struct AppState: StateType {
var dataState = DataState()
var uiState = UiState()

static var current: AppState {
return yearvuStore.state
}
@@ -22,6 +22,7 @@ extension AppState: Hashable {
hasher.combine(dataState)
hasher.combine(uiState)
}

static func == (lhs: AppState, rhs: AppState) -> Bool {
return lhs.hashValue == rhs.hashValue
}
@@ -9,13 +9,21 @@
import protocol ReSwift.StateType

struct DataState: StateType {


var expenses: [Expense] = []
var currencies: [String] = []

static var current: DataState {
return yearvuStore.state.dataState
}
}

extension DataState: Hashable {

func hash(into hasher: inout Hasher) {
hasher.combine(expenses)
hasher.combine(currencies)
}

static func == (lhs: DataState, rhs: DataState) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
@@ -9,13 +9,9 @@
import protocol ReSwift.StateType

struct UiState: StateType {


static var current: UiState {
return yearvuStore.state.uiState
}
}

extension UiState: Hashable {

}
extension UiState: Hashable {}
@@ -10,10 +10,9 @@ import protocol Combine.ObservableObject
import struct Combine.Published
import struct Foundation.Date
import struct Foundation.UUID
import struct SwiftUI.Binding

class EntryFormViewModel: ObservableObject, ViewModel {
var id: UUID = UUID()
var id: String = UUID().uuidString
var entry: ExpenseEntry
let currencies: [String]

@@ -6,4 +6,18 @@
// Copyright Ā© 2020 Thiago Ricieri. All rights reserved.
//
import Foundation
import struct Combine.Published
import struct Foundation.UUID
import protocol ReSwift.StoreSubscriber
import protocol SwiftUI.ObservableObject

class ExpensesListViewModel: ObservableObject, ViewModel {
var id: String = UUID().uuidString
@Published var expenses: [Expense] = []
}

extension ExpensesListViewModel: StoreSubscriber {
func newState(state: DataState) {
expenses = state.expenses
}
}
@@ -0,0 +1,17 @@
//
// ExpenseRowViewModel.swift
// Yearvu
//
// Created by Thiago Ricieri on 08/01/20.
// Copyright Ā© 2020 Thiago Ricieri. All rights reserved.
//
import protocol Combine.ObservableObject
import struct Combine.Published
import struct Foundation.Date
import struct Foundation.UUID
import struct SwiftUI.Binding

class ExpenseRowViewModel: ObservableObject, ViewModel {
var id: String = UUID().uuidString
}
@@ -9,11 +9,11 @@
import SwiftUI

struct ExpensesListView: View {
let expenses: [Expense]
@EnvironmentObject var viewModel: ExpensesListViewModel

var body: some View {
NavigationView {
List(expenses) { expense in
List(viewModel.expenses) { expense in
NavigationLink(destination: ExpenseDetailView(expense: expense)) {
ExpenseRowView(expense: expense)
}
@@ -26,7 +26,7 @@ struct ExpensesListView: View {
#if DEBUG
struct ExpensesListView_Previews: PreviewProvider {
static var previews: some View {
ExpensesListView(expenses: expenses)
ExpensesListView()
}
}
#endif
@@ -24,11 +24,24 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// Get the managed object context from the shared persistent container.
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

// Environment object + ReSwift store subscription
let expensesViewModel = ExpensesListViewModel()
yearvuStore.subscribe(expensesViewModel) { subscription in
subscription
.select { $0.dataState }
.skip { $0.expenses == $1.expenses }
}

// Dispatch action to load expenses
yearvuStore.dispatch(LoadExpenses())
yearvuStore.dispatch(LoadCurrencies())

// Create the SwiftUI view and set the context as the value for the managedObjectContext environment keyPath.
// Add `@Environment(\.managedObjectContext)` in the views that will need the context.
let contentView = ExpensesListView(expenses: expenses)
let contentView = ExpensesListView()
.environment(\.managedObjectContext, context)
.environmentObject(expensesViewModel)

// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {

0 comments on commit ffffade

Please sign in to comment.
You canā€™t perform that action at this time.