Skip to content

Commit

Permalink
History is now saved, can be cleared
Browse files Browse the repository at this point in the history
  • Loading branch information
antingle committed May 2, 2022
1 parent 03e4ace commit a422f24
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 59 deletions.
4 changes: 4 additions & 0 deletions Calculator.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
928D96C8280AA58D00B6CE2E /* CalculatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 928D96C7280AA58D00B6CE2E /* CalculatorView.swift */; };
928D96CA280AA5E400B6CE2E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 928D96C9280AA5E400B6CE2E /* SettingsView.swift */; };
92AA9002281EEBEA008C9EC8 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92AA9001281EEBEA008C9EC8 /* HistoryView.swift */; };
92AA9004281F802C008C9EC8 /* History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92AA9003281F802C008C9EC8 /* History.swift */; };
92B01F4A2809250B0069F3D7 /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = 92B01F492809250B0069F3D7 /* HotKey */; };
92B01F4D280942500069F3D7 /* Expression in Frameworks */ = {isa = PBXBuildFile; productRef = 92B01F4C280942500069F3D7 /* Expression */; };
92F4476C28089FEE00EC45D5 /* CalculatorApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92F4476B28089FEE00EC45D5 /* CalculatorApp.swift */; };
Expand All @@ -28,6 +29,7 @@
928D96C7280AA58D00B6CE2E /* CalculatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorView.swift; sourceTree = "<group>"; };
928D96C9280AA5E400B6CE2E /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
92AA9001281EEBEA008C9EC8 /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = "<group>"; };
92AA9003281F802C008C9EC8 /* History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = History.swift; sourceTree = "<group>"; };
92F4476828089FEE00EC45D5 /* Calculator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Calculator.app; sourceTree = BUILT_PRODUCTS_DIR; };
92F4476B28089FEE00EC45D5 /* CalculatorApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorApp.swift; sourceTree = "<group>"; };
92F4476D28089FEE00EC45D5 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -81,6 +83,7 @@
92AA9001281EEBEA008C9EC8 /* HistoryView.swift */,
928D96C5280A8F9300B6CE2E /* ButtonView.swift */,
92F447862808C3B000EC45D5 /* HelperFunctions.swift */,
92AA9003281F802C008C9EC8 /* History.swift */,
92F4476F28089FF100EC45D5 /* Assets.xcassets */,
92F4477428089FF100EC45D5 /* Calculator.entitlements */,
92F4477128089FF100EC45D5 /* Preview Content */,
Expand Down Expand Up @@ -178,6 +181,7 @@
928D96CA280AA5E400B6CE2E /* SettingsView.swift in Sources */,
928D96C8280AA58D00B6CE2E /* CalculatorView.swift in Sources */,
928D96C6280A8F9300B6CE2E /* ButtonView.swift in Sources */,
92AA9004281F802C008C9EC8 /* History.swift in Sources */,
925E9140281D02AC0027E183 /* CustomMacTextView.swift in Sources */,
92F4476E28089FEE00EC45D5 /* ContentView.swift in Sources */,
92F447852808C25800EC45D5 /* AppDelegate.swift in Sources */,
Expand Down
18 changes: 9 additions & 9 deletions Calculator/ButtonView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import SwiftUI

