changeset 122:2d6df8debf12

Add search history and fix bugs
author Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com>
date Sat, 06 Feb 2021 17:45:04 +0100
parents c7532d18d6be
children a1acd9f4e8c8
files LazyBear.xcodeproj/project.pbxproj LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate RecentSearch+CoreDataClass.swift RecentSearch+CoreDataProperties.swift lazybear/ContentView.swift lazybear/LazyBear.xcdatamodeld/LazyBear.xcdatamodel/contents lazybear/Persistence.swift lazybear/Views/AddWatchlist.swift lazybear/Views/Company.swift lazybear/Views/CompanyRow.swift lazybear/Views/RecentSearches.swift lazybear/Views/Search.swift lazybear/Views/Watchlist.swift lazybear/Views/WatchlistRow.swift
diffstat 14 files changed, 193 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- a/LazyBear.xcodeproj/project.pbxproj	Sat Feb 06 12:33:57 2021 +0100
+++ b/LazyBear.xcodeproj/project.pbxproj	Sat Feb 06 17:45:04 2021 +0100
@@ -46,6 +46,9 @@
 		95E411A725BEE03000A9C23F /* Watchlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95E411A625BEE03000A9C23F /* Watchlist.swift */; };
 		95E411B625BEE84E00A9C23F /* Stock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95E411B525BEE84E00A9C23F /* Stock.swift */; };
 		95E411BE25BEEA6C00A9C23F /* WatchlistRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95E411BD25BEEA6C00A9C23F /* WatchlistRow.swift */; };
+		95ED176125CEF14E00AE34B3 /* RecentSearches.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95ED176025CEF14E00AE34B3 /* RecentSearches.swift */; };
+		95ED176B25CEFE1B00AE34B3 /* RecentSearch+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95ED176925CEFE1B00AE34B3 /* RecentSearch+CoreDataClass.swift */; };
+		95ED176C25CEFE1B00AE34B3 /* RecentSearch+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95ED176A25CEFE1B00AE34B3 /* RecentSearch+CoreDataProperties.swift */; };
 		95F6C2F025BAE2ED003CF389 /* Company.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F6C2EF25BAE2ED003CF389 /* Company.swift */; };
 		95F6C30925BAF7C2003CF389 /* DateSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F6C30825BAF7C2003CF389 /* DateSelection.swift */; };
 		95F6F45C25C20D8D002AC66A /* Price.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F6F45B25C20D8D002AC66A /* Price.swift */; };
@@ -97,6 +100,9 @@
 		95E411A625BEE03000A9C23F /* Watchlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Watchlist.swift; sourceTree = "<group>"; };
 		95E411B525BEE84E00A9C23F /* Stock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stock.swift; sourceTree = "<group>"; };
 		95E411BD25BEEA6C00A9C23F /* WatchlistRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchlistRow.swift; sourceTree = "<group>"; };
+		95ED176025CEF14E00AE34B3 /* RecentSearches.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RecentSearches.swift; path = lazybear/Views/RecentSearches.swift; sourceTree = SOURCE_ROOT; };
+		95ED176925CEFE1B00AE34B3 /* RecentSearch+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RecentSearch+CoreDataClass.swift"; sourceTree = "<group>"; };
+		95ED176A25CEFE1B00AE34B3 /* RecentSearch+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RecentSearch+CoreDataProperties.swift"; sourceTree = "<group>"; };
 		95F6C2EF25BAE2ED003CF389 /* Company.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Company.swift; sourceTree = "<group>"; };
 		95F6C30825BAF7C2003CF389 /* DateSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateSelection.swift; sourceTree = "<group>"; };
 		95F6F45B25C20D8D002AC66A /* Price.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Price.swift; sourceTree = "<group>"; };
@@ -191,6 +197,8 @@
 		95B04EA625212369000AD27F = {
 			isa = PBXGroup;
 			children = (
+				95ED176925CEFE1B00AE34B3 /* RecentSearch+CoreDataClass.swift */,
+				95ED176A25CEFE1B00AE34B3 /* RecentSearch+CoreDataProperties.swift */,
 				95FE646525C2DC570052832E /* WatchlistCompany+CoreDataClass.swift */,
 				95FE646625C2DC570052832E /* WatchlistCompany+CoreDataProperties.swift */,
 				95B04EB125212369000AD27F /* LazyBear */,
