Mercurial > public > lazybear
changeset 465:6953d83060a4
New design
line wrap: on
line diff
--- a/LazyBear.xcodeproj/project.pbxproj Sat Jul 17 09:33:44 2021 +0100 +++ b/LazyBear.xcodeproj/project.pbxproj Sat Jul 17 17:58:57 2021 +0100 @@ -3,10 +3,31 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ + 956CAF8426A2D3940071A75D /* CompanyQuoteModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAF8326A2D3940071A75D /* CompanyQuoteModel.swift */; }; + 956CAF8726A2D43D0071A75D /* WatchlistRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAF8626A2D43D0071A75D /* WatchlistRow.swift */; }; + 956CAF8A26A2D7500071A75D /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 956CAF8926A2D7500071A75D /* Alamofire */; }; + 956CAF9926A2DD090071A75D /* Watchlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAF9826A2DD080071A75D /* Watchlist.swift */; }; + 956CAF9F26A2DFD30071A75D /* Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAF9E26A2DFD30071A75D /* Search.swift */; }; + 956CAFA126A2E0690071A75D /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFA026A2E0690071A75D /* SearchBar.swift */; }; + 956CAFA326A2E1A60071A75D /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFA226A2E1A60071A75D /* SearchModel.swift */; }; + 956CAFA526A2E6EC0071A75D /* SearchRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFA426A2E6EC0071A75D /* SearchRow.swift */; }; + 956CAFA726A2F43A0071A75D /* CompanyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFA626A2F43A0071A75D /* CompanyView.swift */; }; + 956CAFB026A2F7490071A75D /* CompanyInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFAF26A2F7490071A75D /* CompanyInfoModel.swift */; }; + 956CAFB226A2FCF70071A75D /* StockSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFB126A2FCF70071A75D /* StockSection.swift */; }; + 956CAFB426A3004A0071A75D /* AboutSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFB326A3004A0071A75D /* AboutSection.swift */; }; + 956CAFB726A320100071A75D /* Company+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFB526A320100071A75D /* Company+CoreDataClass.swift */; }; + 956CAFB826A320100071A75D /* Company+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFB626A320100071A75D /* Company+CoreDataProperties.swift */; }; + 956CAFBA26A321D40071A75D /* ConditionallyWrapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFB926A321D40071A75D /* ConditionallyWrapView.swift */; }; + 956CAFBC26A322250071A75D /* ContentViewPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFBB26A322250071A75D /* ContentViewPad.swift */; }; + 956CAFBE26A3226B0071A75D /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFBD26A3226B0071A75D /* Sidebar.swift */; }; + 956CAFC626A33BC40071A75D /* UsStatesCoordinates.json in Resources */ = {isa = PBXBuildFile; fileRef = 956CAFC526A33BC40071A75D /* UsStatesCoordinates.json */; }; + 956CAFC826A33C520071A75D /* UsStatesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFC726A33C520071A75D /* UsStatesModel.swift */; }; + 956CAFCA26A33E660071A75D /* MapSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 956CAFC926A33E660071A75D /* MapSection.swift */; }; + 956CAFCE26A340C60071A75D /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 956CAFCD26A340C60071A75D /* CloudKit.framework */; }; 95F7C16126A2CCA2002F08DB /* lazybearApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F7C16026A2CCA2002F08DB /* lazybearApp.swift */; }; 95F7C16326A2CCA2002F08DB /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F7C16226A2CCA2002F08DB /* ContentView.swift */; }; 95F7C16526A2CCA2002F08DB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 95F7C16426A2CCA2002F08DB /* Assets.xcassets */; }; @@ -35,6 +56,27 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 956CAF8326A2D3940071A75D /* CompanyQuoteModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyQuoteModel.swift; sourceTree = "<group>"; }; + 956CAF8626A2D43D0071A75D /* WatchlistRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchlistRow.swift; sourceTree = "<group>"; }; + 956CAF9826A2DD080071A75D /* Watchlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Watchlist.swift; sourceTree = "<group>"; }; + 956CAF9E26A2DFD30071A75D /* Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Search.swift; sourceTree = "<group>"; }; + 956CAFA026A2E0690071A75D /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = "<group>"; }; + 956CAFA226A2E1A60071A75D /* SearchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModel.swift; sourceTree = "<group>"; }; + 956CAFA426A2E6EC0071A75D /* SearchRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchRow.swift; sourceTree = "<group>"; }; + 956CAFA626A2F43A0071A75D /* CompanyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyView.swift; sourceTree = "<group>"; }; + 956CAFAF26A2F7490071A75D /* CompanyInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyInfoModel.swift; sourceTree = "<group>"; }; + 956CAFB126A2FCF70071A75D /* StockSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StockSection.swift; sourceTree = "<group>"; }; + 956CAFB326A3004A0071A75D /* AboutSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutSection.swift; sourceTree = "<group>"; }; + 956CAFB526A320100071A75D /* Company+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Company+CoreDataClass.swift"; sourceTree = "<group>"; }; + 956CAFB626A320100071A75D /* Company+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Company+CoreDataProperties.swift"; sourceTree = "<group>"; }; + 956CAFB926A321D40071A75D /* ConditionallyWrapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionallyWrapView.swift; sourceTree = "<group>"; }; + 956CAFBB26A322250071A75D /* ContentViewPad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentViewPad.swift; sourceTree = "<group>"; }; + 956CAFBD26A3226B0071A75D /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = "<group>"; }; + 956CAFC526A33BC40071A75D /* UsStatesCoordinates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = UsStatesCoordinates.json; sourceTree = "<group>"; }; + 956CAFC726A33C520071A75D /* UsStatesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsStatesModel.swift; sourceTree = "<group>"; }; + 956CAFC926A33E660071A75D /* MapSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapSection.swift; sourceTree = "<group>"; }; + 956CAFCB26A340B10071A75D /* lazybear.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = lazybear.entitlements; sourceTree = "<group>"; }; + 956CAFCD26A340C60071A75D /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; }; 95F7C15D26A2CCA2002F08DB /* lazybear.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = lazybear.app; sourceTree = BUILT_PRODUCTS_DIR; }; 95F7C16026A2CCA2002F08DB /* lazybearApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = lazybearApp.swift; sourceTree = "<group>"; }; 95F7C16226A2CCA2002F08DB /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; }; @@ -56,6 +98,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 956CAFCE26A340C60071A75D /* CloudKit.framework in Frameworks */, + 956CAF8A26A2D7500071A75D /* Alamofire in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -76,6 +120,56 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 956CAF8226A2D3870071A75D /* Models */ = { + isa = PBXGroup; + children = ( + 956CAFB526A320100071A75D /* Company+CoreDataClass.swift */, + 956CAFB626A320100071A75D /* Company+CoreDataProperties.swift */, + 956CAF8326A2D3940071A75D /* CompanyQuoteModel.swift */, + 956CAFA226A2E1A60071A75D /* SearchModel.swift */, + 956CAFAF26A2F7490071A75D /* CompanyInfoModel.swift */, + 956CAFC726A33C520071A75D /* UsStatesModel.swift */, + ); + path = Models; + sourceTree = "<group>"; + }; + 956CAF8526A2D4300071A75D /* Helpers */ = { + isa = PBXGroup; + children = ( + 956CAF8626A2D43D0071A75D /* WatchlistRow.swift */, + 956CAFA026A2E0690071A75D /* SearchBar.swift */, + 956CAFA426A2E6EC0071A75D /* SearchRow.swift */, + 956CAFC926A33E660071A75D /* MapSection.swift */, + 956CAFB126A2FCF70071A75D /* StockSection.swift */, + 956CAFB326A3004A0071A75D /* AboutSection.swift */, + 956CAFB926A321D40071A75D /* ConditionallyWrapView.swift */, + ); + path = Helpers; + sourceTree = "<group>"; + }; + 956CAFBF26A32D9E0071A75D /* Functions */ = { + isa = PBXGroup; + children = ( + ); + path = Functions; + sourceTree = "<group>"; + }; + 956CAFC426A33BA10071A75D /* Resources */ = { + isa = PBXGroup; + children = ( + 956CAFC526A33BC40071A75D /* UsStatesCoordinates.json */, + ); + path = Resources; + sourceTree = "<group>"; + }; + 956CAFCC26A340C60071A75D /* Frameworks */ = { + isa = PBXGroup; + children = ( + 956CAFCD26A340C60071A75D /* CloudKit.framework */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; 95F7C15426A2CCA2002F08DB = { isa = PBXGroup; children = ( @@ -83,6 +177,7 @@ 95F7C17626A2CCA3002F08DB /* lazybearTests */, 95F7C18126A2CCA3002F08DB /* lazybearUITests */, 95F7C15E26A2CCA2002F08DB /* Products */, + 956CAFCC26A340C60071A75D /* Frameworks */, ); sourceTree = "<group>"; }; @@ -99,12 +194,22 @@ 95F7C15F26A2CCA2002F08DB /* lazybear */ = { isa = PBXGroup; children = ( + 956CAFCB26A340B10071A75D /* lazybear.entitlements */, 95F7C16026A2CCA2002F08DB /* lazybearApp.swift */, 95F7C16226A2CCA2002F08DB /* ContentView.swift */, + 956CAFBB26A322250071A75D /* ContentViewPad.swift */, + 956CAFBD26A3226B0071A75D /* Sidebar.swift */, + 956CAF9826A2DD080071A75D /* Watchlist.swift */, + 956CAF9E26A2DFD30071A75D /* Search.swift */, + 956CAFA626A2F43A0071A75D /* CompanyView.swift */, 95F7C16426A2CCA2002F08DB /* Assets.xcassets */, 95F7C16926A2CCA2002F08DB /* Persistence.swift */, 95F7C16E26A2CCA2002F08DB /* Info.plist */, 95F7C16B26A2CCA2002F08DB /* lazybear.xcdatamodeld */, + 956CAF8526A2D4300071A75D /* Helpers */, + 956CAFBF26A32D9E0071A75D /* Functions */, + 956CAF8226A2D3870071A75D /* Models */, + 956CAFC426A33BA10071A75D /* Resources */, 95F7C16626A2CCA2002F08DB /* Preview Content */, ); path = lazybear; @@ -152,6 +257,9 @@ dependencies = ( ); name = lazybear; + packageProductDependencies = ( + 956CAF8926A2D7500071A75D /* Alamofire */, + ); productName = lazybear; productReference = 95F7C15D26A2CCA2002F08DB /* lazybear.app */; productType = "com.apple.product-type.application"; @@ -223,6 +331,9 @@ Base, ); mainGroup = 95F7C15426A2CCA2002F08DB; + packageReferences = ( + 956CAF8826A2D7500071A75D /* XCRemoteSwiftPackageReference "Alamofire" */, + ); productRefGroup = 95F7C15E26A2CCA2002F08DB /* Products */; projectDirPath = ""; projectRoot = ""; @@ -239,6 +350,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 956CAFC626A33BC40071A75D /* UsStatesCoordinates.json in Resources */, 95F7C16826A2CCA2002F08DB /* Preview Assets.xcassets in Resources */, 95F7C16526A2CCA2002F08DB /* Assets.xcassets in Resources */, ); @@ -265,10 +377,28 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 956CAFB026A2F7490071A75D /* CompanyInfoModel.swift in Sources */, + 956CAFB726A320100071A75D /* Company+CoreDataClass.swift in Sources */, + 956CAFC826A33C520071A75D /* UsStatesModel.swift in Sources */, + 956CAF8726A2D43D0071A75D /* WatchlistRow.swift in Sources */, 95F7C16A26A2CCA2002F08DB /* Persistence.swift in Sources */, + 956CAFBC26A322250071A75D /* ContentViewPad.swift in Sources */, + 956CAFA326A2E1A60071A75D /* SearchModel.swift in Sources */, + 956CAFB826A320100071A75D /* Company+CoreDataProperties.swift in Sources */, + 956CAF8426A2D3940071A75D /* CompanyQuoteModel.swift in Sources */, 95F7C16326A2CCA2002F08DB /* ContentView.swift in Sources */, + 956CAFBA26A321D40071A75D /* ConditionallyWrapView.swift in Sources */, + 956CAFBE26A3226B0071A75D /* Sidebar.swift in Sources */, + 956CAFCA26A33E660071A75D /* MapSection.swift in Sources */, + 956CAFA526A2E6EC0071A75D /* SearchRow.swift in Sources */, 95F7C16126A2CCA2002F08DB /* lazybearApp.swift in Sources */, + 956CAFB226A2FCF70071A75D /* StockSection.swift in Sources */, + 956CAF9F26A2DFD30071A75D /* Search.swift in Sources */, 95F7C16D26A2CCA2002F08DB /* lazybear.xcdatamodeld in Sources */, + 956CAFA726A2F43A0071A75D /* CompanyView.swift in Sources */, + 956CAFA126A2E0690071A75D /* SearchBar.swift in Sources */, + 956CAFB426A3004A0071A75D /* AboutSection.swift in Sources */, + 956CAF9926A2DD090071A75D /* Watchlist.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -425,6 +555,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = lazybear/lazybear.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"lazybear/Preview Content\""; DEVELOPMENT_TEAM = MTX83R5H8X; @@ -447,6 +578,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = lazybear/lazybear.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"lazybear/Preview Content\""; DEVELOPMENT_TEAM = MTX83R5H8X; @@ -589,6 +721,25 @@ }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + 956CAF8826A2D7500071A75D /* XCRemoteSwiftPackageReference "Alamofire" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Alamofire/Alamofire.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.4.3; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 956CAF8926A2D7500071A75D /* Alamofire */ = { + isa = XCSwiftPackageProductDependency; + package = 956CAF8826A2D7500071A75D /* XCRemoteSwiftPackageReference "Alamofire" */; + productName = Alamofire; + }; +/* End XCSwiftPackageProductDependency section */ + /* Begin XCVersionGroup section */ 95F7C16B26A2CCA2002F08DB /* lazybear.xcdatamodeld */ = { isa = XCVersionGroup;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "Alamofire", + "repositoryURL": "https://github.com/Alamofire/Alamofire.git", + "state": { + "branch": null, + "revision": "f96b619bcb2383b43d898402283924b80e2c4bae", + "version": "5.4.3" + } + } + ] + }, + "version": 1 +}
Binary file LazyBear.xcodeproj/project.xcworkspace/xcuserdata/dennis.xcuserdatad/UserInterfaceState.xcuserstate has changed
--- a/LazyBear/Assets.xcassets/AppIcon.appiconset/Contents.json Sat Jul 17 09:33:44 2021 +0100 +++ b/LazyBear/Assets.xcassets/AppIcon.appiconset/Contents.json Sat Jul 17 17:58:57 2021 +0100 @@ -1,91 +1,109 @@ { "images" : [ { + "filename" : "icon_20pt@2x-1.png", "idiom" : "iphone", "scale" : "2x", "size" : "20x20" }, { + "filename" : "icon_20pt@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "20x20" }, { + "filename" : "icon_29pt@2x-1.png", "idiom" : "iphone", "scale" : "2x", "size" : "29x29" }, { + "filename" : "icon_29pt@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "29x29" }, { + "filename" : "icon_40pt@2x-1.png", "idiom" : "iphone", "scale" : "2x", "size" : "40x40" }, { + "filename" : "icon_40pt@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "40x40" }, { + "filename" : "icon_60pt@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "60x60" }, { + "filename" : "icon_60pt@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "60x60" }, { + "filename" : "icon_20pt.png", "idiom" : "ipad", "scale" : "1x", "size" : "20x20" }, { + "filename" : "icon_20pt@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "20x20" }, { + "filename" : "icon_29pt.png", "idiom" : "ipad", "scale" : "1x", "size" : "29x29" }, { + "filename" : "icon_29pt@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "29x29" }, { + "filename" : "icon_40pt.png", "idiom" : "ipad", "scale" : "1x", "size" : "40x40" }, { + "filename" : "icon_40pt@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "40x40" }, { + "filename" : "icon_76pt.png", "idiom" : "ipad", "scale" : "1x", "size" : "76x76" }, { + "filename" : "icon_76pt@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "76x76" }, { + "filename" : "icon_83.5@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" }, { + "filename" : "Icon.png", "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/CompanyView.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,132 @@ +// +// CompanyView.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI +import Alamofire +import MapKit + +struct CompanyView: View { + var symbol: String + @State private var companyInfo = CompanyInfoModel() + @State private var companyQuote = CompanyQuoteModel() + @Environment(\.managedObjectContext) private var viewContext + @FetchRequest(sortDescriptors: [], animation: .default) private var companies: FetchedResults<Company> + + var body: some View { + ScrollView { + VStack { + MapSection(state: companyInfo.state ?? "New York") + + VStack(alignment: .leading) { + Text(companyInfo.symbol ?? "") + .font(.title) + .fontWeight(.semibold) + .foregroundColor(.primary) + + HStack { + Text(companyInfo.companyName ?? "") + Spacer() + Text(companyInfo.state ?? "") + } + .font(.subheadline) + .foregroundColor(.secondary) + + Divider() + StockSection(companyQuote: companyQuote) + .padding(.bottom) + + AboutSection(companyInfo: companyInfo) + + Spacer() + } + .padding() + } + } + .ignoresSafeArea(edges: .top) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + let watchlistSymbols = companies.map { $0.symbol } + if watchlistSymbols.contains(symbol) { + Button(action: deleteCompanyFromWatchlist) { + Image(systemName: "star.fill") + .font(.system(size: 25)) + } + + } else { + Button(action: addCompanyToWatchlist) { + Image(systemName: "star") + .font(.system(size: 25)) + } + } + } + } + .onAppear(perform: requestApi) + } + + // MARK: - REQUEST API + private func requestApi() { + // Request company information + var url = "https://cloud.iexapis.com/stable/stock/\(symbol)/company?token=pk_58fd944e924e4a70acf8635bc335cec4" + + AF.request(url).responseDecodable(of: CompanyInfoModel.self) { response in + if let value = response.value { + self.companyInfo = value + } else { + // Handle error + } + } + + // Request quote + url = "https://cloud.iexapis.com/stable/stock/\(symbol)/quote?token=pk_58fd944e924e4a70acf8635bc335cec4" + AF.request(url).responseDecodable(of: CompanyQuoteModel.self) { response in + if let value = response.value { + self.companyQuote = value + } + else { + // Handle error + } + } + } + + // MARK: - ADD CORE DATA + private func addCompanyToWatchlist() { + withAnimation { + let company = Company(context: viewContext) + company.symbol = symbol + company.companyName = companyQuote.companyName! + + do { + try viewContext.save() + } catch { + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } + } + + // MARK: - DELETE CORE DATA + private func deleteCompanyFromWatchlist() { + withAnimation { + let company = companies.first(where: { $0.symbol == symbol }) + viewContext.delete(company!) + do { + try viewContext.save() + } catch { + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } + } +} + +struct CompanyView_Previews: PreviewProvider { + static var previews: some View { + NavigationView { + CompanyView(symbol: "AAPL") + } + } +}
--- a/LazyBear/ContentView.swift Sat Jul 17 09:33:44 2021 +0100 +++ b/LazyBear/ContentView.swift Sat Jul 17 17:58:57 2021 +0100 @@ -9,72 +9,71 @@ import CoreData struct ContentView: View { + @State private var tab: Tab = .watchlist @Environment(\.managedObjectContext) private var viewContext - - @FetchRequest( - sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)], - animation: .default) - private var items: FetchedResults<Item> - + @FetchRequest(sortDescriptors: []) private var companies: FetchedResults<Company> + var body: some View { - List { - ForEach(items) { item in - Text("Item at \(item.timestamp!, formatter: itemFormatter)") - } - .onDelete(perform: deleteItems) + TabView(selection: $tab) { + Watchlist() + .tabItem { + Image(systemName: "list.star") + Text("Watchlist") + } + .tag(Tab.watchlist) + + Search() + .tabItem { + Image(systemName: "magnifyingglass") + Text("Search") + } + .tag(Tab.search) } - .toolbar { - #if os(iOS) - EditButton() - #endif - - Button(action: addItem) { - Label("Add Item", systemImage: "plus") - } - } + .onAppear(perform: createDefaultWatchlist) + } + + private enum Tab { + case watchlist, search, settings } + + // MARK: - Create default watchlist + private func createDefaultWatchlist() { + if companies.isEmpty { + let defaultCompanies = [ + "AAPL": "Apple Inc", + "AMZN": "Amazon.com Inc.", + "BRK.A": "Berkshire Hathaway Inc.", + "GS": "Goldman Sachs Group, Inc.", + "TSLA": "Tesla Inc", + "MSFT": "Microsoft Corporation", + "WMT": "Walmart Inc", + "DIS": "Walt Disney Co (The)", + "NKE": "Nike, Inc." + ] + + for defaultCompany in defaultCompanies { + withAnimation { + let company = Company(context: viewContext) + company.symbol = defaultCompany.key + company.companyName = defaultCompany.value - private func addItem() { - withAnimation { - let newItem = Item(context: viewContext) - newItem.timestamp = Date() - - do { - try viewContext.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nsError = error as NSError - fatalError("Unresolved error \(nsError), \(nsError.userInfo)") - } - } - } - - private func deleteItems(offsets: IndexSet) { - withAnimation { - offsets.map { items[$0] }.forEach(viewContext.delete) - - do { - try viewContext.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nsError = error as NSError - fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + do { + try viewContext.save() + } catch { + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } } } } } -private let itemFormatter: DateFormatter = { - let formatter = DateFormatter() - formatter.dateStyle = .short - formatter.timeStyle = .medium - return formatter -}() struct ContentView_Previews: PreviewProvider { static var previews: some View { - ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) + ContentView() + .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/ContentViewPad.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,24 @@ +// +// ContentViewPad.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI + +struct ContentViewPad: View { + var body: some View { + NavigationView { + Sidebar() + Watchlist() + CompanyView(symbol: "AAPL") + } + } +} + +struct ContentViewPad_Previews: PreviewProvider { + static var previews: some View { + ContentViewPad() + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Helpers/AboutSection.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,26 @@ +// +// AboutSection.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI + +struct AboutSection: View { + var companyInfo: CompanyInfoModel + + var body: some View { + Text("About") + .font(.title2) + .fontWeight(.semibold) + + Text(companyInfo.description ?? "") + } +} + +struct AboutSection_Previews: PreviewProvider { + static var previews: some View { + AboutSection(companyInfo: CompanyInfoModel(symbol: "AAPL", companyName: "Apple Inc", description: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.", address: "One Apple Park Way", state: "CA", country: "US")) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Helpers/ConditionallyWrapView.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,19 @@ +// +// ConditionallyWrapView.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI + +extension View { + @ViewBuilder + func `if`<Content: View>(_ conditional: Bool, content: (Self) -> Content) -> some View { + if conditional { + content(self) + } else { + self + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Helpers/MapSection.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,51 @@ +// +// MapSection.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI +import MapKit + +struct MapSection: View { + var state: String + + var body: some View { + let states: [UsStatesModel] = parseJson("UsStatesCoordinates.json") + let stateObject = states.first(where: { $0.state == state }) + let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: stateObject?.latitude ?? 40.741895, longitude: stateObject?.longitude ?? -73.989308), span: MKCoordinateSpan(latitudeDelta: 3.5, longitudeDelta: 3.5)) + + Map(coordinateRegion: .constant(region)) + .frame(height: 350) + } + + // MARK: - PARSE JSON + private func parseJson<T: Decodable>(_ filename: String) -> T { + let data: Data + + guard let file = Bundle.main.url(forResource: filename, withExtension: nil) + else { + fatalError("Couldn't find \(filename) in main bundle.") + } + + do { + data = try Data(contentsOf: file) + } catch { + fatalError("Couldn't load \(filename) from main bundle:\n\(error)") + } + + do { + let decoder = JSONDecoder() + return try decoder.decode(T.self, from: data) + } catch { + fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)") + } + } +} + +struct MapSection_Previews: PreviewProvider { + static var previews: some View { + MapSection(state: "California") + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Helpers/SearchBar.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,47 @@ +// +// SearchBar.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI + +struct SearchBar: View { + @Binding var searchText: String + @Binding var searching: Bool + + var body: some View { + ZStack { + Rectangle() + .foregroundColor(Color(.secondarySystemBackground)) + + HStack { + Image(systemName: "magnifyingglass") + TextField("Search ..", text: $searchText) { startedEditing in + if startedEditing { + withAnimation { + searching = true + } + } + } + onCommit: { + withAnimation { + searching = false + } + } + } + .padding(.leading, 13) + + } + .frame(height: 40) + .cornerRadius(13) + .padding() + } +} + +struct SearchBar_Previews: PreviewProvider { + static var previews: some View { + SearchBar(searchText: .constant(""), searching: .constant(false)) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Helpers/SearchRow.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,34 @@ +// +// SearchRow.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI + +struct SearchRow: View { + var company: SearchModel + + var body: some View { + HStack { + VStack(alignment: .leading) { + Text("\(company.symbol!)") + .font(.headline) + + Text("\(company.securityName!)") + .font(.subheadline) + .opacity(0.5) + .lineLimit(1) + } + + Spacer() + } + } +} + +struct SearchRow_Previews: PreviewProvider { + static var previews: some View { + SearchRow(company: SearchModel(symbol: "AAPL", securityName: "Apple Inc")) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Helpers/StockSection.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,36 @@ +// +// StockSection.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI + +struct StockSection: View { + var companyQuote: CompanyQuoteModel + + var body: some View { + VStack(alignment: .leading) { + Text("Stock") + .font(.title2) + .fontWeight(.semibold) + + Text("\(companyQuote.companyName ?? "Company") is currently trading at \(companyQuote.latestPrice ?? 0, specifier: "%.2f"). That's a price change of \((companyQuote.change ?? 0.0), specifier: "%.2f") or a \((companyQuote.changePercent ?? 0)*100, specifier: "%.2f")% \(generateTendency()) since yesterday.") + } + } + + private func generateTendency() -> String { + if companyQuote.change ?? 0 >= 0 { + return "up" + } else { + return "down" + } + } +} + +struct StockSection_Previews: PreviewProvider { + static var previews: some View { + StockSection(companyQuote: CompanyQuoteModel(symbol: "AAPL", companyName: "Apple Inc", latestPrice: 120.30, changePercent: 0.03, change: 1.50)) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Helpers/WatchlistRow.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,70 @@ +// +// WatchlistRow.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI +import Alamofire + +struct WatchlistRow: View { + var symbol: String + @State private var companyQuote = CompanyQuoteModel() + @State private var showingRow = false + + var body: some View { + HStack { + if showingRow { + VStack(alignment: .leading) { + Text("\(symbol)") + .font(.headline) + + Text("\(companyQuote.companyName!)") + .font(.subheadline) + .foregroundColor(.secondary) + } + + Spacer() + VStack(alignment: .trailing) { + Text("\(companyQuote.latestPrice!, specifier: "%.2f")") + .font(.headline) + + Text("\(companyQuote.change!, specifier: "%.2f") \(companyQuote.changePercent!*100, specifier: "%.2f")%") + .font(.subheadline) + .foregroundColor(generateColour()) + } + } else { + ProgressView() + } + } + .onAppear(perform: requestApi) + } + + private func generateColour() -> Color { + if companyQuote.change! >= 0 { + return Color(.systemGreen) + } else { + return Color(.systemRed) + } + } + + private func requestApi() { + let url = "https://cloud.iexapis.com/stable/stock/\(symbol)/quote?token=pk_58fd944e924e4a70acf8635bc335cec4" + AF.request(url).responseDecodable(of: CompanyQuoteModel.self) { response in + if let value = response.value { + self.companyQuote = value + self.showingRow = true + } + else { + // Handle error + } + } + } +} + +struct WatchlistRow_Previews: PreviewProvider { + static var previews: some View { + WatchlistRow(symbol: "AAPL") + } +}
--- a/LazyBear/Info.plist Sat Jul 17 09:33:44 2021 +0100 +++ b/LazyBear/Info.plist Sat Jul 17 17:58:57 2021 +0100 @@ -29,6 +29,10 @@ </dict> <key>UIApplicationSupportsIndirectInputEvents</key> <true/> + <key>UIBackgroundModes</key> + <array> + <string>remote-notification</string> + </array> <key>UILaunchScreen</key> <dict/> <key>UIRequiredDeviceCapabilities</key>
--- a/LazyBear/LazyBear.xcdatamodeld/LazyBear.xcdatamodel/contents Sat Jul 17 09:33:44 2021 +0100 +++ b/LazyBear/LazyBear.xcdatamodeld/LazyBear.xcdatamodel/contents Sat Jul 17 17:58:57 2021 +0100 @@ -1,9 +1,14 @@ <?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1" systemVersion="11A491" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="true" userDefinedModelVersionIdentifier=""> +<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="18154" systemVersion="20F71" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier=""> + <entity name="Company" representedClassName="Company" syncable="YES"> + <attribute name="companyName" optional="YES" attributeType="String"/> + <attribute name="symbol" optional="YES" attributeType="String"/> + </entity> <entity name="Item" representedClassName="Item" syncable="YES" codeGenerationType="class"> <attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/> </entity> <elements> <element name="Item" positionX="-63" positionY="-18" width="128" height="44"/> + <element name="Company" positionX="-63" positionY="-9" width="128" height="59"/> </elements> </model> \ No newline at end of file
--- a/LazyBear/LazyBearApp.swift Sat Jul 17 09:33:44 2021 +0100 +++ b/LazyBear/LazyBearApp.swift Sat Jul 17 17:58:57 2021 +0100 @@ -13,8 +13,13 @@ var body: some Scene { WindowGroup { - ContentView() - .environment(\.managedObjectContext, persistenceController.container.viewContext) + if UIDevice.current.userInterfaceIdiom == .pad { + ContentViewPad() + .environment(\.managedObjectContext, persistenceController.container.viewContext) + } else { + ContentView() + .environment(\.managedObjectContext, persistenceController.container.viewContext) + } } } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Models/Company+CoreDataClass.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,15 @@ +// +// Company+CoreDataClass.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// +// + +import Foundation +import CoreData + +@objc(Company) +public class Company: NSManagedObject { + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Models/Company+CoreDataProperties.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,26 @@ +// +// Company+CoreDataProperties.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// +// + +import Foundation +import CoreData + + +extension Company { + + @nonobjc public class func fetchRequest() -> NSFetchRequest<Company> { + return NSFetchRequest<Company>(entityName: "Company") + } + + @NSManaged public var symbol: String + @NSManaged public var companyName: String + +} + +extension Company : Identifiable { + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Models/CompanyInfoModel.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,17 @@ +// +// CompanyInfoModel.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI + +struct CompanyInfoModel: Codable { + var symbol: String? + var companyName: String? + var description: String? + var address: String? + var state: String? + var country: String? +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Models/CompanyQuoteModel.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,16 @@ +// +// CompanyQuoteModel.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI + +struct CompanyQuoteModel: Codable { + var symbol: String? + var companyName: String? + var latestPrice: Float? + var changePercent: Float? + var change: Float? +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Models/SearchModel.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,13 @@ +// +// SearchModel.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI + +struct SearchModel: Codable, Hashable { + var symbol: String? + var securityName: String? +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Models/UsStatesModel.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,15 @@ +// +// UsStatesModel.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI +import MapKit + +struct UsStatesModel: Codable { + var state: String + var latitude: CLLocationDegrees + var longitude: CLLocationDegrees +}
--- a/LazyBear/Persistence.swift Sat Jul 17 09:33:44 2021 +0100 +++ b/LazyBear/Persistence.swift Sat Jul 17 17:58:57 2021 +0100 @@ -17,6 +17,12 @@ let newItem = Item(context: viewContext) newItem.timestamp = Date() } + + for _ in 0..<10 { + let company = Company(context: viewContext) + company.symbol = "AAPL" + company.companyName = "Apple Inc" + } do { try viewContext.save() } catch {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Resources/UsStatesCoordinates.json Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,252 @@ +[ + { + "state":"Alaska", + "latitude":61.3850, + "longitude":-152.2683 + }, + { + "state":"Alabama", + "latitude":32.7990, + "longitude":-86.8073 + }, + { + "state":"Arkansas", + "latitude":34.9513, + "longitude":-92.3809 + }, + { + "state":"Arizona", + "latitude":33.7712, + "longitude":-111.3877 + }, + { + "state":"California", + "latitude":36.1700, + "longitude":-119.7462 + }, + { + "state":"Colorado", + "latitude":39.0646, + "longitude":-105.3272 + }, + { + "state":"Connecticut", + "latitude":41.5834, + "longitude":-72.7622 + }, + { + "state":"Delaware", + "latitude":39.3498, + "longitude":-75.5148 + }, + { + "state":"Florida", + "latitude":27.8333, + "longitude":-81.7170 + }, + { + "state":"Georgia", + "latitude":32.9866, + "longitude":-83.6487 + }, + { + "state":"Hawaii", + "latitude":21.1098, + "longitude":-157.5311 + }, + { + "state":"Iowa", + "latitude":42.0046, + "longitude":-93.2140 + }, + { + "state":"Idaho", + "latitude":44.2394, + "longitude":-114.5103 + }, + { + "state":"Illinois", + "latitude":40.3363, + "longitude":-89.0022 + }, + { + "state":"Indiana", + "latitude":39.8647, + "longitude":-86.2604 + }, + { + "state":"Kansas", + "latitude":38.5111, + "longitude":-96.8005 + }, + { + "state":"Kentucky", + "latitude":37.6690, + "longitude":-84.6514 + }, + { + "state":"Louisiana", + "latitude":31.1801, + "longitude":-91.8749 + }, + { + "state":"Massachusetts", + "latitude":42.2373, + "longitude":-71.5314 + }, + { + "state":"Maryland", + "latitude":39.0724, + "longitude":-76.7902 + }, + { + "state":"Maine", + "latitude":44.6074, + "longitude":-69.3977 + }, + { + "state":"Michigan", + "latitude":43.3504, + "longitude":-84.5603 + }, + { + "state":"Minnesota", + "latitude":45.7326, + "longitude":-93.9196 + }, + { + "state":"Missouri", + "latitude":38.4623, + "longitude":-92.3020 + }, + { + "state":"Mississippi", + "latitude":32.7673, + "longitude":-89.6812 + }, + { + "state":"Montana", + "latitude":46.9048, + "longitude":-110.3261 + }, + { + "state":"North Carolina", + "latitude":35.6411, + "longitude":-79.8431 + }, + { + "state":"North Dakota", + "latitude":47.5362, + "longitude":-99.7930 + }, + { + "state":"Nebraska", + "latitude":41.1289, + "longitude":-98.2883 + }, + { + "state":"New Hampshire", + "latitude":43.4108, + "longitude":-71.5653 + }, + { + "state":"New Jersey", + "latitude":40.3140, + "longitude":-74.5089 + }, + { + "state":"New Mexico", + "latitude":34.8375, + "longitude":-106.2371 + }, + { + "state":"Nevada", + "latitude":38.4199, + "longitude":-117.1219 + }, + { + "state":"New York", + "latitude":42.1497, + "longitude":-74.9384 + }, + { + "state":"Ohio", + "latitude":40.3736, + "longitude":-82.7755 + }, + { + "state":"Oklahoma", + "latitude":35.5376, + "longitude":-96.9247 + }, + { + "state":"Oregon", + "latitude":44.5672, + "longitude":-122.1269 + }, + { + "state":"Pennsylvania", + "latitude":40.5773, + "longitude":-77.2640 + }, + { + "state":"Rhode Island", + "latitude":41.6772, + "longitude":-71.5101 + }, + { + "state":"South Carolina", + "latitude":33.8191, + "longitude":-80.9066 + }, + { + "state":"South Dakota", + "latitude":44.2853, + "longitude":-99.4632 + }, + { + "state":"Tennessee", + "latitude":35.7449, + "longitude":-86.7489 + }, + { + "state":"Texas", + "latitude":31.1060, + "longitude":-97.6475 + }, + { + "state":"Utah", + "latitude":40.1135, + "longitude":-111.8535 + }, + { + "state":"Virginia", + "latitude":37.7680, + "longitude":-78.2057 + }, + { + "state":"Vermont", + "latitude":44.0407, + "longitude":-72.7093 + }, + { + "state":"Washington", + "latitude":47.3917, + "longitude":-121.5708 + }, + { + "state":"Wisconsin", + "latitude":44.2563, + "longitude":-89.6385 + }, + { + "state":"West Virginia", + "latitude":38.4680, + "longitude":-80.9696 + }, + { + "state":"Wyoming", + "latitude":42.7475, + "longitude":-107.2085 + } +]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Search.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,77 @@ +// +// Search.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI +import Alamofire + +struct Search: View { + @State var searchCompany = "" + @State var searching = false + @State private var companies: [SearchModel] = [] + + var body: some View { + VStack { + SearchBar(searchText: $searchCompany, searching: $searching) + .onChange(of: searchCompany, perform: { _ in + requestApi() + }) + Spacer() + if companies.isEmpty { + + } else { + List(companies, id: \.self) { company in + NavigationLink(destination: CompanyView(symbol: company.symbol!)) { + SearchRow(company: company) + } + } + .id(UUID()) + .listStyle(InsetListStyle()) + .gesture(DragGesture() + .onChanged({ _ in + UIApplication.shared.dismissKeyboard() + }) + ) + } + } + .navigationTitle("Search") + .toolbar { + if searching { + Button("Cancel") { searchCompany = ""; companies = [] + withAnimation { + searching = false + UIApplication.shared.dismissKeyboard() + } + } + } + } + .if(UIDevice.current.userInterfaceIdiom == .phone) { content in + NavigationView { content } + } + } + + private func requestApi() { + let url = "https://cloud.iexapis.com/stable/search/\(searchCompany)?token=pk_58fd944e924e4a70acf8635bc335cec4" + AF.request(url).responseDecodable(of: [SearchModel].self) { response in + if let value = response.value { + self.companies = value + } else { + // Handle error + } + } + } +} +extension UIApplication { + func dismissKeyboard() { + sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + } +} + +struct Search_Previews: PreviewProvider { + static var previews: some View { + Search() + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Sidebar.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,29 @@ +// +// Sidebar.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI + +struct Sidebar: View { + var body: some View { + List { + NavigationLink(destination: Watchlist()) { + Label("Watchlist", systemImage: "list.star") + } + NavigationLink(destination: Search()) { + Label("Search", systemImage: "magnifyingglass") + } + } + .navigationTitle("Categories") + .listStyle(SidebarListStyle()) + } +} + +struct Sidebar_Previews: PreviewProvider { + static var previews: some View { + Sidebar() + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/Watchlist.swift Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,57 @@ +// +// Watchlist.swift +// lazybear +// +// Created by Dennis Concepción Martín on 17/07/2021. +// + +import SwiftUI +import CoreData + +struct Watchlist: View { + @Environment(\.managedObjectContext) private var viewContext + @FetchRequest( + sortDescriptors: [NSSortDescriptor(keyPath: \Company.symbol, ascending: true)], + animation: .default) + private var companies: FetchedResults<Company> + + var body: some View { + List { + ForEach(companies) { company in + NavigationLink(destination: CompanyView(symbol: company.symbol)) { + WatchlistRow(symbol: company.symbol) + } + } + .onDelete(perform: deleteCompanyFromWatchlist) + } + .navigationTitle("Companies") + .toolbar { + #if os(iOS) + EditButton() + #endif + } + .if(UIDevice.current.userInterfaceIdiom == .phone) { content in + NavigationView { content } + } + } + + private func deleteCompanyFromWatchlist(offsets: IndexSet) { + withAnimation { + offsets.map { companies[$0] }.forEach(viewContext.delete) + + do { + try viewContext.save() + } catch { + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } + } +} + +struct Watchlist_Previews: PreviewProvider { + static var previews: some View { + Watchlist() + .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LazyBear/lazybear.entitlements Sat Jul 17 17:58:57 2021 +0100 @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>aps-environment</key> + <string>development</string> + <key>com.apple.developer.icloud-container-identifiers</key> + <array/> + <key>com.apple.developer.icloud-services</key> + <array> + <string>CloudKit</string> + </array> +</dict> +</plist>