Mercurial > public > lazybear
changeset 390:6303385b3629
Companies added to watchlists now are correctly updated
author | Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com> |
---|---|
date | Sun, 25 Apr 2021 19:52:04 +0200 |
parents | db8bc3ed526a |
children | 8ec37b2baafd |
files | LazyBear.xcodeproj/project.pbxproj LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate LazyBear/Global Models/QuoteModel.swift LazyBear/Views/Global Helpers/Company list/ExtensiveList.swift LazyBear/Views/Global Helpers/StockItem.swift LazyBear/Views/Home/HomeView.swift LazyBear/Views/Home/Networking/Home.swift LazyBear/Views/Profile/Helpers/RenameSheet.swift LazyBear/Views/Profile/Helpers/TextfieldAlert.swift LazyBear/Views/Profile/Networking/Profile.swift LazyBear/Views/Profile/ProfileView.swift LazyBear/Views/Search/CompanyList.swift LazyBear/Views/Search/SearchView.swift LazyBear/Views/WatchlistManagerTest.swift LazyBear/WatchlistManagerTest.swift |
diffstat | 15 files changed, 222 insertions(+), 208 deletions(-) [+] |
line wrap: on
line diff
--- a/LazyBear.xcodeproj/project.pbxproj Sun Apr 25 16:42:26 2021 +0200 +++ b/LazyBear.xcodeproj/project.pbxproj Sun Apr 25 19:52:04 2021 +0200 @@ -49,7 +49,7 @@ 95A7C066261639E0003E2EC1 /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A7C065261639DF003E2EC1 /* SearchView.swift */; }; 95A7C0742616409D003E2EC1 /* ParseJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A7C0732616409D003E2EC1 /* ParseJSON.swift */; }; 95AD4A2D26078C1400498079 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95AD4A2C26078C1400498079 /* ContentView.swift */; }; - 95BD2FAE26341BD1008B6752 /* RenameSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BD2FAD26341BD1008B6752 /* RenameSheet.swift */; }; + 95BD2FAE26341BD1008B6752 /* TextfieldAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BD2FAD26341BD1008B6752 /* TextfieldAlert.swift */; }; 95BD2FB326341D36008B6752 /* BlurBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BD2FB226341D36008B6752 /* BlurBackground.swift */; }; 95C8C0DB262A36990082D1D9 /* Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C8C0DA262A36990082D1D9 /* Profile.swift */; }; 95C8C0E0262A369F0082D1D9 /* ProfileResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C8C0DF262A369F0082D1D9 /* ProfileResponse.swift */; }; @@ -127,7 +127,7 @@ 95A7C065261639DF003E2EC1 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; }; 95A7C0732616409D003E2EC1 /* ParseJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseJSON.swift; sourceTree = "<group>"; }; 95AD4A2C26078C1400498079 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; }; - 95BD2FAD26341BD1008B6752 /* RenameSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenameSheet.swift; sourceTree = "<group>"; }; + 95BD2FAD26341BD1008B6752 /* TextfieldAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextfieldAlert.swift; sourceTree = "<group>"; }; 95BD2FB226341D36008B6752 /* BlurBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurBackground.swift; sourceTree = "<group>"; }; 95C8C0DA262A36990082D1D9 /* Profile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profile.swift; sourceTree = "<group>"; }; 95C8C0DF262A369F0082D1D9 /* ProfileResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileResponse.swift; sourceTree = "<group>"; }; @@ -311,6 +311,7 @@ 95672B9925DDA54800DCBE4A /* LazyBear.xcdatamodeld */, 95672B9225DDA54700DCBE4A /* Assets.xcassets */, 95672B8E25DDA54700DCBE4A /* LazyBearApp.swift */, + 95E6C57526345CE700AC1518 /* WatchlistManagerTest.swift */, 95AD4A2C26078C1400498079 /* ContentView.swift */, 95672B9725DDA54700DCBE4A /* Persistence.swift */, 950C36E2260FB6180081CF53 /* HapticsManager.swift */, @@ -381,7 +382,6 @@ 95A7C06126163943003E2EC1 /* Search */, 95FBE0DA2619CA6000440386 /* Profile */, 95893DD22613CAB5003698C5 /* Global Helpers */, - 95E6C57526345CE700AC1518 /* WatchlistManagerTest.swift */, ); path = Views; sourceTree = "<group>"; @@ -389,7 +389,7 @@ 95BD2F91263418F7008B6752 /* Helpers */ = { isa = PBXGroup; children = ( - 95BD2FAD26341BD1008B6752 /* RenameSheet.swift */, + 95BD2FAD26341BD1008B6752 /* TextfieldAlert.swift */, ); path = Helpers; sourceTree = "<group>"; @@ -606,7 +606,7 @@ 95721DB4262787EF00EC527B /* ExtensiveList.swift in Sources */, 95C8C0DB262A36990082D1D9 /* Profile.swift in Sources */, 952045152610C7C600A76362 /* ConvertEpoch.swift in Sources */, - 95BD2FAE26341BD1008B6752 /* RenameSheet.swift in Sources */, + 95BD2FAE26341BD1008B6752 /* TextfieldAlert.swift in Sources */, 95A07F5D26305A8F009865AA /* IntradayPricesModel.swift in Sources */, 95A07F7626305AE3009865AA /* TradingDatesModel.swift in Sources */, );
Binary file LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate has changed
--- a/LazyBear/Global Models/QuoteModel.swift Sun Apr 25 16:42:26 2021 +0200 +++ b/LazyBear/Global Models/QuoteModel.swift Sun Apr 25 19:52:04 2021 +0200 @@ -8,7 +8,7 @@ import SwiftUI struct QuoteModel: Codable { - var changePercent: Double + var changePercent: Double? var companyName: String - var latestPrice: Double + var latestPrice: Double? }
--- a/LazyBear/Views/Global Helpers/Company list/ExtensiveList.swift Sun Apr 25 16:42:26 2021 +0200 +++ b/LazyBear/Views/Global Helpers/Company list/ExtensiveList.swift Sun Apr 25 19:52:04 2021 +0200 @@ -59,7 +59,7 @@ .onTapGesture { showRenameAction = false } // Show rename Action Sheet - RenameSheet(listName: listName, showRenameAction: $showRenameAction, presentationMode: presentationMode) + TextfieldAlert(listName: listName, showRenameAction: $showRenameAction, presentationMode: presentationMode) .offset(y: showRenameAction ? 0: 700) .animation(.easeInOut) }
--- a/LazyBear/Views/Global Helpers/StockItem.swift Sun Apr 25 16:42:26 2021 +0200 +++ b/LazyBear/Views/Global Helpers/StockItem.swift Sun Apr 25 19:52:04 2021 +0200 @@ -60,7 +60,7 @@ .opacity(0.6) .lineLimit(1) - PriceView(latestPrice: company.latestPrice, changePercent: company.changePercent, align: .leading) + PriceView(latestPrice: company.latestPrice ?? 0, changePercent: company.changePercent ?? 0, align: .leading) .padding(.top) } @@ -70,7 +70,7 @@ if let prices = intradayPrices?.compactMap { $0.open } { LineView(data: prices) - .foregroundColor(company.changePercent < 0 ? .red: .green) + .foregroundColor(company.changePercent ?? 0 < 0 ? .red: .green) .padding(.vertical) .clipped() } @@ -105,13 +105,13 @@ if !hidePriceView { if let prices = intradayPrices?.compactMap { $0.open } { LineView(data: prices) - .foregroundColor(company.changePercent < 0 ? .red: .green) + .foregroundColor(company.changePercent ?? 0 < 0 ? .red: .green) .frame(width: 80) .padding(.vertical, 10) .padding(.leading) } - PriceView(latestPrice: company.latestPrice, changePercent: company.changePercent, align: .trailing) + PriceView(latestPrice: company.latestPrice ?? 0, changePercent: company.changePercent ?? 0, align: .trailing) // Center PriceView with the other rows .frame(minWidth: 80, alignment: .trailing) }
--- a/LazyBear/Views/Home/HomeView.swift Sun Apr 25 16:42:26 2021 +0200 +++ b/LazyBear/Views/Home/HomeView.swift Sun Apr 25 19:52:04 2021 +0200 @@ -50,7 +50,7 @@ .listRowInsets(EdgeInsets()) } } - .onReceive(timer) { _ in home.request("https://api.lazybear.app/home/type=streaming") } + .onReceive(timer) { _ in home.request("https://api.lazybear.app/home/type=streaming", isInitRequest: false) } .onDisappear { self.timer.upstream.connect().cancel() } // Stop timer .navigationTitle("\(dueDate, formatter: Self.taskDateFormat)") .navigationBarTitleDisplayMode(.inline) @@ -71,7 +71,7 @@ } else { ProgressView() .onAppear { - home.request("https://api.lazybear.app/home/type=init") + home.request("https://api.lazybear.app/home/type=init", isInitRequest: true) // Restart timer self.timer = Timer.publish(every: 10, on: .main, in: .common).autoconnect()
--- a/LazyBear/Views/Home/Networking/Home.swift Sun Apr 25 16:42:26 2021 +0200 +++ b/LazyBear/Views/Home/Networking/Home.swift Sun Apr 25 19:52:04 2021 +0200 @@ -11,14 +11,11 @@ @Published var showView = false @Published var data = HomeResponse() - var streamingRequests = 0 // Count streaming requests - - func request(_ url: String) { + func request(_ url: String, isInitRequest: Bool) { genericRequest(url: url, model: HomeResponse.self) { response in - self.streamingRequests += 1 // If is the first request -> init() - if self.streamingRequests == 1 { + if isInitRequest { self.data = response } else { // If not, request streaming data (without intradayPrices and latestCurrencies)
--- a/LazyBear/Views/Profile/Helpers/RenameSheet.swift Sun Apr 25 16:42:26 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -// -// RenameSheet.swift -// LazyBear -// -// Created by Dennis Concepción Martín on 24/4/21. -// - -import SwiftUI - -struct RenameSheet: View { - var listName: String - - @State private var newListName: String = String() - @Binding var showRenameAction: Bool - @Binding var presentationMode: PresentationMode - - @Environment(\.managedObjectContext) private var moc - @FetchRequest(entity: WatchlistCompany.entity(), sortDescriptors: []) - var watchlistCompany: FetchedResults<WatchlistCompany> - - var body: some View { - RoundedRectangle(cornerRadius: 15) - .frame(width: 280, height: 180) - .foregroundColor(Color(.secondarySystemBackground)) - .overlay( - VStack { - Text("Rename your list") - .font(.headline) - - Text("Enter a name") - .font(.callout) - - Spacer() - TextField("Technologies, banks...", text: $newListName) - .padding(7) - .background( - Color(.systemBackground) - .cornerRadius(7) - ) - - Divider() - - HStack { - Spacer() - Button(action: { - UIApplication.shared.endEditing() - self.showRenameAction = false - }) { - Text("Cancel") - .fontWeight(.semibold) - .foregroundColor(.red) - } - - - Spacer() - Divider() - - Spacer() - Button(action: { renameList(newListName) }) { - Text("Done") - } - Spacer() - } - .frame(height: 25) - } - .padding() - ) - .background( - BlurBackground(style: .systemMaterial) - .clipShape(RoundedRectangle(cornerRadius: 15)) - ) - } - - private func renameList(_ newListName: String) { - let selectedWatchlist = watchlistCompany.filter({ $0.watchlist == listName }) - for company in selectedWatchlist { - company.watchlist = newListName - } - do { - try moc.save() - print("List updated") - UIApplication.shared.endEditing() // Dismiss Keyboard - self.showRenameAction = false // Dismiss action rename sheet - self.$presentationMode.wrappedValue.dismiss() // Dismiss Modal View - } catch { - print(error.localizedDescription) - } - } -} -// Dismiss Keyboard -extension UIApplication { - func endEditing() { - sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) - } -} - -struct RenameSheet_Previews: PreviewProvider { - @Environment(\.presentationMode) static var presentationMode - - static var previews: some View { - RenameSheet(listName: "MyWatchlist", showRenameAction: .constant(true), presentationMode: presentationMode) - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Views/Profile/Helpers/TextfieldAlert.swift Sun Apr 25 19:52:04 2021 +0200 @@ -0,0 +1,104 @@ +// +// TextfieldAlert.swift +// LazyBear +// +// Created by Dennis Concepción Martín on 24/4/21. +// + +import SwiftUI + +struct TextfieldAlert: View { + var listName: String + + @State private var newListName: String = String() + @Binding var showRenameAction: Bool + @Binding var presentationMode: PresentationMode + + @Environment(\.managedObjectContext) private var moc + @FetchRequest(entity: WatchlistCompany.entity(), sortDescriptors: []) + var watchlistCompany: FetchedResults<WatchlistCompany> + + var body: some View { + RoundedRectangle(cornerRadius: 15) + .frame(width: 280, height: 180) + .foregroundColor(Color(.secondarySystemBackground)) + .overlay( + VStack { + Text("Rename your list") + .font(.headline) + + Text("Enter a name") + .font(.callout) + + Spacer() + TextField("Technologies, banks...", text: $newListName) + .padding(7) + .background( + Color(.systemBackground) + .cornerRadius(7) + ) + + Divider() + + + // Cancel and Done buttons + HStack { + Spacer() + Button(action: { + UIApplication.shared.endEditing() + self.showRenameAction = false + }) { + Text("Cancel") + .fontWeight(.semibold) + .foregroundColor(.red) + } + + Spacer() + Divider() + + Spacer() + Button(action: { renameList(newListName) }) { + Text("Done") + } + Spacer() + } + .frame(height: 25) + } + .padding() + ) + .background( + BlurBackground(style: .systemMaterial) + .clipShape(RoundedRectangle(cornerRadius: 15)) + ) + } + + private func renameList(_ newListName: String) { + let selectedWatchlist = watchlistCompany.filter({ $0.watchlist == listName }) + for company in selectedWatchlist { + company.watchlist = newListName + } + do { + try moc.save() + print("List updated") + UIApplication.shared.endEditing() // Dismiss Keyboard + self.showRenameAction = false // Dismiss action rename sheet + self.$presentationMode.wrappedValue.dismiss() // Dismiss Modal View + } catch { + print(error.localizedDescription) + } + } +} +// Dismiss Keyboard +extension UIApplication { + func endEditing() { + sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + } +} + +struct RenameSheet_Previews: PreviewProvider { + @Environment(\.presentationMode) static var presentationMode + + static var previews: some View { + TextfieldAlert(listName: "MyWatchlist", showRenameAction: .constant(true), presentationMode: presentationMode) + } +}
--- a/LazyBear/Views/Profile/Networking/Profile.swift Sun Apr 25 16:42:26 2021 +0200 +++ b/LazyBear/Views/Profile/Networking/Profile.swift Sun Apr 25 19:52:04 2021 +0200 @@ -11,14 +11,11 @@ @Published var showView = false @Published var data = ProfileResponse() - var streamingRequests = 0 // Count streaming requests - - func request(_ url: String) { + func request(_ url: String, isInitRequest: Bool) { genericRequest(url: url, model: ProfileResponse.self) { response in - self.streamingRequests += 1 // If is the first request -> init() - if self.streamingRequests == 1 { + if isInitRequest { self.data = response } else { // If not, request streaming data (without intradayPrices)
--- a/LazyBear/Views/Profile/ProfileView.swift Sun Apr 25 16:42:26 2021 +0200 +++ b/LazyBear/Views/Profile/ProfileView.swift Sun Apr 25 19:52:04 2021 +0200 @@ -12,22 +12,17 @@ @ObservedObject var profile = Profile() @FetchRequest(entity: WatchlistCompany.entity(), sortDescriptors: []) var watchlistCompanies: FetchedResults<WatchlistCompany> - - // Refresh view when watchlistCompanies change - @State private var refreshing = false - private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave) var body: some View { if profile.showView { NavigationView { + // Get Watchlist names -> Create rows for each watchlist -> in each row, show companies List { - // Take all the different watchlist created let watchlists = Set(watchlistCompanies.map { $0.watchlist }) // Set -> avoid duplicates names + ForEach(Array(watchlists), id: \.self) { watchlist in - - // Get all the symbols of this watchlist let symbols = watchlistCompanies.filter({ $0.watchlist == watchlist }).map { $0.symbol } - + if let companies = profile.data.quotes { let filteredCompanies = companies.filter({ symbols.contains($0.key) }) StockRow(listName: watchlist, @@ -35,13 +30,10 @@ intradayPrices: profile.data.intradayPrices, addOnDelete: true ) + .onAppear { updateRows(symbols.count, filteredCompanies.count) } .listRowInsets(EdgeInsets()) } } - // The listener for refresh the view - .onReceive(self.didSave) { _ in - self.refreshing.toggle() - } } .navigationTitle("My profile") .navigationBarTitleDisplayMode(.inline) @@ -55,11 +47,11 @@ } } else { ProgressView() - .onAppear { prepareUrl() } + .onAppear { prepareUrl(isInitRequest: true) } } } - private func prepareUrl() { + private func prepareUrl(isInitRequest: Bool) { if watchlistCompanies.isEmpty { profile.showView = true } else { @@ -75,7 +67,17 @@ url += ",\(symbol)" } } - profile.request(url) + profile.request(url, isInitRequest: isInitRequest) + } + } + + /* + If Core Data changes, companies is not updated because the API request is not called -> + Check if symbols.count (Core Data) is equal to filteredCompanies -> if not -> call API + */ + private func updateRows(_ numberOfCoreDataCompanies: Int, _ numberOfApiRequestedCompanies: Int) { + if numberOfCoreDataCompanies != numberOfApiRequestedCompanies { + prepareUrl(isInitRequest: true) // Call API } } }
--- a/LazyBear/Views/Search/CompanyList.swift Sun Apr 25 16:42:26 2021 +0200 +++ b/LazyBear/Views/Search/CompanyList.swift Sun Apr 25 19:52:04 2021 +0200 @@ -16,7 +16,6 @@ SearchedCompanyItem(company: company) } } - .listStyle(GroupedListStyle()) } }
--- a/LazyBear/Views/Search/SearchView.swift Sun Apr 25 16:42:26 2021 +0200 +++ b/LazyBear/Views/Search/SearchView.swift Sun Apr 25 19:52:04 2021 +0200 @@ -49,6 +49,7 @@ } }) } + .navigationViewStyle(StackNavigationViewStyle()) } }
--- a/LazyBear/Views/WatchlistManagerTest.swift Sun Apr 25 16:42:26 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -// -// WatchlistManagerTest.swift -// LazyBear -// -// Created by Dennis Concepción Martín on 24/4/21. -// - -import SwiftUI - -struct WatchlistManagerTest: View { - @Environment(\.managedObjectContext) private var moc - @FetchRequest(entity: WatchlistCompany.entity(), sortDescriptors: []) - var watchlistCompany: FetchedResults<WatchlistCompany> - - var body: some View { - VStack { - Button("Add company", action: addCompany) - } - } - -// private func removeCompany(at offsets: IndexSet) { -// for index in offsets { -// let company = companies[index] -// moc.delete(company) -// } -// do { -// try moc.save() -// } catch { -// // Error -// } -// } - - // Add to watchlist - private func addCompany() { - let watchlistCompany = WatchlistCompany(context: moc) - watchlistCompany.symbol = "TSLA" - watchlistCompany.name = "Tesla Inc" - watchlistCompany.watchlist = "Watchlist2" - do { - try moc.save() - print("Company saved") - } catch { - print(error.localizedDescription) - } - } - -// private func removeCompany() { -// let symbols = companies.map { $0.symbol } -// let index = symbols.firstIndex(of: symbol) -// let company = companies[index!] -// moc.delete(company) -// do { -// try moc.save() -// print("Company deleted") -// } catch { -// // Error -// } -// } -} - -struct WatchlistManagerTest_Previews: PreviewProvider { - static var previews: some View { - WatchlistManagerTest() - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/WatchlistManagerTest.swift Sun Apr 25 19:52:04 2021 +0200 @@ -0,0 +1,82 @@ +// +// WatchlistManagerTest.swift +// LazyBear +// +// Created by Dennis Concepción Martín on 24/4/21. +// + +import SwiftUI + +/* + DELETE IN PRODUCTION + */ + +struct WatchlistManagerTest: View { + @Environment(\.managedObjectContext) private var moc + @FetchRequest(entity: WatchlistCompany.entity(), sortDescriptors: []) + var watchlistCompany: FetchedResults<WatchlistCompany> + + var body: some View { + VStack { + Button("Add company", action: addCompany) + Button("Reset Entity", action: resetCoreDataEntity) + } + } + +// private func removeCompany(at offsets: IndexSet) { +// for index in offsets { +// let company = companies[index] +// moc.delete(company) +// } +// do { +// try moc.save() +// } catch { +// // Error +// } +// } + + // Add to watchlist + private func addCompany() { + let watchlistCompany = WatchlistCompany(context: moc) + watchlistCompany.symbol = "TSLA" + watchlistCompany.name = "Tesla Inc" + watchlistCompany.watchlist = "My portfolio" + do { + try moc.save() + print("Company saved") + } catch { + print(error.localizedDescription) + } + } + +// private func removeCompany() { +// let symbols = companies.map { $0.symbol } +// let index = symbols.firstIndex(of: symbol) +// let company = companies[index!] +// moc.delete(company) +// do { +// try moc.save() +// print("Company deleted") +// } catch { +// // Error +// } +// } + + private func resetCoreDataEntity() { + for item in watchlistCompany { + moc.delete(item) + } + do { + try moc.save() + print("Company deleted") + } catch { + // Error + } + } +} + +struct WatchlistManagerTest_Previews: PreviewProvider { + static var previews: some View { + WatchlistManagerTest() + } +}