struct ButtonView: View {
@Binding var expression: String
@Binding var history: [History]
@Binding var historyIndex: Int
@EnvironmentObject var historyStore: HistoryStore

var body: some View {
HStack {
Expand Down Expand Up @@ -50,15 +50,15 @@ struct ButtonView: View {

Button {
// check that history array is not empty and history index does not go out of bounds
if (!history.isEmpty && historyIndex < history.count - 1)
if (!historyStore.history.isEmpty && historyIndex < historyStore.history.count - 1)
{
historyIndex += 1

// if incrementing history, remove the number of characters of previous addition
if historyIndex > 0 {
expression.removeLast(history[historyIndex - 1].solution.count)
expression.removeLast(historyStore.history[historyIndex - 1].solution.count)
}
expression += history[historyIndex].solution
expression += historyStore.history[historyIndex].solution
}
} label: {
Image(systemName: "arrow.up")
Expand Down Expand Up @@ -95,17 +95,17 @@ struct ButtonView: View {

Button {

if (!history.isEmpty)
if (!historyStore.history.isEmpty)
{
// check if UP ARROW has been pressed yet
if historyIndex != -1 {
// if decrementing history, remove the number of characters of previous addition
expression.removeLast(history[historyIndex].solution.count)
expression.removeLast(historyStore.history[historyIndex].solution.count)
historyIndex -= 1

// if we are not at the beginning of history, add the next solution in
if historyIndex != -1 {
expression += history[historyIndex].solution
expression += historyStore.history[historyIndex].solution
}
}
}
Expand Down Expand Up @@ -146,9 +146,9 @@ struct ButtonView: View {
}

func addAnswer() {
if (!history.isEmpty)
if (!historyStore.history.isEmpty)
{
expression += history[0].solution
expression += historyStore.history[0].solution
}
}
}
33 changes: 20 additions & 13 deletions Calculator/CalculatorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
import SwiftUI

struct CalculatorView: View {
@EnvironmentObject var historyStore: HistoryStore
@State private var expression: String = ""
@State private var solution: Double = 0.0
@State private var history: [History] = []
@State private var historyIndex: Int = -1

var body: some View {

VStack {
// A view to show all previous solutions and expressions
HistoryView(expression: $expression, history: $history, historyIndex: $historyIndex)
HistoryView(expression: $expression, historyIndex: $historyIndex)

// An NSTextField wrapped as a SwiftUI view
CustomMacTextView(placeholderText: "Calculate", text: $expression,
Expand All @@ -30,15 +30,22 @@ struct CalculatorView: View {
// insert item into history
withAnimation(.spring()) {
let historyItem = History(expression: expression, solution: solution.removeZerosFromEnd())
history.insert(historyItem, at: 0)

historyStore.history.insert(historyItem, at: 0)
}
expression = ""
solution = 0

// save history
HistoryStore.save(history: historyStore.history) { result in
if case .failure(let error) = result {
fatalError(error.localizedDescription)
}
}
}
catch {
solution = 0
}

},
// On every update of the textfield by keyboard
onTextChange: { newExpression in
Expand All @@ -47,30 +54,30 @@ struct CalculatorView: View {
// on UP ARROW key
onMoveUp: {
// check that history array is not empty and history index does not go out of bounds
if (!history.isEmpty && historyIndex < history.count - 1)
if (!historyStore.history.isEmpty && historyIndex < historyStore.history.count - 1)
{
historyIndex += 1

// if incrementing history, remove the number of characters of previous addition
if historyIndex > 0 {
expression.removeLast(history[historyIndex - 1].solution.count)
expression.removeLast(historyStore.history[historyIndex - 1].solution.count)
}
expression += history[historyIndex].solution
expression += historyStore.history[historyIndex].solution
}
},
// on DOWN ARROW key
onMoveDown: {
if (!history.isEmpty)
if (!historyStore.history.isEmpty)
{
// check if UP ARROW has been pressed yet
if historyIndex != -1 {
// if decrementing history, remove the number of characters of previous addition
expression.removeLast(history[historyIndex].solution.count)
expression.removeLast(historyStore.history[historyIndex].solution.count)
historyIndex -= 1

// if we are not at the beginning of history, add the next solution in
if historyIndex != -1 {
expression += history[historyIndex].solution
expression += historyStore.history[historyIndex].solution
}
}
}
Expand All @@ -81,14 +88,14 @@ struct CalculatorView: View {
.onChange(of: expression, perform: { newExpression in

// insert previous solution if these operators used first
if !history.isEmpty &&
if !historyStore.history.isEmpty &&
(newExpression == "+" ||
newExpression == "*" ||
newExpression == "/" ||
newExpression == "^" ||
newExpression == "%")
{
expression.insert(contentsOf: history[0].solution, at: expression.startIndex)
expression.insert(contentsOf: historyStore.history[0].solution, at: expression.startIndex)
}

do {
Expand All @@ -106,7 +113,7 @@ struct CalculatorView: View {
.foregroundColor(.gray)

// A view for all the buttons at the bottom
ButtonView(expression: $expression, history: $history, historyIndex: $historyIndex)
ButtonView(expression: $expression, historyIndex: $historyIndex)
}
}
}
14 changes: 13 additions & 1 deletion Calculator/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import SwiftUI
import Expression
struct ContentView: View {
@State private var showingSettings = false
@StateObject private var store = HistoryStore()

var body: some View {
VStack {

CalculatorView()
CalculatorView()

HStack {
Text("Menu Bar Calc")
Expand Down Expand Up @@ -43,6 +44,17 @@ struct ContentView: View {
}
.padding([.horizontal, .bottom])
.frame(maxWidth: .infinity, maxHeight: .infinity)
.environmentObject(store)
.onAppear {
HistoryStore.load { result in
switch result {
case .failure(let error):
fatalError(error.localizedDescription)
case .success(let history):
store.history = history
}
}
}
}
}

Expand Down
39 changes: 10 additions & 29 deletions Calculator/HelperFunctions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@
import SwiftUI
import Expression

struct History: Identifiable {
let id = UUID()
var expression: String
var solution: String
func evaluateExpression(_ givenExpression: String) throws -> Double {
var solution: Double = 0;

// ability to use ^ as a power operator
let expression = Expression(givenExpression, symbols: [
.infix("^"): { params in pow(params[0], params[1]) },
])
solution = try expression.evaluate()

return solution;
}

extension Double {
Expand All @@ -25,18 +31,6 @@ extension Double {
}
}

func evaluateExpression(_ givenExpression: String) throws -> Double {
var solution: Double = 0;

// ability to use ^ as a power operator
let expression = Expression(givenExpression, symbols: [
.infix("^"): { params in pow(params[0], params[1]) },
])
solution = try expression.evaluate()

return solution;
}

struct CalcButtonStyle: ButtonStyle {
var foregroundColor: Color
var backgroundColor: Color
Expand Down Expand Up @@ -67,16 +61,3 @@ extension Button {
)
}
}

extension View {

@discardableResult
func openInWindow(title: String, sender: Any?) -> NSWindow {
let controller = NSHostingController(rootView: self)
let win = NSWindow(contentViewController: controller)
win.contentViewController = controller
win.title = title
win.makeKeyAndOrderFront(sender)
return win
}
}
74 changes: 74 additions & 0 deletions Calculator/History.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// History.swift
// Calculator
//
// Created by Anthony Ingle on 5/1/22.
//

import Foundation
import SwiftUI

struct History: Identifiable, Codable {
let id: UUID
var date: Date
var expression: String
var solution: String

init(expression: String, solution: String) {
id = UUID()
date = .now
self.expression = expression
self.solution = solution
}
}

class HistoryStore : ObservableObject {
@Published var history: [History] = []

private static func fileURL() throws -> URL {
try FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
.appendingPathComponent("history.data")
}

static func load(completion: @escaping (Result<[History], Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
do {
let fileURL = try fileURL()
guard let file = try? FileHandle(forReadingFrom: fileURL) else {
DispatchQueue.main.async {
completion(.success([]))
}
return
}
let historyData = try JSONDecoder().decode([History].self, from: file.availableData)
DispatchQueue.main.async {
completion(.success(historyData))
}
} catch {
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
}

static func save(history: [History], completion: @escaping (Result<Int, Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
do {
let data = try JSONEncoder().encode(history)
let outfile = try fileURL()
try data.write(to: outfile)
DispatchQueue.main.async {
completion(.success(history.count))
}
} catch {
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
}
}
6 changes: 3 additions & 3 deletions Calculator/HistoryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import SwiftUI

struct HistoryView: View {
@Binding var expression: String
@Binding var history: [History]
@Binding var historyIndex: Int
@EnvironmentObject var historyStore: HistoryStore

var body: some View {
// this is an upside down scroll view to show history of expressions and solutions
ScrollView(.vertical, showsIndicators: false, content: {
LazyVStack(alignment: .center, spacing: nil, content: {

// this is reversed order since it is flipped
ForEach(history) { item in
ForEach(historyStore.history) { item in
VStack {
HStack {
Text(item.solution)
Expand All @@ -27,7 +27,7 @@ struct HistoryView: View {
.onTapGesture {
expression += item.solution
}
.foregroundColor(historyIndex != -1 ? (item.id == history[historyIndex].id ? .accentColor : .primary) : .primary)
.foregroundColor(historyIndex != -1 ? (item.id == historyStore.history[historyIndex].id ? .accentColor : .primary) : .primary)

// copy solution button
Button {
Expand Down
Loading

0 comments on commit a422f24

Please sign in to comment.