@@ -237,6 +245,7 @@
 				95E07B6D25CE95A1001718AB /* Search.swift */,
 				95612C4F2598D48200F7698F /* SearchBar.swift */,
 				95AB4A8F259DD66D0064C9C1 /* CompanyRow.swift */,
+				95ED176025CEF14E00AE34B3 /* RecentSearches.swift */,
 			);
 			name = Search;
 			sourceTree = "<group>";
@@ -394,6 +403,7 @@
 				95FE646B25C30B880052832E /* ApiModel.swift in Sources */,
 				95B3552F25CD629F00BCDE8E /* TransactionCodes.swift in Sources */,
 				95612C512598D48200F7698F /* SearchBar.swift in Sources */,
+				95ED176125CEF14E00AE34B3 /* RecentSearches.swift in Sources */,
 				95E411BE25BEEA6C00A9C23F /* WatchlistRow.swift in Sources */,
 				95078FD125BF4E640004FA75 /* CloudKitManager.swift in Sources */,
 				95B04EB525212369000AD27F /* ContentView.swift in Sources */,
@@ -407,6 +417,8 @@
 				958B678525C42B2400BF9F89 /* ApiAccess.swift in Sources */,
 				95B04EB325212369000AD27F /* LazyBearApp.swift in Sources */,
 				95E07B6B25CE9398001718AB /* AppView.swift in Sources */,
+				95ED176C25CEFE1B00AE34B3 /* RecentSearch+CoreDataProperties.swift in Sources */,
+				95ED176B25CEFE1B00AE34B3 /* RecentSearch+CoreDataClass.swift in Sources */,
 				95AB4A7D259DCC0C0064C9C1 /* CompanyModel.swift in Sources */,
 				95E411B625BEE84E00A9C23F /* Stock.swift in Sources */,
 				9520F0B225C712D000692610 /* LineChartShape.swift in Sources */,
Binary file LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RecentSearch+CoreDataClass.swift	Sat Feb 06 17:45:04 2021 +0100
@@ -0,0 +1,15 @@
+//
+//  RecentSearch+CoreDataClass.swift
+//  LazyBear
+//
+//  Created by Dennis Concepción Martín on 6/2/21.
+//
+//
+
+import Foundation
+import CoreData
+
+@objc(RecentSearch)
+public class RecentSearch: NSManagedObject {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RecentSearch+CoreDataProperties.swift	Sat Feb 06 17:45:04 2021 +0100
@@ -0,0 +1,27 @@
+//
+//  RecentSearch+CoreDataProperties.swift
+//  LazyBear
+//
+//  Created by Dennis Concepción Martín on 6/2/21.
+//
+//
+
+import Foundation
+import CoreData
+
+
+extension RecentSearch {
+
+    @nonobjc public class func fetchRequest() -> NSFetchRequest<RecentSearch> {
+        return NSFetchRequest<RecentSearch>(entityName: "RecentSearch")
+    }
+
+    @NSManaged public var name: String?
+    @NSManaged public var symbol: String?
+    @NSManaged public var date: Date?
+
+}
+
+extension RecentSearch : Identifiable {
+
+}
--- a/lazybear/ContentView.swift	Sat Feb 06 12:33:57 2021 +0100
+++ b/lazybear/ContentView.swift	Sat Feb 06 17:45:04 2021 +0100
@@ -24,11 +24,11 @@
             }
         
         // Second view
-            //Watchlist()
-            //.tabItem {
-                //Image(systemName: "list.bullet")
-                //Text("Watchlist")
-            //}
+            Watchlist()
+            .tabItem {
+                Image(systemName: "list.bullet")
+                Text("Watchlist")
+            }
         
         // Third view
             //Settings()
