# HG changeset patch # User Dennis Concepción Martín <66180929+denniscm190@users.noreply.github.com> # Date 1612986358 -3600 # Node ID 867cc725c6a5fc2703949ac0d7e855637837029c # Parent 528550464befd466c1d383832ed75d51a2e40d85 Comment code diff -r 528550464bef -r 867cc725c6a5 LazyBear.xcodeproj/project.pbxproj --- a/LazyBear.xcodeproj/project.pbxproj Mon Feb 08 23:18:53 2021 +0100 +++ b/LazyBear.xcodeproj/project.pbxproj Wed Feb 10 20:45:58 2021 +0100 @@ -39,6 +39,7 @@ 95B3552825CD4A5600BCDE8E /* NewsDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95B3552725CD4A5600BCDE8E /* NewsDetail.swift */; }; 95B3552F25CD629F00BCDE8E /* TransactionCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95B3552E25CD629F00BCDE8E /* TransactionCodes.swift */; }; 95B395A525BDF42E009A7EB0 /* companies.json in Resources */ = {isa = PBXBuildFile; fileRef = 95B395A425BDF42E009A7EB0 /* companies.json */; }; + 95BF355225D464B20010E48D /* CoreDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BF355125D464B20010E48D /* CoreDataManager.swift */; }; 95D1BF4925ADCF7700E5D063 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95D1BF4825ADCF7700E5D063 /* Persistence.swift */; }; 95DDC7B025D1ABF5002B2C9A /* Title.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95DDC7AF25D1ABF5002B2C9A /* Title.swift */; }; 95DDC7B525D1AD3C002B2C9A /* Placeholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95DDC7B425D1AD3C002B2C9A /* Placeholder.swift */; }; @@ -100,6 +101,7 @@ 95B3552725CD4A5600BCDE8E /* NewsDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NewsDetail.swift; path = LazyBear/Views/NewsDetail.swift; sourceTree = SOURCE_ROOT; }; 95B3552E25CD629F00BCDE8E /* TransactionCodes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TransactionCodes.swift; path = lazybear/Data/TransactionCodes.swift; sourceTree = SOURCE_ROOT; }; 95B395A425BDF42E009A7EB0 /* companies.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = companies.json; path = lazybear/Data/companies.json; sourceTree = SOURCE_ROOT; }; + 95BF355125D464B20010E48D /* CoreDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CoreDataManager.swift; path = lazybear/CoreDataManager.swift; sourceTree = SOURCE_ROOT; }; 95D1BF4825ADCF7700E5D063 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Persistence.swift; path = LazyBear/Persistence.swift; sourceTree = SOURCE_ROOT; }; 95DDC7AF25D1ABF5002B2C9A /* Title.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Title.swift; path = lazybear/Views/Title.swift; sourceTree = SOURCE_ROOT; }; 95DDC7B425D1AD3C002B2C9A /* Placeholder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Placeholder.swift; path = lazybear/Views/Placeholder.swift; sourceTree = SOURCE_ROOT; }; @@ -196,8 +198,8 @@ 959D28E125CC9B370029F689 /* NewsRow.swift */, 95B3552725CD4A5600BCDE8E /* NewsDetail.swift */, 95E07BA225CEAC7D001718AB /* Transactions.swift */, + 95E07B9F25CEAC61001718AB /* TransactionRow.swift */, 95E07B9C25CEAC46001718AB /* TransactionDetail.swift */, - 95E07B9F25CEAC61001718AB /* TransactionRow.swift */, 95ED8CDF25D03D8900B6B605 /* StockAndNews.swift */, ); path = Views; @@ -261,6 +263,7 @@ 95D1BF4825ADCF7700E5D063 /* Persistence.swift */, 95078FD025BF4E640004FA75 /* CloudKitManager.swift */, 958B678425C42B2400BF9F89 /* ApiManager.swift */, + 95BF355125D464B20010E48D /* CoreDataManager.swift */, 95B04EB225212369000AD27F /* LazyBearApp.swift */, 95E07B6A25CE9398001718AB /* AppView.swift */, 95B04EB425212369000AD27F /* ContentView.swift */, @@ -369,6 +372,7 @@ 95B3552F25CD629F00BCDE8E /* TransactionCodes.swift in Sources */, 95612C512598D48200F7698F /* SearchBar.swift in Sources */, 95E411BE25BEEA6C00A9C23F /* WatchlistRow.swift in Sources */, + 95BF355225D464B20010E48D /* CoreDataManager.swift in Sources */, 95078FD125BF4E640004FA75 /* CloudKitManager.swift in Sources */, 95B04EB525212369000AD27F /* ContentView.swift in Sources */, 95AB4A90259DD66D0064C9C1 /* CompanyRow.swift in Sources */, diff -r 528550464bef -r 867cc725c6a5 LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate Binary file LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate has changed diff -r 528550464bef -r 867cc725c6a5 lazybear/ApiManager.swift --- a/lazybear/ApiManager.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/ApiManager.swift Wed Feb 10 20:45:58 2021 +0100 @@ -5,11 +5,10 @@ // Created by Dennis Concepción Martín on 29/1/21. // -import SwiftUI -import CloudKit +import Foundation class ApiManager: ObservableObject { @Published var results = [ApiManagerModel]() @Published var showingView = false - @Published var option = 2 // 1 -> Sandbox / 2 -> Production + @Published var option = 1 // 1 -> Sandbox / 2 -> Production } diff -r 528550464bef -r 867cc725c6a5 lazybear/AppView.swift --- a/lazybear/AppView.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/AppView.swift Wed Feb 10 20:45:58 2021 +0100 @@ -8,29 +8,36 @@ import SwiftUI import CloudKit +/* + This view is called inmediately after the app is launched. When it appears, it will + request from my Apple server the API keys and URL to request the IEX Cloud API in later views. + If the request is successful, then we can show the ContentView() +*/ + struct AppView: View { - let persistenceController = PersistenceController.shared // Core Data - - // <--------- CloudKit ---------> - let cloud = CloudKitManager() - @EnvironmentObject var apiManager: ApiManager // Env apis info - @State var cloudFetch = [CKRecord]() { didSet { cloudValues() }} // Fetch arrives here - // <--------- CloudKit ---------> + let persistenceController = PersistenceController.shared // Core Data + let cloud = CloudKitManager() // CloudKit request function + @EnvironmentObject var apiManager: ApiManager // Api info + @State var cloudFetch = [CKRecord]() { didSet { cloudValues() }} // Fetch arrives here var body: some View { VStack { if apiManager.showingView { ContentView() .environment(\.managedObjectContext, persistenceController.container.viewContext) - .environmentObject(apiManager) // Api info (url and token) + .environmentObject(apiManager) } - }.onAppear { cloud.query(recordType: "API") { cloudFetch = $0 } } // Request CloudKit + } + // Request CloudKit + .onAppear { cloud.query(recordType: "API") { cloudFetch = $0 } } } - // Assign CloudKit fetch to model + // Assign CloudKit fetch to model structure private func cloudValues() { var results = [ApiManagerModel]() cloudFetch.forEach({ (result) in + + // In result, search for the key named 'key', 'name', and 'url' let key = result.object(forKey: "key") as? String let name = result.object(forKey: "name") as? String let url = result.object(forKey: "url") as? String @@ -38,9 +45,12 @@ let value = ApiManagerModel(key: key, name: name, url: url) results.append(value) }) + // Main thread DispatchQueue.main.async { apiManager.results = results + + // Everything is ok, so I can show the ContentView() apiManager.showingView = true } } diff -r 528550464bef -r 867cc725c6a5 lazybear/CloudKitManager.swift --- a/lazybear/CloudKitManager.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/CloudKitManager.swift Wed Feb 10 20:45:58 2021 +0100 @@ -5,7 +5,7 @@ // Created by Dennis Concepción Martín on 25/1/21. // -import SwiftUI +import Foundation import CloudKit class CloudKitManager { diff -r 528550464bef -r 867cc725c6a5 lazybear/ContentView.swift --- a/lazybear/ContentView.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/ContentView.swift Wed Feb 10 20:45:58 2021 +0100 @@ -8,7 +8,7 @@ import SwiftUI struct ContentView: View { - @EnvironmentObject var apiManager: ApiManager // Env apis info + @EnvironmentObject var apiManager: ApiManager // Api info let persistenceController = PersistenceController.shared // Core Data var body: some View { @@ -16,7 +16,7 @@ // First view Search() .environment(\.managedObjectContext, persistenceController.container.viewContext) - .environmentObject(apiManager) // Api info (url and token) + .environmentObject(apiManager) .tabItem { Image(systemName: "magnifyingglass") diff -r 528550464bef -r 867cc725c6a5 lazybear/CoreDataManager.swift --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lazybear/CoreDataManager.swift Wed Feb 10 20:45:58 2021 +0100 @@ -0,0 +1,8 @@ +// +// CoreDataManager.swift +// LazyBear +// +// Created by Dennis Concepción Martín on 10/2/21. +// + +import Foundation diff -r 528550464bef -r 867cc725c6a5 lazybear/LazyBearApp.swift --- a/lazybear/LazyBearApp.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/LazyBearApp.swift Wed Feb 10 20:45:58 2021 +0100 @@ -10,13 +10,13 @@ @main struct LazyBearApp: App { let persistenceController = PersistenceController.shared // Core Data - var apiManager = ApiManager() // Environment data + var apiManager = ApiManager() // For Cloudkit request var body: some Scene { WindowGroup { AppView() - .environment(\.managedObjectContext, persistenceController.container.viewContext) - .environmentObject(apiManager) // Api info (url and token) + .environment(\.managedObjectContext, persistenceController.container.viewContext) // Pass Core Data + .environmentObject(apiManager) // Pass Api info } } } diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/AddButton.swift --- a/lazybear/Views/AddButton.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/AddButton.swift Wed Feb 10 20:45:58 2021 +0100 @@ -8,6 +8,10 @@ import SwiftUI import SPAlert +/* + Generic add button. When tapped many options could be shown. Save to favourite, to watchlist, etc + */ + struct AddButton: View { var symbol: String var name: String @@ -22,7 +26,7 @@ Button(action: { self.showingActionSheet = true }) { Image(systemName: "plus") .imageScale(.large) - .frame(width: 30, height: 30) // This frame change the tappable area + .frame(width: 30, height: 30) // Increase tappable area .actionSheet(isPresented: $showingActionSheet) { alert() } } } @@ -31,7 +35,7 @@ private func alert() -> ActionSheet { var buttons: [ActionSheet.Button] = [.cancel(Text("Cancel")) { self.showingActionSheet = false }] - // Logic to create buttons + // If the company is already in watchlist => show Remove from watchlist instead let watchlistSymbols = companies.map { $0.symbol } if watchlistSymbols.contains(symbol) { @@ -47,6 +51,7 @@ return action } + // Save company to Watchlist private func addWatchlist() { let alertView = SPAlertView(title: "Added to watchlist", preset: .done) let watchlistCompany = WatchlistData(context: viewContext) @@ -61,6 +66,7 @@ } } + // Remove company from watchlist func removeWatchlist() { let alertView = SPAlertView(title: "Removed from watchlist", preset: .done) let watchlistSymbols = companies.map { $0.symbol } diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/AddWatchlist.swift --- a/lazybear/Views/AddWatchlist.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/AddWatchlist.swift Wed Feb 10 20:45:58 2021 +0100 @@ -8,30 +8,35 @@ import SwiftUI import SPAlert +/* + If the company is not in watchlist => Add it + */ + struct AddWatchlist: View { var symbol: String var name: String - // <--------- Core Data ---------> + // CoreData variables @Environment(\.managedObjectContext) private var viewContext @FetchRequest(entity: WatchlistData.entity(), sortDescriptors: []) var watchlistData: FetchedResults - // <--------- Core Data ---------> var body: some View { - let watchlistSymbols = watchlistData.map { $0.symbol } - let alertView = SPAlertView(title: "Company added", preset: .done) + let watchlistSymbols = watchlistData.map { $0.symbol } // Array of symbols + let alertView = SPAlertView(title: "Company added", preset: .done) // Create HUD + // If symbol is not in the array of symbols => show add button if !watchlistSymbols.contains(symbol) { Button(action: { addWatchlist() - alertView.present(haptic: .success) + alertView.present(haptic: .success) // Show HUD when added }) { Text("Add") } } } + // Add to watchlist private func addWatchlist() { let watchlistData = WatchlistData(context: viewContext) watchlistData.name = name @@ -44,10 +49,10 @@ } } } -/* + struct AddWatchlist_Previews: PreviewProvider { static var previews: some View { AddWatchlist(symbol: "aapl", name: "apple") } } - */ + diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/Company.swift --- a/lazybear/Views/Company.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/Company.swift Wed Feb 10 20:45:58 2021 +0100 @@ -7,12 +7,14 @@ import SwiftUI +/* + Base view when a company is tapped. On appear it will save the company to HistoryData. + */ + struct Company: View { var name: String var symbol: String @State var viewSelected = 0 - - @Environment(\.presentationMode) var presentationMode @Environment(\.managedObjectContext) private var viewContext // Core data var body: some View { @@ -37,6 +39,7 @@ ) } + // Save company to HistoryData private func saveSearch(name: String, symbol: String) { let searched = HistoryData(context: viewContext) searched.name = name diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/CompanyList.swift --- a/lazybear/Views/CompanyList.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/CompanyList.swift Wed Feb 10 20:45:58 2021 +0100 @@ -12,11 +12,12 @@ var body: some View { List { + //Filter list ForEach(companiesData.filter({ searchedCompany.isEmpty ? true : $0.name.localizedStandardContains(searchedCompany) }) , id: \.symbol) { company in CompanyRow(companyModel: company) } - } .id(UUID()) // Increase speed in search the list + } .id(UUID()) // Increase list speed when searching } } diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/CompanyRow.swift --- a/lazybear/Views/CompanyRow.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/CompanyRow.swift Wed Feb 10 20:45:58 2021 +0100 @@ -7,9 +7,15 @@ import SwiftUI +/* + Reuse this struct for two types of data, CompanyModel and HistoryData. + If HistoryData is passed => show the base row + date added to the list. + Else show base row + */ + struct CompanyRow: View { - var companyModel: CompanyModel? - var historyData: HistoryData? + var companyModel: CompanyModel? //Optional + var historyData: HistoryData? // Optional @State var showingCompany = false var body: some View { @@ -36,6 +42,7 @@ } } + // From long date get only day and month in letters private func formatDate() -> (String, String) { let date = historyData?.date ?? Date() let calendar = Calendar.current diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/HistoryList.swift --- a/lazybear/Views/HistoryList.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/HistoryList.swift Wed Feb 10 20:45:58 2021 +0100 @@ -7,14 +7,17 @@ import SwiftUI +/* + If the HistoryData is not empty => Show HistoryData. Else show a placeholder + */ + struct HistoryList: View { @State private var showingActionSheet = false - // <--------- Core Data ---------> + // Core Data variables @Environment(\.managedObjectContext) private var viewContext @FetchRequest(entity: HistoryData.entity(), sortDescriptors: []) var historyData: FetchedResults - // <--------- Core Data ---------> var body: some View { if historyData.count > 0 { @@ -26,7 +29,7 @@ .actionSheet(isPresented: $showingActionSheet) { alert() } } ) { - // Sorte array by Date. The new ones come first in the list + // Sore array by Date. The latest added to the list comes first let sorted = historyData.sorted { $0.date ?? Date() > $1.date ?? Date() } ForEach(sorted) { company in CompanyRow(historyData: company) diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/LineChart.swift --- a/lazybear/Views/LineChart.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/LineChart.swift Wed Feb 10 20:45:58 2021 +0100 @@ -21,14 +21,6 @@ LineChartShape(dataPoints: dataPoints, pointSize: pointSize, drawingLines: true) .stroke(lineColor, lineWidth: lineWidth) } - - // Add the data points on the line - /* - if lineColor != .clear { - LineChartShape(dataPoints: dataPoints, pointSize: pointSize, drawingLines: false) - .fill(pointColor) - } - */ } } } diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/News.swift --- a/lazybear/Views/News.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/News.swift Wed Feb 10 20:45:58 2021 +0100 @@ -11,12 +11,11 @@ var symbol: String @EnvironmentObject var apiManager: ApiManager - // <--------- API Job ---------> + // API job variables @State private var url = String() { didSet { request(url: url, model: [NewsModel].self) { self.news = $0 } }} @State private var news = [NewsModel]() - // <--------- API Job ---------> var body: some View { VStack(alignment: .leading) { @@ -33,7 +32,7 @@ .onAppear { getUrl() } } - + // Create endpoint private func getUrl() { let baseUrl = apiManager.results[apiManager.option].url ?? "" let token = apiManager.results[apiManager.option].key ?? "" diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/NewsDetail.swift --- a/lazybear/Views/NewsDetail.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/NewsDetail.swift Wed Feb 10 20:45:58 2021 +0100 @@ -32,7 +32,6 @@ Link("Read the full article", destination: URL(string: new.url ?? "")!) .padding(.top) } - } } .padding() diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/NewsRow.swift --- a/lazybear/Views/NewsRow.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/NewsRow.swift Wed Feb 10 20:45:58 2021 +0100 @@ -43,8 +43,11 @@ } } + // Cover Epoch time to human date private func epochToHours() -> (String, String) { let now = Date() // Current date + // Time when the article was published. Divide new.datetime by 1,000 because + // TimeInterval() function must be in seconds, not in miliseconds let articlePublished = Date(timeIntervalSince1970: TimeInterval(new.datetime ?? 0)/1000) let calendar = Calendar.current diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/Placeholder.swift --- a/lazybear/Views/Placeholder.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/Placeholder.swift Wed Feb 10 20:45:58 2021 +0100 @@ -7,6 +7,10 @@ import SwiftUI +/* + Placeholder when some lists are empty, such as HistoryList and WachtList + */ + struct Placeholder: View { @State var title: String @State var text: String? diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/Price.swift --- a/lazybear/Views/Price.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/Price.swift Wed Feb 10 20:45:58 2021 +0100 @@ -8,16 +8,21 @@ import SwiftUI import CloudKit +/* + Real-time stock prices. 1) On appear create endpoint, 2) when it's created request data from API, + 3) when data is received show view. Every x seconds the view will be refreshed to recall the API + and show the new stock price. + */ + struct Price: View { @State var symbol: String @State var showHorizontal: Bool @EnvironmentObject var apiManager: ApiManager - // <--------- API Job ---------> + // API job variables @State private var showingView = false @State private var url = String() { didSet { requestPrice() }} @State private var data = [QuoteModel]() { didSet { self.showingView = true }} - // <--------- API Job ---------> // Set recurrent price request. Real-time prices let timer = Timer.publish(every: 5, on: .main, in: .common).autoconnect() @@ -34,6 +39,8 @@ .font(.subheadline) .foregroundColor(percentageColor()) } + // Wrap content in a HStack if showHorizontal is passed true + // So the prices and the percentage change will show horizontal .if(showHorizontal) { content in HStack { content } } @@ -44,6 +51,7 @@ .onDisappear { self.timer.upstream.connect().cancel() } // Stop timer } + // Create endpoint private func getUrl() { let baseUrl = apiManager.results[apiManager.option].url ?? "" let token = apiManager.results[apiManager.option].key ?? "" diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/Search.swift --- a/lazybear/Views/Search.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/Search.swift Wed Feb 10 20:45:58 2021 +0100 @@ -7,6 +7,11 @@ import SwiftUI +/* + If the search is not tapped, which means the user is not searching => show HistoryList(). + If it's tapped and the text searched is bigger that 2 letters => show results + */ + struct Search: View { @State var searchedCompany = String() diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/Selection.swift --- a/lazybear/Views/Selection.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/Selection.swift Wed Feb 10 20:45:58 2021 +0100 @@ -12,7 +12,7 @@ @Binding var selected: Int var body: some View { - Picker(selection: $selected, label: Text("Please choose a period")) { + Picker(selection: $selected, label: Text("")) { ForEach(0 ..< items.count) { Text(self.items[$0]) } diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/Stock.swift --- a/lazybear/Views/Stock.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/Stock.swift Wed Feb 10 20:45:58 2021 +0100 @@ -7,24 +7,26 @@ import SwiftUI +/* + Show real-time prices and LineChart of historical stock prices. + */ + struct Stock: View { var name: String var symbol: String var lineChartHeight: CGFloat @EnvironmentObject var apiManager: ApiManager - // <--------- Picker ---------> + // Picker @State var periods = ["1W", "1M", "3M", "6M", "1Y", "2Y", "5Y"] @State var selectedPeriod = 2 - // <--------- Picker ---------> - // <--------- API Job ---------> + // API job variables @State private var url = String() { didSet { request(url: url, model: [HistoricalPricesModel].self) { self.data = $0 } }} @State private var data = [HistoricalPricesModel]() { didSet { self.showingLineChart = true }} @State private var showingLineChart = false - // <--------- API Job ---------> var body: some View { VStack(alignment: .leading) { @@ -49,6 +51,7 @@ let prices = data.map { $0.close ?? 0 } if showingLineChart { + // Normalize prices to fill the entira graph from 0 to 1 let normalPrices = normalize(prices) LineChart(dataPoints: normalPrices, lineColor: colorLineChart(prices: prices) ? .green : .red, lineWidth: 2) .frame(height: lineChartHeight) @@ -59,10 +62,16 @@ .onAppear { getUrl(range: periods[selectedPeriod]) } } + // Create endpoint private func getUrl(range: String) { var range = range let baseUrl = apiManager.results[apiManager.option].url ?? "" let token = apiManager.results[apiManager.option].key ?? "" + + // Normally every point in the graph is a day, so if a short period is called, + // it will be shown so many points in the graph and will be ugly => If a short period + // is called => call 5dm or 1mm which shows points every 15 and 30 minutes + if periods[selectedPeriod] == "1W" { range = "5dm" } if periods[selectedPeriod] == "1M" { range = "1mm" } let path = "/stable/stock/\(symbol)/chart/\(range)?chartCloseOnly=true&includeToday=false&token=" @@ -70,6 +79,7 @@ self.url = baseUrl + path + token } + // If start price < end price => profit => green color private func colorLineChart(prices: [Double]) -> Bool { let startPrice = prices.first ?? 0 let endPrice = prices.last ?? 1 diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/Title.swift --- a/lazybear/Views/Title.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/Title.swift Wed Feb 10 20:45:58 2021 +0100 @@ -7,6 +7,10 @@ import SwiftUI +/* + Views title modifier inside Company + */ + struct Title: ViewModifier { func body(content: Content) -> some View { content diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/Transactions.swift --- a/lazybear/Views/Transactions.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/Transactions.swift Wed Feb 10 20:45:58 2021 +0100 @@ -11,12 +11,11 @@ var symbol: String @EnvironmentObject var apiManager: ApiManager - // <--------- API Job ---------> + // API job variables @State private var url = String() { didSet { request(url: url, model: [InsiderTransactionModel].self) { self.data = $0 } }} @State private var data = [InsiderTransactionModel]() - // <--------- API Job ---------> var body: some View { VStack(alignment: .leading) { @@ -34,6 +33,7 @@ } .onAppear { getUrl() } } + // Create endpoint private func getUrl() { let baseUrl = apiManager.results[apiManager.option].url ?? "" let token = apiManager.results[apiManager.option].key ?? "" diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/ViewSelector.swift --- a/lazybear/Views/ViewSelector.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/ViewSelector.swift Wed Feb 10 20:45:58 2021 +0100 @@ -7,6 +7,10 @@ import SwiftUI +/* + When AddButton is tapped the following options will be shown + */ + struct ViewSelector: View { @State private var showingActionSheet = false @Binding var viewSelected: Int @@ -15,12 +19,13 @@ Button(action: { self.showingActionSheet = true }) { Image(systemName: "ellipsis") .imageScale(.large) - .frame(width: 30, height: 30) // This frame change the tappable area + .frame(width: 30, height: 30) // Increase tappable area .actionSheet(isPresented: $showingActionSheet) { alert() } } } + // Create action sheet private func alert() -> ActionSheet { let action = ActionSheet( title: Text("Views"), diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/Watchlist.swift --- a/lazybear/Views/Watchlist.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/Watchlist.swift Wed Feb 10 20:45:58 2021 +0100 @@ -8,10 +8,15 @@ import SwiftUI import CloudKit +/* + If Watchlist is empty => show placeholder. Else show watchlist. + */ + struct Watchlist: View { + // CoreData variables @Environment(\.managedObjectContext) private var viewContext @FetchRequest(entity: WatchlistData.entity(), sortDescriptors: []) - var watchlistData: FetchedResults // Fetch core data + var watchlistData: FetchedResults // List var body: some View { NavigationView { @@ -22,7 +27,8 @@ ForEach(watchlistData) { company in WatchlistRow(watchlistData: company) } - .onDelete { indexSet in deleteWatchlist(indexSet: indexSet) } // Delete from persistent storage + // Swipe to delete a company + .onDelete { indexSet in deleteWatchlist(indexSet: indexSet) } } } .toolbar { EditButton() } @@ -35,7 +41,7 @@ .navigationViewStyle(StackNavigationViewStyle()) } - + // Delete a company from watchlist func deleteWatchlist(indexSet: IndexSet) { for index in indexSet { viewContext.delete(watchlistData[index]) diff -r 528550464bef -r 867cc725c6a5 lazybear/Views/WatchlistRow.swift --- a/lazybear/Views/WatchlistRow.swift Mon Feb 08 23:18:53 2021 +0100 +++ b/lazybear/Views/WatchlistRow.swift Wed Feb 10 20:45:58 2021 +0100 @@ -19,6 +19,7 @@ let symbol = watchlistData.symbol ?? "" NavigationLink(destination: Company(name: name, symbol: symbol)) { HStack { + // Request image. SDWebImageSwiftUI framework WebImage(url: URL(string: endpoint(symbol: symbol))) .resizable() .placeholder { LogoPlaceholder() } // If there is no logo @@ -34,7 +35,9 @@ } Spacer() - if self.editMode?.wrappedValue.isEditing ?? true { } else { // If EditButton() is not clicked -> show prices + // If EditButton() is not clicked => show prices + if self.editMode?.wrappedValue.isEditing ?? true { } + else { Price(symbol: symbol, showHorizontal: false) } } @@ -42,6 +45,7 @@ } } + // Create endpoint to request logos private func endpoint(symbol: String) -> String { let url = apiManager.results[0].url let path = "/iex/api/logos/\(symbol).png"