--- a/lazybear/LazyBear.xcdatamodeld/LazyBear.xcdatamodel/contents	Sat Feb 06 12:33:57 2021 +0100
+++ b/lazybear/LazyBear.xcdatamodeld/LazyBear.xcdatamodel/contents	Sat Feb 06 17:45:04 2021 +0100
@@ -1,5 +1,10 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20C69" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20D64" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
+    <entity name="RecentSearch" representedClassName="RecentSearch" syncable="YES">
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="name" optional="YES" attributeType="String"/>
+        <attribute name="symbol" optional="YES" attributeType="String"/>
+    </entity>
     <entity name="WatchlistCompany" representedClassName="WatchlistCompany" syncable="YES">
         <attribute name="cik" optional="YES" attributeType="String"/>
         <attribute name="currency" optional="YES" attributeType="String"/>
@@ -18,5 +23,6 @@
     </entity>
     <elements>
         <element name="WatchlistCompany" positionX="-63" positionY="-18" width="128" height="239"/>
+        <element name="RecentSearch" positionX="-63" positionY="90" width="128" height="74"/>
     </elements>
 </model>
\ No newline at end of file
--- a/lazybear/Persistence.swift	Sat Feb 06 12:33:57 2021 +0100
+++ b/lazybear/Persistence.swift	Sat Feb 06 17:45:04 2021 +0100
@@ -30,6 +30,12 @@
             newItem.cik = String()
             newItem.lei = String()
         }
+        
+        for _ in 0..<1 {
+            let newItem = RecentSearch(context: viewContext)
+            newItem.name = String()
+            newItem.symbol = String()
+        }
         do {
             try viewContext.save()
         } catch {
--- a/lazybear/Views/AddWatchlist.swift	Sat Feb 06 12:33:57 2021 +0100
+++ b/lazybear/Views/AddWatchlist.swift	Sat Feb 06 17:45:04 2021 +0100
@@ -32,7 +32,7 @@
         }
     }
     
-    func addWatchlist(name: String, symbol: String) {
+    private func addWatchlist(name: String, symbol: String) {
         let watchlistCompany = WatchlistCompany(context: viewContext)
         watchlistCompany.name = name
         watchlistCompany.symbol = symbol
--- a/lazybear/Views/Company.swift	Sat Feb 06 12:33:57 2021 +0100
+++ b/lazybear/Views/Company.swift	Sat Feb 06 17:45:04 2021 +0100
@@ -12,35 +12,34 @@
     var symbol: String
     
     @Environment(\.presentationMode) var presentationMode
+    @Environment(\.managedObjectContext) private var viewContext  // Core data
     
     var body: some View {
-        NavigationView {
-            TabView {
-                GeometryReader { geo in
-                    ScrollView {
-                        VStack(alignment: .leading) {
-                            Stock(name: name, symbol: symbol, lineChartHeight: geo.size.height*0.2)
-                                .padding(.bottom)
-                            
-                            News(symbol: symbol)
-                        }
-                    }
+        GeometryReader { geo in
+            ScrollView {
+                VStack(alignment: .leading) {
+                    Stock(name: name, symbol: symbol, lineChartHeight: geo.size.height*0.2)
+                        .padding(.bottom)
+                    
+                    News(symbol: symbol)
                 }
-            .tabItem {
-                Image(systemName: "magnifyingglass")
-                Text("Stock")
+                .onAppear { saveSearch(name: name, symbol: symbol) }
             }
         }
-        .navigationBarTitle(symbol, displayMode: .inline)
-        .navigationBarItems(
-            leading:
-                Button(action: { self.presentationMode.wrappedValue.dismiss() }) {
-                    Image(systemName: "multiply")
-                }
-            ,
-            trailing:
-                AddWatchlist(symbol: symbol, name: name)
-        )
+        .navigationBarTitle(symbol, displayMode: .large)
+        .navigationBarItems(trailing: AddWatchlist(symbol: symbol, name: name))
+    }
+    
+    private func saveSearch(name: String, symbol: String) {
+        let searched = RecentSearch(context: viewContext)
+        searched.name = name
+        searched.symbol = symbol
+        searched.date = Date()
+        do {
+            try viewContext.save()
+            print("Search saved")
+        } catch {
+            print(error.localizedDescription)
         }
     }
 }
--- a/lazybear/Views/CompanyRow.swift	Sat Feb 06 12:33:57 2021 +0100
+++ b/lazybear/Views/CompanyRow.swift	Sat Feb 06 17:45:04 2021 +0100
@@ -8,27 +8,29 @@
 import SwiftUI
 
 struct CompanyRow: View {
-    var company: CompanyModel
+    var company: CompanyModel?
+    var history: RecentSearch?
     @State var showingCompany = false
     
     @EnvironmentObject var apiAccess: ApiAccess  // Env apis info
     let persistenceController = PersistenceController.shared // Core Data
     
     var body: some View {
-        Button(action: { self.showingCompany.toggle() }) {
+        let name = company?.name ?? history?.name ?? ""
+        let symbol = company?.symbol ?? history?.symbol ?? ""
+        
+        NavigationLink(destination: Company(name: name, symbol: symbol)
+                        .environment(\.managedObjectContext, persistenceController.container.viewContext)
+                        .environmentObject(apiAccess)  // Api info (url and token)
+        ) {
             VStack(alignment: .leading) {
-                Text(company.symbol.uppercased())
+                Text(symbol.uppercased())
                     .fontWeight(.semibold)
                 
-                Text(company.name.capitalized)
+                Text(name.capitalized)
                     .font(.subheadline)
             }
         }
-        .fullScreenCover(isPresented: $showingCompany) {
-            Company(name: company.name, symbol: company.symbol)
-                .environment(\.managedObjectContext, persistenceController.container.viewContext)
-                .environmentObject(apiAccess)  // Api info (url and token)
-        }
     }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lazybear/Views/RecentSearches.swift	Sat Feb 06 17:45:04 2021 +0100
@@ -0,0 +1,23 @@
+//
+//  RecentSearches.swift
+//  LazyBear
+//
+//  Created by Dennis Concepción Martín on 6/2/21.
+//
+
+import SwiftUI
+
+struct RecentSearches: View {
+    var body: some View {
+        VStack {
+            Text("Recent searches")
+                .fontWeight(.semibold)
+        }
+    }
+}
+
+struct RecentSearches_Previews: PreviewProvider {
+    static var previews: some View {
+        RecentSearches()
+    }
+}
--- a/lazybear/Views/Search.swift	Sat Feb 06 12:33:57 2021 +0100
+++ b/lazybear/Views/Search.swift	Sat Feb 06 17:45:04 2021 +0100
@@ -10,7 +10,13 @@
 struct Search: View {
     @State var searchedCompany = String()
     @EnvironmentObject var apiAccess: ApiAccess  // Env apis info
+    
+    // <--------- Core Data --------->
     let persistenceController = PersistenceController.shared // Core Data
+    @Environment(\.managedObjectContext) private var viewContext
+    @FetchRequest(entity: RecentSearch.entity(), sortDescriptors: [])
+    var searches: FetchedResults<RecentSearch>
+    // <--------- Core Data --------->
     
     var body: some View {
         NavigationView {
@@ -23,6 +29,16 @@
                                 .environment(\.managedObjectContext, persistenceController.container.viewContext)
                                 .environmentObject(apiAccess)  // Api info (url and token)
                         }
+                    } else {
+                        if searches.count > 0 {
+                            Section(header: Text("History"),
+                                    footer: Button(action: { delete() }) { Text("Clear history").foregroundColor(.blue)}
+                            ) {
+                                ForEach(searches) { search in
+                                    CompanyRow(history: search)
+                                }
+                            }
+                        }
                     }
                 }
                 .id(UUID())  // Increase speed in search the list
@@ -30,6 +46,18 @@
             }
         } .navigationViewStyle(StackNavigationViewStyle())
     }
+    
+    private func delete() {
+        for value in searches {
+            viewContext.delete(value)
+        }
+        do {
+            try viewContext.save()
+            print("History deleted")
+        } catch {
+            print(error.localizedDescription)
+        }
+    }
 }
 
 struct Search_Previews: PreviewProvider {
--- a/lazybear/Views/Watchlist.swift	Sat Feb 06 12:33:57 2021 +0100
+++ b/lazybear/Views/Watchlist.swift	Sat Feb 06 17:45:04 2021 +0100
@@ -16,17 +16,15 @@
     var body: some View {
         NavigationView {
             List {
-                Section(header: Text("Watchlist")) {
-                    ForEach(companies) { company in
-                        WatchlistRow(company: company)
-                    }
-                    .onDelete { indexSet in deleteWatchlist(indexSet: indexSet) }  // Delete from persistent storage
+                ForEach(companies) { company in
+                    WatchlistRow(company: company)
                 }
+                .onDelete { indexSet in deleteWatchlist(indexSet: indexSet) }  // Delete from persistent storage
             }
             .navigationBarTitle("Watchlist", displayMode: .inline)
             .toolbar { EditButton() }
             
-        }
+        }.navigationViewStyle(StackNavigationViewStyle())
     }
     
     func deleteWatchlist(indexSet: IndexSet) {
--- a/lazybear/Views/WatchlistRow.swift	Sat Feb 06 12:33:57 2021 +0100
+++ b/lazybear/Views/WatchlistRow.swift	Sat Feb 06 17:45:04 2021 +0100
@@ -17,31 +17,40 @@
     let persistenceController = PersistenceController.shared
     
     var body: some View {
-        HStack {
-            let url = apiAccess.results[0].url
-            let path = "/iex/api/logos/\(company.symbol ?? "").png"
-            let endpoint = url! + path
-            
-            WebImage(url: URL(string: endpoint))
-                .resizable()
-                .placeholder { LogoPlaceholder() }  // If there is no logo
-                .indicator(.activity)
-                .modifier(LogoModifier())
-            
-            VStack(alignment: .leading) {
-                Text(company.symbol ?? "".uppercased())
-                    .fontWeight(.semibold)
+        NavigationLink(destination: Company(name: company.name ?? "", symbol: company.symbol ?? "")
+                        .environment(\.managedObjectContext, persistenceController.container.viewContext)
+                        .environmentObject(apiAccess)  // Api info (url and token)
+        ) {
+            HStack {
+                WebImage(url: URL(string: endpoint()))
+                    .resizable()
+                    .placeholder { LogoPlaceholder() }  // If there is no logo
+                    .indicator(.activity)
+                    .modifier(LogoModifier())
                 
-                Text(company.name ?? "".capitalized)
-                    .font(.subheadline)
+                VStack(alignment: .leading) {
+                    Text(company.symbol ?? "".uppercased())
+                        .fontWeight(.semibold)
+                    
+                    Text(company.name ?? "".capitalized)
+                        .font(.subheadline)
+                }
+                
+                Spacer()
+                if self.editMode?.wrappedValue.isEditing ?? true { } else { // If EditButton() is not clicked -> show prices
+                    Price(symbol: company.symbol ?? "", showHorizontal: false)
+                }
             }
-            
-            Spacer()
-            if self.editMode?.wrappedValue.isEditing ?? true { } else { // If EditButton() is not clicked -> show prices
-                Price(symbol: company.symbol ?? "", showHorizontal: false)
-            }
+            .padding([.top, .bottom], 6)
         }
-        .padding([.top, .bottom], 6)
+    }
+    
+    private func endpoint() -> String {
+        let url = apiAccess.results[0].url
+        let path = "/iex/api/logos/\(company.symbol ?? "").png"
+        let endpoint = url! + path
+        
+        return endpoint
     }
 }