├── ModerneShopping ├── Assets.xcassets │ ├── Contents.json │ ├── darkText.colorset │ │ └── Contents.json │ ├── tertiary.colorset │ │ └── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── Background.colorset │ │ └── Contents.json │ ├── SecondaryBackground.colorset │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── ModerneShoppingApp.swift ├── ViewModel │ ├── FakeShoppingTests.xctestplan │ ├── ProductListViewModel.swift │ ├── CartViewModel.swift │ ├── UserViewModel.swift │ └── APIServices.swift ├── Utilities │ ├── ProfilButtonMenu.swift │ ├── ShoppingButtonStyle.swift │ └── CustomPicker.swift ├── Views │ ├── CartViews │ │ ├── FillAndStrokeShape.swift │ │ ├── CartLoadingView.swift │ │ ├── LottieFiles │ │ │ ├── CartLoading.swift │ │ │ └── cartloading.json │ │ ├── CartListView.swift │ │ ├── CartListItem.swift │ │ ├── CartView.swift │ │ └── CheckOutView.swift │ ├── ProductViews │ │ ├── LottieFiles │ │ │ ├── ProductLoading.swift │ │ │ └── productLoading.json │ │ ├── LoadingView.swift │ │ ├── ProductList.swift │ │ ├── ProductCarousel.swift │ │ ├── ProductListItem.swift │ │ ├── ProductCarouselCard.swift │ │ └── ProductView.swift │ ├── ProfilViews │ │ ├── LottieFile │ │ │ └── LoginLottieView.swift │ │ ├── ProfilButtons.swift │ │ ├── LoggedInView.swift │ │ └── LoginView.swift │ ├── ProfilView.swift │ ├── MainView.swift │ └── HomeView.swift ├── Extensions │ ├── Extension+Colors.swift │ ├── Bundle+LoadAndDecode.swift │ └── ImageLoader.swift ├── Model │ ├── Product.swift │ └── User.swift ├── Info.plist ├── api.json └── products.json ├── .gitignore ├── ModerneShopping.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── djallilelkebir.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj ├── ModerneShoppingTests ├── UserAPITest.swift ├── MockAPIServices.swift ├── ProductListObjectTest.swift ├── Info.plist └── ModerneShoppingTests.swift ├── ModerneShoppingUITests ├── Info.plist └── ModerneShoppingUITests.swift ├── LICENSE.md └── README.md /ModerneShopping/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ModerneShopping/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /ModerneShopping.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ModerneShopping.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ModerneShopping/ModerneShoppingApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModerneShoppingApp.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-01. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct ModerneShoppingApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | MainView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ModerneShopping/ViewModel/FakeShoppingTests.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "B354EA94-D972-4129-A959-01943F96159A", 5 | "name" : "Configuration 1", 6 | "options" : { 7 | 8 | } 9 | } 10 | ], 11 | "defaultOptions" : { 12 | 13 | }, 14 | "testTargets" : [ 15 | 16 | ], 17 | "version" : 1 18 | } 19 | -------------------------------------------------------------------------------- /ModerneShopping.xcodeproj/xcuserdata/djallilelkebir.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ModerneShopping.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ModerneShoppingTests/UserAPITest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserAPITest.swift 3 | // ModerneShoppingTests 4 | // 5 | // Created by Djallil Elkebir on 2021-09-12. 6 | // 7 | 8 | import XCTest 9 | @testable import ModerneShopping 10 | class UserAPITest: XCTestCase { 11 | 12 | var mockUsers: MockAPIServices! 13 | var users: UserViewModel! 14 | 15 | 16 | override func setUp() { 17 | mockUsers = MockAPIServices() 18 | users = .init(userServices: mockUsers) 19 | } 20 | 21 | func testLoadingUser(){ 22 | } 23 | 24 | override func tearDown() { 25 | 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /ModerneShopping/Utilities/ProfilButtonMenu.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfilButtonMenu.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-06. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// Styling to the button in the profil view 11 | struct ProfilButtonMenu: ButtonStyle { 12 | func makeBody(configuration: Self.Configuration) -> some View { 13 | configuration.label 14 | .font(.headline) 15 | .padding() 16 | .background(configuration.isPressed ? Color.tertiary : Color.secondaryBackground) 17 | .cornerRadius(12) 18 | .shadow(color: .accentColor.opacity(0.1), radius: 2, x: 0.5, y: 1) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ModerneShoppingTests/MockAPIServices.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockProductList.swift 3 | // ModerneShoppingTests 4 | // 5 | // Created by Djallil Elkebir on 2021-09-10. 6 | // 7 | 8 | import Foundation 9 | @testable import ModerneShopping 10 | 11 | final class MockAPIServices: APIServicesProtocol { 12 | private let apiCall = URLSession.shared 13 | func fetchProducts(from endpoint: ProductListEndpoint, completion: @escaping (Result<[Product], APICallError>) -> ()) { 14 | completion(.success(Product.sampleProducts)) 15 | } 16 | func fetchUser(completion: @escaping (Result) -> ()) { 17 | completion(.success(UserAPIResults.sampleUsers)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ModerneShopping/Views/CartViews/FillAndStrokeShape.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FillAndStrokeShape.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-11. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FillAndStrokeShape: View { 11 | var body: some View { 12 | strokeAndFilledCircle(strokeColor: Color.blue, lineWidth: 10, fillColor: Color.red) 13 | } 14 | 15 | func strokeAndFilledCircle(strokeColor: Color,lineWidth: CGFloat, fillColor: Color)-> some View{ 16 | let circle = Circle() 17 | 18 | return 19 | ZStack{ 20 | circle.fill(fillColor) 21 | circle.stroke(lineWidth: lineWidth) 22 | .fill(strokeColor) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ModerneShoppingTests/ProductListObjectTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductViewModelTests.swift 3 | // ModerneShoppingTests 4 | // 5 | // Created by Djallil Elkebir on 2021-09-10. 6 | // 7 | 8 | import XCTest 9 | @testable import ModerneShopping 10 | 11 | class ProductListObjectTest: XCTestCase { 12 | var mockProductListObject: MockAPIServices! 13 | var product: ProductsListObject! 14 | 15 | 16 | override func setUp() { 17 | mockProductListObject = MockAPIServices() 18 | product = .init(productServices: mockProductListObject) 19 | } 20 | 21 | func testLoadingTwentyProducts(){ 22 | product.products = Product.sampleProducts 23 | XCTAssert(product.products != nil) 24 | } 25 | 26 | override func tearDown() { 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ModerneShopping/Views/CartViews/CartLoadingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CartLoadingView.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-06. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CartLoadingView: View { 11 | var body: some View { 12 | HStack{ 13 | Spacer() 14 | VStack { 15 | CartLoading() 16 | .frame(width: 200, height: 200, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) 17 | Text("Your cart is empty").font(.headline) 18 | } 19 | Spacer() 20 | } 21 | } 22 | } 23 | 24 | struct CartLoadingView_Previews: PreviewProvider { 25 | static var previews: some View { 26 | LoadingView(isLoading: true, error: nil, retryAction: nil) 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /ModerneShoppingTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ModerneShoppingUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ModerneShopping/Assets.xcassets/darkText.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x29", 9 | "green" : "0x25", 10 | "red" : "0x21" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0xFA", 27 | "green" : "0xF9", 28 | "red" : "0xF8" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ModerneShopping/Assets.xcassets/tertiary.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFA", 9 | "green" : "0xF9", 10 | "red" : "0xF8" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x29", 27 | "green" : "0x25", 28 | "red" : "0x21" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ModerneShopping/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x29", 9 | "green" : "0x25", 10 | "red" : "0x21" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x29", 27 | "green" : "0x25", 28 | "red" : "0x21" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ModerneShopping/Assets.xcassets/Background.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFA", 9 | "green" : "0xF9", 10 | "red" : "0xF8" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x29", 27 | "green" : "0x25", 28 | "red" : "0x21" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ModerneShopping/Assets.xcassets/SecondaryBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xDA", 9 | "green" : "0xD4", 10 | "red" : "0xCE" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x7D", 27 | "green" : "0x75", 28 | "red" : "0x6C" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ModerneShopping/Extensions/Extension+Colors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension+Colors.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-03. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension Color { 11 | static let background: Color = Color("Background") 12 | static let secondaryBackground: Color = Color("SecondaryBackground") 13 | static let tertiary: Color = Color("tertiary") 14 | static let darkText: Color = Color("darkText") 15 | 16 | /// Change color of the border shadow depending on the user when he click sign in on LoginView 17 | /// - Parameter condition: an optional bool that will affect the color that is returned 18 | /// - Returns: a Color 19 | static func borderColor(condition: Bool?)-> Color{ 20 | switch condition { 21 | case .some(true): 22 | return Color.green.opacity(0.8) 23 | case .some(false): 24 | return Color.red.opacity(0.8) 25 | case .none: 26 | return Color.darkText.opacity(0.2) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ModerneShoppingTests/ModerneShoppingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModerneShoppingTests.swift 3 | // ModerneShoppingTests 4 | // 5 | // Created by Djallil Elkebir on 2021-09-10. 6 | // 7 | 8 | import XCTest 9 | @testable import ModerneShopping 10 | class ModerneShoppingTests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | } 15 | 16 | override func tearDownWithError() throws { 17 | // Put teardown code here. This method is called after the invocation of each test method in the class. 18 | } 19 | 20 | func testExample() throws { 21 | // This is an example of a functional test case. 22 | // Use XCTAssert and related functions to verify your tests produce the correct results. 23 | } 24 | 25 | func testPerformanceExample() throws { 26 | // This is an example of a performance test case. 27 | measure { 28 | // Put the code you want to measure the time of here. 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /ModerneShopping/Views/CartViews/LottieFiles/CartLoading.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CartLoading.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-06. 6 | // 7 | 8 | import SwiftUI 9 | import Lottie 10 | 11 | struct CartLoading: UIViewRepresentable { 12 | 13 | var animationView = AnimationView() 14 | 15 | func makeUIView(context: UIViewRepresentableContext) -> UIView { 16 | let view = UIView(frame: .zero) 17 | 18 | animationView.animation = Animation.named("cartloading") 19 | animationView.contentMode = .scaleAspectFit 20 | animationView.loopMode = .loop 21 | animationView.play() 22 | animationView.translatesAutoresizingMaskIntoConstraints = false 23 | view.addSubview(animationView) 24 | NSLayoutConstraint.activate([ animationView.heightAnchor.constraint(equalTo: view.heightAnchor), animationView.widthAnchor.constraint(equalTo: view.widthAnchor)]) 25 | return view 26 | } 27 | func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext) { 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Djallil14 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # SwiftUI-FakeShopping-App 6 | A shopping app fully made in SwiftUI for iOS 14.1. 7 | 8 | The app is fully made in SwiftUI, it uses [Fake-Store-API](https://fakestoreapi.com/) to get the products with a custom category picker. 9 | When logged in, a user is randomly generated by [Random User Generator](https://randomuser.me/) 10 | 11 | the black and white theme is made because the product API returns a jpg image with a white background, i adapted the design to that. 12 | 13 | # Screens 14 | ![FirstImage](https://user-images.githubusercontent.com/82174673/133019472-8c9fed6d-f4d3-408a-ae4d-4878f35c8ac9.png) 15 | ![SecondImage](https://user-images.githubusercontent.com/82174673/133019476-2d09b367-b317-4540-9ed2-86565ac725ed.png) 16 | # Recording 17 | https://user-images.githubusercontent.com/82174673/133020940-5d3157bd-8db9-48da-84a4-7f30b184839c.mov 18 | 19 | # Lottie files credit: 20 | [Luis Eduardo Andrade](https://lottiefiles.com/user/198076) 21 | [Machiel Schippers](https://lottiefiles.com/74576-loading) 22 | [Porhour LY ](https://lottiefiles.com/71390-shopping-cart-loader) 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ModerneShopping/Views/ProductViews/LottieFiles/ProductLoading.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductLoading.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-06. 6 | // 7 | 8 | import SwiftUI 9 | import Lottie 10 | 11 | struct ProductLoading: UIViewRepresentable { 12 | 13 | var animationView = AnimationView() 14 | 15 | func makeUIView(context: UIViewRepresentableContext) -> UIView { 16 | let view = UIView(frame: .zero) 17 | 18 | animationView.animation = Animation.named("productLoading") 19 | animationView.contentMode = .scaleAspectFit 20 | animationView.loopMode = .loop 21 | animationView.play() 22 | animationView.translatesAutoresizingMaskIntoConstraints = false 23 | view.addSubview(animationView) 24 | NSLayoutConstraint.activate([ animationView.heightAnchor.constraint(equalTo: view.heightAnchor), animationView.widthAnchor.constraint(equalTo: view.widthAnchor)]) 25 | return view 26 | } 27 | func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext) { 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ModerneShopping/Views/ProfilViews/LottieFile/LoginLottieView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginLottieView.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-08. 6 | // 7 | 8 | import SwiftUI 9 | import Lottie 10 | 11 | struct LoginLottieView: UIViewRepresentable { 12 | 13 | var animationView = AnimationView() 14 | 15 | func makeUIView(context: UIViewRepresentableContext) -> UIView { 16 | let view = UIView(frame: .zero) 17 | 18 | animationView.animation = Animation.named("shoppingCart") 19 | animationView.contentMode = .scaleAspectFit 20 | animationView.loopMode = .loop 21 | animationView.play() 22 | animationView.translatesAutoresizingMaskIntoConstraints = false 23 | view.addSubview(animationView) 24 | NSLayoutConstraint.activate([ animationView.heightAnchor.constraint(equalTo: view.heightAnchor), animationView.widthAnchor.constraint(equalTo: view.widthAnchor)]) 25 | return view 26 | } 27 | func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext) { 28 | 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /ModerneShopping/Extensions/Bundle+LoadAndDecode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+LoadAndDecode.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-01. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Bundle { 11 | /// Load and decode a JSON File 12 | /// - Parameter filename: file name in the bundle 13 | /// - Throws: you got to catch the error :) 14 | /// - Returns: a decoded object 15 | func loadAndDecodeJSON(filename: String) throws -> D? { 16 | guard let url = url(forResource: filename, withExtension: ".json") else { 17 | return nil 18 | } 19 | let data = try Data(contentsOf: url) 20 | let jsonDecoder = JSONDecoder() 21 | let decodedData = try jsonDecoder.decode(D.self, from: data) 22 | return decodedData 23 | } 24 | } 25 | 26 | extension Double { 27 | /// format a double base on objective-c formating 28 | /// - Parameter f: ".2" to return only the 2 number etc 29 | /// - Returns: a trimmed double in stringly typed 30 | func format(f: String) -> String { 31 | return String(format: "%\(f)f", self) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ModerneShopping/Views/ProfilView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfilView.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-05. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ProfilView: View { 11 | @EnvironmentObject var userVM: UserViewModel 12 | @StateObject var imageLoader = ImageLoader() 13 | var body: some View { 14 | ZStack { 15 | VStack{ 16 | if let user = userVM.user{ 17 | LoggedInView(user: user.results[0]) 18 | .environmentObject(userVM) 19 | } else { 20 | LoginView().environmentObject(userVM) 21 | } 22 | } 23 | if userVM.isLoading{ 24 | ZStack{ 25 | Color.background.edgesIgnoringSafeArea(.all) 26 | ProductLoading() 27 | .frame(width: 80, height: 80, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) 28 | } 29 | } 30 | } 31 | } 32 | } 33 | 34 | struct ProfilView_Previews: PreviewProvider { 35 | static var previews: some View { 36 | ProfilView() 37 | .environmentObject(UserViewModel()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ModerneShopping/Views/MainView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainView.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-02. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MainView: View { 11 | @StateObject var products = ProductsListObject() 12 | @StateObject var cartItems = CartViewModel() 13 | @StateObject var user = UserViewModel() 14 | var body: some View { 15 | TabView{ 16 | HomeView(productsList: products, user: user).environmentObject(cartItems) 17 | .tabItem { 18 | Image(systemName:"house") 19 | Text("Home") 20 | } 21 | CartView(cartProducts: cartItems) 22 | .environmentObject(user) 23 | .tabItem { 24 | Image(systemName: "cart") 25 | Text("Cart") 26 | } 27 | ProfilView() 28 | .environmentObject(user) 29 | .tabItem { 30 | Image(systemName: "person") 31 | Text("Profil") 32 | } 33 | } 34 | .zIndex(10) 35 | } 36 | } 37 | 38 | struct MainView_Previews: PreviewProvider { 39 | static var previews: some View { 40 | MainView() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ModerneShopping/Views/ProductViews/LoadingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingView.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-02. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LoadingView: View { 11 | let isLoading: Bool 12 | let error: NSError? 13 | let retryAction: (() -> ())? 14 | var body: some View { 15 | Group { 16 | if isLoading{ 17 | VStack { 18 | Spacer() 19 | HStack{ 20 | Spacer() 21 | ProductLoading() 22 | .frame(width: 80, height: 80, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) 23 | Spacer() 24 | } 25 | Spacer() 26 | } 27 | } else if error != nil { 28 | Text("Can't load the products") 29 | if retryAction != nil { 30 | Button(action: retryAction!){ 31 | Text("retry") 32 | } 33 | } 34 | } else { 35 | EmptyView() 36 | } 37 | } 38 | } 39 | } 40 | 41 | struct LoadingView_Previews: PreviewProvider { 42 | static var previews: some View { 43 | LoadingView(isLoading: true, error: nil, retryAction: nil) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ModerneShoppingUITests/ModerneShoppingUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModerneShoppingUITests.swift 3 | // ModerneShoppingUITests 4 | // 5 | // Created by Djallil Elkebir on 2021-09-10. 6 | // 7 | 8 | import XCTest 9 | 10 | class ModerneShoppingUITests: XCTestCase { 11 | 12 | override func setUp() { 13 | continueAfterFailure = false 14 | } 15 | 16 | func testBasicUI() throws { 17 | // UI tests must launch the application that they test. 18 | let app = XCUIApplication() 19 | app.launch() 20 | XCTAssertEqual(app.tabBars.buttons.count, 3) 21 | 22 | } 23 | func testCartBadgeAddingItemToCart(){ 24 | let app = XCUIApplication() 25 | app.launch() 26 | let addCartButton = app.buttons["Add to cart1"] 27 | let cartNavigationTrailingItem = app.navigationBars.buttons["trailingNavigationBarItem"] 28 | XCTAssert(cartNavigationTrailingItem.exists) 29 | XCTAssertTrue(addCartButton.waitForExistence(timeout: 2)) 30 | addCartButton.tap() 31 | } 32 | 33 | func testLaunchPerformance() throws { 34 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { 35 | // This measures how long it takes to launch your application. 36 | measure(metrics: [XCTApplicationLaunchMetric()]) { 37 | XCUIApplication().launch() 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ModerneShopping/Model/Product.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShoppingItem.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-01. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Product: Identifiable, Codable, Hashable { 11 | var id: Int 12 | var title: String 13 | var price: Double 14 | var description: String 15 | var category: String 16 | var image: String 17 | var rating: Rating 18 | } 19 | extension Product { 20 | var imageURL: URL { 21 | URL(string: image)! 22 | } 23 | var formatedRating: String { 24 | var result = "" 25 | for _ in 0...Int(rating.rate){ 26 | result.append("★") 27 | } 28 | while result.count<5{ 29 | result += "☆" 30 | } 31 | return result 32 | } 33 | } 34 | 35 | struct Rating: Codable, Hashable { 36 | let rate: Double 37 | // to remplace with let count when the api bug is fixed https://github.com/keikaavousi/fake-store-api/issues/31 38 | // let count: Int 39 | let manualCount: Int = Int.random(in: 0...500) 40 | } 41 | 42 | extension Product { 43 | static var sampleProducts: [Product] { 44 | let response: [Product]? = try? Bundle.main.loadAndDecodeJSON(filename: "products") 45 | return response ?? [Product(id: 1, title: "noproduct", price: 10.5, description: "noproduct", category: "noproduct", image: "noproduct", rating: Rating(rate: 10.0))] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ModerneShopping/Model/User.swift: -------------------------------------------------------------------------------- 1 | // 2 | // User.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-05. 6 | // 7 | 8 | import Foundation 9 | struct UserAPIResults: Codable { 10 | var results: [User] 11 | var info: APIInfo 12 | } 13 | struct User: Codable { 14 | var gender: String 15 | var name: Name 16 | var location: Location 17 | var email: String 18 | var login: Login 19 | var picture: Picture 20 | } 21 | struct Name: Codable { 22 | var title: String 23 | var first: String 24 | var last: String 25 | } 26 | 27 | struct Location: Codable { 28 | var city: String 29 | var state: String 30 | var coordinates: Coordinate 31 | 32 | } 33 | struct Coordinate: Codable { 34 | var latitude: String 35 | var longitude: String 36 | } 37 | 38 | struct Login: Codable { 39 | var uuid: String 40 | var username: String 41 | var password: String 42 | } 43 | 44 | struct APIInfo: Codable{ 45 | var seed: String 46 | var results: Int 47 | var page: Int 48 | var version: String 49 | } 50 | 51 | struct Picture: Codable { 52 | var large: String 53 | var medium: String 54 | var thumbnail: String 55 | } 56 | 57 | 58 | 59 | extension UserAPIResults { 60 | static var sampleUsers: UserAPIResults { 61 | let response: UserAPIResults? = try? Bundle.main.loadAndDecodeJSON(filename: "api") 62 | return response ?? UserAPIResults(results: [], info: APIInfo(seed: "", results: 1, page: 1, version: "")) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ModerneShopping/Utilities/ShoppingButtonStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShoppingButtonStyle.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-01. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// Add a styling to the add cart button in product view, change to green and success when added to the cart 11 | struct AddCartButtonStyle: ButtonStyle { 12 | func makeBody(configuration: Self.Configuration) -> some View { 13 | VStack(alignment: .leading) { 14 | HStack { 15 | Spacer() 16 | Image(systemName: configuration.isPressed ? "cart.badge.plus" : "cart") 17 | Spacer() 18 | } 19 | HStack { 20 | Spacer() 21 | if configuration.isPressed { 22 | Text("Added to Cart").bold() 23 | .foregroundColor(.tertiary) 24 | } else { 25 | configuration.label 26 | } 27 | Spacer() 28 | } 29 | } 30 | .foregroundColor(.tertiary) 31 | .padding() 32 | .background( 33 | Group{ 34 | if configuration.isPressed { 35 | Color.green 36 | } else { 37 | Color.darkText 38 | } 39 | } 40 | ) 41 | .shadow(color: .gray, radius: 2, x: /*@START_MENU_TOKEN@*/0.0/*@END_MENU_TOKEN@*/, y: /*@START_MENU_TOKEN@*/0.0/*@END_MENU_TOKEN@*/) 42 | .cornerRadius(12) 43 | .padding(.horizontal) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ModerneShopping/Utilities/CustomPicker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomPicker.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-02. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CustomPicker: View { 11 | @Binding var choosenCategory: ProductListEndpoint 12 | var body: some View { 13 | HStack(spacing:0){ 14 | // enumerating all the product category 15 | ForEach(ProductListEndpoint.allCases, id: \.self){category in 16 | VStack { 17 | Button(action: { 18 | withAnimation(.spring()){ 19 | choosenCategory = category 20 | } 21 | }){ 22 | Text(category.rawValue) 23 | .bold() 24 | .font(.caption2) 25 | .padding(8) 26 | .multilineTextAlignment(.center) 27 | .foregroundColor(choosenCategory == category ? .white : .accentColor) 28 | } 29 | .frame(height: 40) 30 | .background(choosenCategory == category ? Color.accentColor : Color.secondaryBackground) 31 | .cornerRadius(25) 32 | } 33 | } 34 | } 35 | .frame(height: 40) 36 | .background(Color.secondaryBackground) 37 | .cornerRadius(25) 38 | .shadow(color: .darkText.opacity(0.2), radius:2, x: 0.0, y: 0.0) 39 | } 40 | } 41 | 42 | struct CustomPicker_Previews: PreviewProvider { 43 | static var previews: some View { 44 | CustomPicker(choosenCategory: .constant(.all)) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ModerneShopping/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | 28 | UIApplicationSupportsIndirectInputEvents 29 | 30 | UILaunchScreen 31 | 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /ModerneShopping/Extensions/ImageLoader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageLoader.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-01. 6 | // 7 | 8 | import SwiftUI 9 | import UIKit 10 | 11 | private let _imageCache = NSCache() 12 | 13 | /// Load an image and cache it 14 | class ImageLoader: ObservableObject { 15 | @Published var image: UIImage? 16 | @Published var isLoading = false 17 | 18 | var imageCache = _imageCache 19 | 20 | func loadImage(with url: URL) { 21 | let urlString = url.absoluteString 22 | if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage { 23 | self.image = imageFromCache 24 | return 25 | } 26 | 27 | DispatchQueue.main.async { [weak self] in 28 | guard let self else { return } 29 | let session = URLSession.shared 30 | let task = session.dataTask(with: url) { data, _, error in 31 | if let error { 32 | print("Error: \(error)") 33 | return 34 | } 35 | 36 | guard let data else { 37 | print("Failed to get data") 38 | return 39 | } 40 | 41 | DispatchQueue.main.async { 42 | guard let image = UIImage(data: data) else { 43 | return 44 | } 45 | self.imageCache.setObject(image, forKey: urlString as AnyObject) 46 | DispatchQueue.main.async { [weak self] in 47 | self?.image = image 48 | } 49 | } 50 | } 51 | task.resume() 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ModerneShopping/ViewModel/ProductListViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductListViewModel.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-02. 6 | // 7 | 8 | import Foundation 9 | 10 | class ProductsListObject: ObservableObject { 11 | @Published var products: [Product]? 12 | @Published var isLoading = false 13 | @Published var error: NSError? 14 | 15 | var featuredProduct : [Product] { 16 | var fProducts: [Product] = [] 17 | if let products = self.products { 18 | if products.count >= 4 { 19 | fProducts = products[0...3].shuffled() 20 | } 21 | } 22 | return fProducts 23 | } 24 | 25 | /// Getting the api services singleton 26 | private let productListServices: APIServicesProtocol 27 | 28 | init(productServices: APIServicesProtocol = APIServices.shared){ 29 | self.productListServices = productServices 30 | } 31 | 32 | /// Call the api services to get the product needed 33 | /// - Parameter url: category of products 34 | func loadProducts(with url: ProductListEndpoint){ 35 | self.products = nil 36 | DispatchQueue.main.async { 37 | self.isLoading = true 38 | } 39 | productListServices.fetchProducts(from: url) { (result) in 40 | DispatchQueue.main.async { 41 | self.isLoading = true 42 | } 43 | switch result { 44 | case .success(let response): 45 | DispatchQueue.main.async { 46 | self.products = response 47 | self.isLoading = false 48 | } 49 | case .failure(let error): 50 | DispatchQueue.main.async { 51 | self.error = error as NSError 52 | print(error.localizedDescription) 53 | } 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ModerneShopping/Views/CartViews/CartListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CartListView.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-02. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CartListView: View { 11 | @ObservedObject var cart: CartViewModel 12 | let products: [Product: Int] 13 | @Binding var showDelete: Bool 14 | var body: some View { 15 | let productsDic = products.map({$0.key}) 16 | List { 17 | ForEach(productsDic, id: \.self){key in 18 | ZStack { 19 | Button(action: { 20 | withAnimation{ 21 | cart.removeFromCart(toRemove: key) 22 | } 23 | }){ 24 | HStack { 25 | VStack { 26 | Spacer() 27 | Image(systemName: "xmark") 28 | .imageScale(.large) 29 | .foregroundColor(.white) 30 | Spacer() 31 | } 32 | .frame(width: 100) 33 | .background(Color.red) 34 | .frame(width: 100) 35 | Spacer() 36 | } 37 | }.disabled(!showDelete) 38 | CartListItem(cart: cart,product: key, quantity: products[key] ?? 0) 39 | .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) 40 | .offset(x:showDelete ? 100 : 0) 41 | }.listRowBackground(Color.background) 42 | } 43 | } 44 | } 45 | } 46 | 47 | struct CartList_Previews: PreviewProvider { 48 | static var previews: some View { 49 | CartListView(cart: CartViewModel(), products: [Product.sampleProducts[0]: 1], showDelete: .constant(true)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ModerneShopping/Views/ProductViews/LottieFiles/productLoading.json: -------------------------------------------------------------------------------- 1 | {"v":"4.8.0","meta":{"g":"LottieFiles AE ","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":60,"w":24,"h":24,"nm":"loader","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":20,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.057,-0.006],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":60,"s":[360]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":20,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.057,-0.006],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1800,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /ModerneShopping/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ModerneShopping/Views/ProductViews/ProductList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductList.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-02. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ProductList: View { 11 | @EnvironmentObject var cart: CartViewModel 12 | let products: [Product] 13 | private let columns: [GridItem] = Array(repeating: .init(.flexible()), count: 2) 14 | @State private var product: Product? = nil 15 | var body: some View { 16 | LazyVGrid(columns: columns){ 17 | ForEach(products){product in 18 | VStack { 19 | Button(action:{self.product = product}){ 20 | ProductListItem(product: product) 21 | } 22 | Button(action: { 23 | withAnimation{ 24 | cart.addToCart(addedProduct: product, quantity: 1) 25 | } 26 | }, label: { 27 | HStack { 28 | Image(systemName: "cart.badge.plus") 29 | Text("Add to cart") 30 | .font(.caption) 31 | .bold() 32 | } 33 | .padding(8) 34 | .background(Color.secondaryBackground) 35 | .cornerRadius(18) 36 | }).accessibility(identifier: "Add to cart\(product.id)") 37 | } 38 | .background(Color.background 39 | .cornerRadius(16) 40 | .shadow(color: .darkText.opacity(0.05), radius: 2, x: 0.0, y: 0.0)) 41 | } 42 | }.sheet(item: $product){product in 43 | ProductView(product: product).environmentObject(cart) 44 | } 45 | } 46 | } 47 | 48 | struct ProductList_Previews: PreviewProvider { 49 | static var previews: some View { 50 | NavigationView { 51 | ProductList(products: Product.sampleProducts).environmentObject(CartViewModel()) 52 | } 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /ModerneShopping/api.json: -------------------------------------------------------------------------------- 1 | { 2 | "results":[ 3 | { 4 | "gender":"male", 5 | "name":{ 6 | "title":"Mr", 7 | "first":"Kadir", 8 | "last":"Süleymanoğlu" 9 | }, 10 | "location":{ 11 | "street":{ 12 | "number":5051, 13 | "name":"Tunalı Hilmi Cd" 14 | }, 15 | "city":"Bursa", 16 | "state":"Erzincan", 17 | "country":"Turkey", 18 | "postcode":28467, 19 | "coordinates":{ 20 | "latitude":"69.2563", 21 | "longitude":"-39.2973" 22 | }, 23 | "timezone":{ 24 | "offset":"-3:00", 25 | "description":"Brazil, Buenos Aires, Georgetown" 26 | } 27 | }, 28 | "email":"kadir.suleymanoglu@example.com", 29 | "login":{ 30 | "uuid":"7b58a343-ee28-4c9e-87a0-70b0a7349103", 31 | "username":"purplebird518", 32 | "password":"jonas", 33 | "salt":"44jFVJ7H", 34 | "md5":"2192248af7cd409653fb0c3088031689", 35 | "sha1":"ae24b6e2ffbd98654b4ba3ea454705e00ea69d34", 36 | "sha256":"4b3cd5ff8e4ea14c71f6008f2d7f39fbcae2bc8eef9e441ed6e50a4cfe01c1f4" 37 | }, 38 | "dob":{ 39 | "date":"1945-11-15T04:08:36.247Z", 40 | "age":76 41 | }, 42 | "registered":{ 43 | "date":"2014-05-31T15:30:22.514Z", 44 | "age":7 45 | }, 46 | "phone":"(642)-250-0292", 47 | "cell":"(681)-934-7614", 48 | "id":{ 49 | "name":"", 50 | "value":null 51 | }, 52 | "picture":{ 53 | "large":"https://randomuser.me/api/portraits/men/1.jpg", 54 | "medium":"https://randomuser.me/api/portraits/med/men/1.jpg", 55 | "thumbnail":"https://randomuser.me/api/portraits/thumb/men/1.jpg" 56 | }, 57 | "nat":"TR" 58 | } 59 | ], 60 | "info":{ 61 | "seed":"8463f9c37473e74e", 62 | "results":1, 63 | "page":1, 64 | "version":"1.3" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ModerneShopping/ViewModel/CartViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CartViewModel.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-03. 6 | // 7 | 8 | import SwiftUI 9 | 10 | class CartViewModel: ObservableObject { 11 | @Published var cartProduct: [Product] = [] 12 | @Published var cartProductDic: [Product: Int] = [:] 13 | @Published var totalPrice: Double = 0 14 | @Published var showShowcaseSheet: Bool = false 15 | 16 | /// adding a product with the quantity on our cart 17 | /// - Parameters: 18 | /// - addedProduct: product we want to add 19 | /// - quantity: quantity of product we want to add 20 | func addToCart(addedProduct: Product, quantity: Int){ 21 | let products = cartProductDic.map({$0.key}) 22 | // if we don't have any product we just create it with our quantity and leave the func 23 | if products.isEmpty { 24 | withAnimation{ 25 | cartProductDic[addedProduct] = quantity 26 | } 27 | return 28 | } 29 | for product in products { 30 | // if we already have the product we check our product and add the quantity 31 | if addedProduct.id == product.id { 32 | withAnimation{ 33 | cartProductDic[product]! += quantity 34 | } 35 | } else { 36 | // if we have products but dont have this one, we create it with the quantity 37 | if !products.contains(where: {$0.id == addedProduct.id}){ 38 | withAnimation{ 39 | cartProductDic[addedProduct] = quantity 40 | } 41 | } 42 | } 43 | } 44 | } 45 | func changeQuantity(product: Product,quantity: Int){ 46 | cartProductDic[product] = quantity 47 | } 48 | 49 | func calculateTotalPrice(){ 50 | var totalprice: Double = 0 51 | for (product,quantity) in cartProductDic { 52 | totalprice += product.price * Double(quantity) 53 | } 54 | withAnimation{ 55 | totalPrice = totalprice 56 | } 57 | } 58 | func removeFromCart(toRemove: Product){ 59 | cartProductDic.removeValue(forKey: toRemove) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ModerneShopping/Views/ProductViews/ProductCarousel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProductCarousel.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-08. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ProductCarousel: View { 11 | private let screenSize = UIScreen.main.bounds 12 | @EnvironmentObject var cart: CartViewModel 13 | let products: [Product] 14 | @State private var product: Product? = nil 15 | @State private var currentIndex: Int = 0 16 | private let timer = Timer.publish(every: 5, on: .main, in: .common).autoconnect() 17 | init(products: [Product]) { 18 | self.products = products 19 | UIPageControl.appearance().currentPageIndicatorTintColor = UIColor(Color.darkText) 20 | UIPageControl.appearance().pageIndicatorTintColor = UIColor(Color.secondaryBackground) 21 | } 22 | var body: some View { 23 | VStack(spacing:0) { 24 | TabView(selection: $currentIndex){ 25 | ForEach(0.. 100 { 19 | return 0 20 | } else { 21 | return 50 22 | } 23 | } 24 | var body: some View { 25 | VStack { 26 | Spacer() 27 | ZStack{ 28 | Color.background.edgesIgnoringSafeArea(.bottom) 29 | Color.secondaryBackground.opacity(0.3).edgesIgnoringSafeArea(.bottom) 30 | VStack(alignment:.center, spacing: 0){ 31 | HStack{ 32 | Button(action: {withAnimation{cart.showShowcaseSheet.toggle()}}, label: { 33 | Image(systemName: "xmark") 34 | .imageScale(.medium) 35 | .foregroundColor(.darkText) 36 | }).padding(8) 37 | .background(Color.secondaryBackground) 38 | .clipShape(Circle()) 39 | Spacer() 40 | }.padding() 41 | Spacer() 42 | ForEach(products){product in 43 | HStack { 44 | Text(product.title) 45 | .font(.caption) 46 | .lineLimit(1) 47 | Text("\(product.price.format(f: ".2"))$").bold() 48 | Spacer() 49 | }.padding(.horizontal) 50 | .foregroundColor(.darkText) 51 | .background(Color.background) 52 | .padding(.horizontal) 53 | } 54 | 55 | Text("Taxes: \(taxes.format(f: ".02"))$") 56 | .font(.caption) 57 | .padding(.top) 58 | Text("Delivery: \(delivery.format(f: ".02"))$") 59 | .font(.caption) 60 | Text("Final Price: \((price + taxes + delivery).format(f: ".02"))$") 61 | .font(.caption) 62 | Button(action: {print("Paying ...")}) { 63 | Text("Click Here to Pay").bold() 64 | .padding() 65 | .background(Color.secondaryBackground) 66 | .cornerRadius(18) 67 | }.padding() 68 | }.foregroundColor(.darkText) 69 | Spacer() 70 | }.cornerRadius(12) 71 | .frame(height: 300) 72 | } 73 | .transition(.move(edge: .bottom)) 74 | .zIndex(20) 75 | } 76 | } 77 | 78 | struct CheckOutView_Previews: PreviewProvider { 79 | static var previews: some View { 80 | CheckOutView(products: Array(Product.sampleProducts[0...2]), price: 500) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /ModerneShopping/ViewModel/UserViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserViewModel.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-06. 6 | // 7 | 8 | import SwiftUI 9 | 10 | class UserViewModel: ObservableObject { 11 | @Published var user: UserAPIResults? 12 | @Published var isLoading = false 13 | @Published var error: NSError? 14 | @Published var isLoggedin = false 15 | @Published var login = "admin@admin.com" 16 | @Published var password = "admin" 17 | @Published var isNameValid: Bool? = nil 18 | @Published var isPasswordValid: Bool? = nil 19 | 20 | private let userServices: APIServicesProtocol 21 | 22 | init(userServices: APIServicesProtocol = APIServices.shared){ 23 | self.userServices = userServices 24 | } 25 | 26 | /// Calling the API Service VM Getting a user through random generated user API 27 | func loadUser(){ 28 | // setting the user to nil to load a fresh one 29 | self.user = nil 30 | // showing the spining loading view, using the main thread for UI Work 31 | DispatchQueue.main.async { 32 | self.isLoading = true 33 | } 34 | // calling the api function and assigning the user if found 35 | userServices.fetchUser { (result) in 36 | DispatchQueue.main.async { 37 | self.isLoading = true 38 | } 39 | switch result { 40 | case .success(let response): 41 | DispatchQueue.main.async { 42 | self.user = response 43 | self.isLoading = false 44 | } 45 | case .failure(let error): 46 | DispatchQueue.main.async { 47 | print(error) 48 | self.error = error as NSError 49 | } 50 | } 51 | } 52 | } 53 | /// signing out and reseting the login view 54 | func signout(){ 55 | isLoading = true 56 | isNameValid = nil 57 | isPasswordValid = nil 58 | // Delaying the logout to see the Loading animation 59 | DispatchQueue.main.asyncAfter(deadline: .now() + 1){ 60 | self.user = nil 61 | self.isLoading = false 62 | } 63 | } 64 | /// validate if the username respect our conditions 65 | /// - Parameter name: username 66 | func validateName(name: String){ 67 | guard name.count > 5 && name.count < 24 else { 68 | withAnimation{ 69 | isNameValid = false 70 | } 71 | return 72 | } 73 | guard name.contains("@") else { 74 | withAnimation{ 75 | isNameValid = false 76 | } 77 | return 78 | } 79 | withAnimation{ 80 | isNameValid = true 81 | } 82 | } 83 | /// validate if the password respect our conditions 84 | /// - Parameter name: password 85 | func validatePassword(name: String){ 86 | guard name.count >= 5 && name.count < 24 else { 87 | withAnimation{ 88 | isPasswordValid = false 89 | } 90 | return 91 | } 92 | withAnimation{ 93 | isPasswordValid = true 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /ModerneShopping/Views/ProfilViews/LoggedInView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoggedInView.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-06. 6 | // 7 | 8 | import SwiftUI 9 | import MapKit 10 | 11 | struct LoggedInView: View { 12 | @EnvironmentObject var users: UserViewModel 13 | @State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 0, longitude: 0), span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10)) 14 | let user: User 15 | var body: some View { 16 | NavigationView{ 17 | ZStack { 18 | Color.background.edgesIgnoringSafeArea(.all) 19 | VStack(alignment: .center) { 20 | HeaderLoggedInView(user: user) 21 | .padding(.bottom) 22 | Divider() 23 | ProfilButtons().environmentObject(users) 24 | Spacer() 25 | MapView(region: $region, user: user) 26 | .shadow(color: .darkText.opacity(0.2), radius: 4, x: 1, y: 2) 27 | Spacer() 28 | } 29 | .navigationBarBackButtonHidden(false) 30 | .navigationBarTitleDisplayMode(.inline) 31 | .onAppear{ 32 | print("Lattitude : \(user.location.coordinates.latitude)") 33 | print("Longitude : \(user.location.coordinates.longitude)") 34 | region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude:Double( user.location.coordinates.latitude) ?? 0, longitude: Double( user.location.coordinates.longitude) ?? 0), span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10)) 35 | } 36 | }.navigationBarHidden(true) 37 | } 38 | } 39 | } 40 | 41 | struct LoggedInView_Previews: PreviewProvider { 42 | static var previews: some View { 43 | LoggedInView(user: UserAPIResults.sampleUsers.results[0]) 44 | } 45 | } 46 | 47 | struct HeaderLoggedInView: View { 48 | @StateObject var imageLoader = ImageLoader() 49 | let user: User 50 | var body: some View { 51 | HStack { 52 | ZStack{ 53 | Circle().fill(Color.secondaryBackground) 54 | .frame(width: 90, height: 90) 55 | if let image = imageLoader.image{ 56 | Image(uiImage: image) 57 | .resizable() 58 | .frame(width: 80, height: 80) 59 | .clipShape(Circle()) 60 | .padding(8) 61 | } 62 | } 63 | .padding() 64 | VStack(alignment: .leading) { 65 | Text("\(user.name.first) \(user.name.last)") 66 | .font(.title2) 67 | .bold() 68 | Text("\(user.email)") 69 | .font(.caption) 70 | .foregroundColor(.secondary) 71 | Text("\(user.location.city)") 72 | } 73 | Spacer() 74 | }.padding() 75 | .onAppear{ 76 | imageLoader.loadImage(with: URL(string:user.picture.large)!) 77 | } 78 | } 79 | } 80 | 81 | struct MapView: View { 82 | @Binding var region: MKCoordinateRegion 83 | let user: User 84 | @State var annotation: [UserLocation] = [UserLocation(name: "Default", coordinate: CLLocationCoordinate2D(latitude: 0, longitude: 0)), UserLocation(name: "Apple", coordinate: CLLocationCoordinate2D(latitude: 37.334722, longitude: -122.008889))] 85 | var body: some View { 86 | Map(coordinateRegion: $region, annotationItems: annotation){ 87 | MapAnnotation(coordinate: $0.coordinate, anchorPoint: CGPoint(x: 0.5, y: 0.5)){ 88 | Image(systemName: "person.circle.fill") 89 | .foregroundColor(.red) 90 | .imageScale(.large) 91 | } 92 | } 93 | .frame(height: 200) 94 | .cornerRadius(12) 95 | .padding(.horizontal) 96 | .onAppear{ 97 | annotation = [UserLocation(name: user.name.first, coordinate: CLLocationCoordinate2D(latitude: Double(user.location.coordinates.latitude) ?? 0, longitude: Double(user.location.coordinates.longitude) ?? 0))] 98 | } 99 | } 100 | } 101 | 102 | struct UserLocation: Identifiable { 103 | let id = UUID() 104 | let name: String 105 | let coordinate: CLLocationCoordinate2D 106 | } 107 | 108 | 109 | -------------------------------------------------------------------------------- /ModerneShopping/ViewModel/APIServices.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIServices.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-02. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol APIServicesProtocol { 11 | func fetchProducts(from endpoint: ProductListEndpoint, completion: @escaping (Result<[Product], APICallError>) -> ()) 12 | func fetchUser(completion: @escaping (Result) -> ()) 13 | } 14 | 15 | class APIServices: APIServicesProtocol { 16 | 17 | /// Shared Signleton of the api calls 18 | static let shared = APIServices() 19 | private let baseURL = "https://fakestoreapi.com/products" 20 | private let userURL = "https://randomuser.me/api/" 21 | private let apiCall = URLSession.shared 22 | 23 | /// Using ProductListEndpoint to generate the right api endpoint for Fake Shopping API 24 | /// - Parameters: 25 | /// - endpoint: category of product we need to access 26 | /// - completion: returning the data needed 27 | func fetchProducts(from endpoint: ProductListEndpoint, completion: @escaping (Result<[Product], APICallError>) -> ()){ 28 | guard let url = URL(string: "\(baseURL)\(endpoint.description)") else { 29 | completion(.failure(.invalidEndpoint)) 30 | return 31 | } 32 | loadURLAndDecode(url: url, completion: completion) 33 | } 34 | /// API Call to Random Generated User API 35 | /// - Parameter completion: return a user 36 | func fetchUser(completion: @escaping (Result) -> ()){ 37 | guard let url = URL(string: "\(userURL)") else { 38 | completion(.failure(.invalidEndpoint)) 39 | return 40 | } 41 | loadURLAndDecode(url: url, completion: completion) 42 | } 43 | } 44 | 45 | 46 | /// Diffrent product and the api endpoint 47 | enum ProductListEndpoint: String, CaseIterable { 48 | 49 | case all = "All" 50 | case jewelery = "Jewelery" 51 | case electronics = "electronics" 52 | case men = "men's clothing" 53 | case women = "women's clothing" 54 | 55 | var description: String { 56 | switch self { 57 | case .all: return "/" 58 | case .jewelery: return "/category/jewelery" 59 | case .electronics: return "/category/electronics" 60 | case .men: return "/category/men's%20clothing" 61 | case .women: return "/category/women's%20clothing" 62 | } 63 | } 64 | } 65 | 66 | extension APIServices { 67 | 68 | /// Call an api Endpoint and decode the data that it returns 69 | /// - Parameters: 70 | /// - url: api call url with api key if needed 71 | /// - parameters: url components, basicaly the html header needed 72 | /// - completion: passing the data through a completion 73 | /// - Returns: decoded data or an API Endpoint Error 74 | private func loadURLAndDecode(url: URL, parameters: [String: String]? = nil, completion: @escaping (Result)-> ()){ 75 | guard let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { 76 | completion(.failure(.invalidEndpoint)) 77 | return 78 | } 79 | 80 | guard let finalURL = urlComponents.url else { 81 | completion(.failure(.invalidEndpoint)) 82 | return 83 | } 84 | apiCall.dataTask(with: finalURL){ (data, response, error) in 85 | 86 | if error != nil { 87 | completion(.failure(.apiError)) 88 | } 89 | guard let urlResponse = response as? HTTPURLResponse, 200..<300 ~= urlResponse.statusCode else { 90 | completion(.failure(.invalidResponse)) 91 | return 92 | } 93 | 94 | guard let data = data else { 95 | completion(.failure(.noData)) 96 | return 97 | } 98 | 99 | do { 100 | let decodedData = try JSONDecoder().decode(T.self, from: data) 101 | completion(.success(decodedData)) 102 | } catch { 103 | completion(.failure(.decodingError)) 104 | } 105 | }.resume() 106 | } 107 | } 108 | 109 | enum APICallError: Error, CustomNSError { 110 | case apiError 111 | case invalidEndpoint 112 | case noData 113 | case invalidResponse 114 | case decodingError 115 | 116 | var localizedDescription: String { 117 | switch self { 118 | case .apiError: return "Failed to fetch data" 119 | case .invalidEndpoint: return "Invalid endpoint" 120 | case .invalidResponse: return "Invalid response" 121 | case .noData: return "No data" 122 | case .decodingError: return "Failed to decode data" 123 | } 124 | } 125 | 126 | var errorUserInfo: [String : Any] { 127 | [NSLocalizedDescriptionKey: localizedDescription] 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /ModerneShopping/Views/ProductViews/ProductView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-01. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ProductView: View { 11 | @EnvironmentObject var cart: CartViewModel 12 | @Environment(\.presentationMode) var presentation 13 | // deprecated in iOS 15 we should use @Environment(.\dismiss) var dismiss 14 | @State private var quantity: Int = 1 15 | let product: Product 16 | var body: some View { 17 | ZStack { 18 | Color.background.edgesIgnoringSafeArea(.bottom) 19 | VStack { 20 | Spacer() 21 | HStack{ 22 | Button(action:{presentation.wrappedValue.dismiss()}){ 23 | Image(systemName: "xmark") 24 | .padding(8) 25 | .background(Color.secondaryBackground) 26 | .clipShape(Circle()) 27 | } 28 | Spacer() 29 | }.padding() 30 | ProductImage(imageURL: product.imageURL).padding(.top) 31 | .environmentObject(cart) 32 | ZStack { 33 | Color.background.edgesIgnoringSafeArea(.bottom) 34 | .cornerRadius(25) 35 | .shadow(color: .accentColor.opacity(0.2), radius: 3, x: /*@START_MENU_TOKEN@*/0.0/*@END_MENU_TOKEN@*/, y: /*@START_MENU_TOKEN@*/0.0/*@END_MENU_TOKEN@*/) 36 | VStack(spacing: 0){ 37 | Text(product.title) 38 | .font(.headline) 39 | .multilineTextAlignment(.center) 40 | .padding(24) 41 | Text("\(product.price.format(f: ".02"))$") 42 | .font(.headline) 43 | HStack(spacing: 2) { 44 | Text("\(product.formatedRating)").font(.title3) 45 | Text("(\(product.rating.manualCount))").font(.caption) 46 | .foregroundColor(.secondary) 47 | .offset(y: 3) 48 | } 49 | .padding(8) 50 | Text(product.description).italic() 51 | .foregroundColor(.secondary) 52 | .padding() 53 | .multilineTextAlignment(.center) 54 | Spacer() 55 | VStack(spacing: 0) { 56 | Text("Quantity").font(.headline) 57 | Picker(selection: $quantity, label: /*@START_MENU_TOKEN@*/Text("Picker")/*@END_MENU_TOKEN@*/, content: { 58 | ForEach(1...10, id:\.self){quantity in 59 | Text("\(quantity)").tag(quantity) 60 | } 61 | 62 | }).pickerStyle(SegmentedPickerStyle()) 63 | .padding() 64 | } 65 | Button(action: { 66 | cart.addToCart(addedProduct: product, quantity: quantity) 67 | }){ 68 | HStack { 69 | Text("Add to cart").bold() 70 | } 71 | }.buttonStyle(AddCartButtonStyle()) 72 | } 73 | }.edgesIgnoringSafeArea(.bottom) 74 | Spacer() 75 | } 76 | }.navigationBarTitleDisplayMode(.large) 77 | // ajouter un navigation view vers le cart 78 | } 79 | } 80 | 81 | struct ProductImage: View { 82 | @EnvironmentObject var cart: CartViewModel 83 | @StateObject private var imageLoader = ImageLoader() 84 | let imageURL: URL 85 | var body: some View { 86 | ZStack{ 87 | Rectangle() 88 | .fill(Color.white) 89 | .frame(width: 260, height: 300, alignment: .center) 90 | .cornerRadius(12) 91 | .overlay( 92 | ZStack { 93 | ProgressView() 94 | if imageLoader.image != nil { 95 | HStack { 96 | Spacer() 97 | Image(uiImage: imageLoader.image!) 98 | .resizable() 99 | .compositingGroup() 100 | .clipped(antialiased: true) 101 | .aspectRatio(contentMode: .fit) 102 | .cornerRadius(12) 103 | .padding() 104 | Spacer() 105 | } 106 | } 107 | } 108 | ) 109 | } 110 | .cornerRadius(12) 111 | .onAppear { 112 | imageLoader.loadImage(with: imageURL) 113 | } 114 | } 115 | } 116 | struct ContentView_Previews: PreviewProvider { 117 | @Namespace static var namespace 118 | static var previews: some View { 119 | ProductView(product: Product.sampleProducts[6]) 120 | .environmentObject(CartViewModel()) 121 | } 122 | } 123 | 124 | //.padding(.leading, product == products.first ? 12 : 0) 125 | //.padding(.trailing, product == products.last ? 12 : 0) 126 | -------------------------------------------------------------------------------- /ModerneShopping/Views/HomeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeView.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-02. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct HomeView: View { 11 | @ObservedObject var productsList: ProductsListObject 12 | @EnvironmentObject var cart: CartViewModel 13 | @ObservedObject var user: UserViewModel 14 | @State var pickedCategory: ProductListEndpoint = .all 15 | var body: some View { 16 | NavigationView{ 17 | ZStack { 18 | Color.background.edgesIgnoringSafeArea(.all) 19 | ScrollView(.vertical){ 20 | VStack(alignment: .center) { 21 | Text("Hello \(user.user?.results[0].name.first ?? "")! \n Enjoy your shopping 🥳") 22 | .font(.title).bold() 23 | .foregroundColor(.darkText) 24 | .multilineTextAlignment(.center) 25 | .padding() 26 | CustomPicker(choosenCategory: $pickedCategory) 27 | .onChange(of: pickedCategory, perform: { value in 28 | DispatchQueue.main.async { 29 | productsList.loadProducts(with: pickedCategory) 30 | } 31 | }) 32 | if productsList.products != nil { 33 | ProductCarousel(products: productsList.featuredProduct) 34 | .environmentObject(cart) 35 | .padding(.top) 36 | } else { 37 | LoadingView(isLoading: productsList.isLoading, error: productsList.error){ productsList.loadProducts(with: pickedCategory) 38 | } 39 | } 40 | if productsList.products != nil { 41 | ProductList(products: productsList.products!) 42 | .environmentObject(cart) 43 | } else { 44 | LoadingView(isLoading: productsList.isLoading, error: productsList.error){ productsList.loadProducts(with: pickedCategory) 45 | } 46 | } 47 | } 48 | .onAppear{ 49 | DispatchQueue.main.async { 50 | productsList.loadProducts(with: pickedCategory) 51 | } 52 | } 53 | Spacer(minLength: 40) 54 | } 55 | }.navigationBarTitleDisplayMode(.large) 56 | .navigationBarItems( 57 | leading: NavigationLink(destination:ProfilView().environmentObject(user)){ 58 | leadingBarItem(user: user.user?.results[0]) 59 | }, 60 | trailing: 61 | TrailingBarItem().environmentObject(cart) 62 | ) 63 | }.statusBar(hidden: true) 64 | } 65 | 66 | } 67 | 68 | struct HomeView_Previews: PreviewProvider { 69 | static var previews: some View { 70 | HomeView(productsList: ProductsListObject(), user: UserViewModel()).environmentObject(CartViewModel()) 71 | } 72 | } 73 | 74 | struct TrailingBarItem: View { 75 | @EnvironmentObject var cart: CartViewModel 76 | var body: some View { 77 | NavigationLink(destination: CartView(cartProducts: cart)){ 78 | Image(systemName:"cart") 79 | .foregroundColor(.darkText) 80 | .imageScale(.large) 81 | .overlay( 82 | VStack { 83 | if cart.cartProductDic.keys.count > 0 { 84 | ZStack { 85 | Circle().fill(Color.secondaryBackground) 86 | Text("\(cart.cartProductDic.keys.count)") 87 | .font(.caption) 88 | .accessibility(identifier:"cartItemsNumber") 89 | .foregroundColor(.darkText) 90 | 91 | } 92 | Spacer() 93 | } 94 | }.offset(x: 10, y: -10) 95 | .shadow(color: .darkText.opacity(0.2), radius: 2, x: /*@START_MENU_TOKEN@*/0.0/*@END_MENU_TOKEN@*/, y: /*@START_MENU_TOKEN@*/0.0/*@END_MENU_TOKEN@*/) 96 | ) 97 | }.accentColor(.darkText) 98 | .accessibility(identifier: "trailingNavigationBarItem") 99 | } 100 | } 101 | 102 | struct leadingBarItem: View { 103 | @StateObject var imageLoader = ImageLoader() 104 | let user: User? 105 | var body: some View { 106 | ZStack { 107 | Circle() 108 | .fill(Color.secondaryBackground) 109 | .frame(width: 40, height: 40) 110 | .overlay( 111 | Group{ 112 | if let user = self.user { 113 | if let image = imageLoader.image{ 114 | Image(uiImage: image) 115 | .resizable() 116 | .clipped() 117 | .clipShape(Circle()) 118 | } 119 | else { 120 | LoadingView(isLoading: imageLoader.isLoading, error: nil, retryAction:{ imageLoader.loadImage(with: URL(string: user.picture.thumbnail)!)}) 121 | } 122 | } else { 123 | Image(systemName: "person") 124 | .foregroundColor(.darkText) 125 | .imageScale(.large) 126 | } 127 | } 128 | ) 129 | .overlay(Circle().stroke(lineWidth: 2).foregroundColor(Color.darkText)) 130 | }.onAppear{ 131 | if let user = self.user{ 132 | imageLoader.loadImage(with: URL(string: user.picture.thumbnail)!) 133 | } 134 | } 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /ModerneShopping/Views/ProfilViews/LoginView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginView.swift 3 | // ModerneShopping 4 | // 5 | // Created by Djallil Elkebir on 2021-09-08. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LoginView: View { 11 | @EnvironmentObject var user: UserViewModel 12 | @State private var isNameValid: Bool? = nil 13 | @State private var isPasswordValid: Bool? = nil 14 | @State private var showPassword: Bool = false 15 | @State private var showSheet: Bool = false 16 | var body: some View { 17 | ZStack{ 18 | Color.background.edgesIgnoringSafeArea(.all) 19 | VStack(alignment: .center,spacing: 16){ 20 | Text("Login") 21 | .font(.title3).bold() 22 | LoginLottieView() 23 | .aspectRatio(contentMode: .fit) 24 | Spacer() 25 | VStack{ 26 | HStack { 27 | LoginTextView(name: $user.login, isValid: $user.isNameValid) 28 | if let nameValid = user.isNameValid{ 29 | if nameValid { 30 | Image(systemName: "checkmark") 31 | .foregroundColor(.green) 32 | .padding(8) 33 | } else { 34 | Image(systemName: "xmark") 35 | .foregroundColor(.red) 36 | .padding(8) 37 | } 38 | } 39 | } 40 | HStack { 41 | PasswordTextView(name: $user.password, isValid: $user.isPasswordValid, showPassword: $showPassword) 42 | if let passwordValid = user.isPasswordValid{ 43 | if passwordValid { 44 | Image(systemName: "checkmark") 45 | .foregroundColor(.green) 46 | .padding(8) 47 | } else { 48 | Image(systemName: "xmark") 49 | .foregroundColor(.red) 50 | .padding(8) 51 | } 52 | } 53 | } 54 | HStack{ 55 | Button(action:{withAnimation{ 56 | showSheet.toggle() 57 | }}){ 58 | Text("Forgot Password ?") 59 | .font(.subheadline).bold() 60 | } 61 | .padding(8) 62 | Spacer() 63 | } 64 | Button(action: { 65 | user.validateName(name: user.login) 66 | user.validatePassword(name: user.password) 67 | DispatchQueue.main.asyncAfter(deadline: .now() + 1){ 68 | if user.isNameValid == true && 69 | user.isPasswordValid == true { 70 | withAnimation{ 71 | user.loadUser() 72 | } 73 | } else { 74 | withAnimation{ 75 | user.isNameValid = nil 76 | user.isPasswordValid = nil 77 | } 78 | } 79 | } 80 | }){ 81 | Text("Sign In") 82 | .foregroundColor(.darkText) 83 | .font(.headline) 84 | .padding() 85 | .background(Color.secondaryBackground) 86 | .cornerRadius(16) 87 | .shadow(color: .darkText.opacity(0.2), radius: 2, x: 1.0, y: 2) 88 | } 89 | Button(action:{withAnimation{ 90 | showSheet.toggle() 91 | }}){ 92 | Text("Create an account").font(.headline) 93 | .foregroundColor(.darkText) 94 | .shadow(color: .darkText.opacity(0.1), radius: 2, x: 1, y: 2) 95 | } 96 | } 97 | .padding() 98 | .background(Color.secondaryBackground) 99 | .cornerRadius(16) 100 | Spacer() 101 | }.padding() 102 | .sheet(isPresented: $showSheet){ 103 | Text("Create an account or forgot password") 104 | } 105 | } 106 | } 107 | } 108 | 109 | struct LoginView_Previews: PreviewProvider { 110 | static var previews: some View { 111 | LoginView() 112 | .environmentObject(UserViewModel()) 113 | 114 | } 115 | } 116 | 117 | struct LoginTextView: View{ 118 | @Binding var name: String 119 | @Binding var isValid: Bool? 120 | var body: some View { 121 | HStack { 122 | TextField("Username",text: $name) 123 | .padding() 124 | .background(Color.background) 125 | .cornerRadius(16) 126 | .shadow(color: .borderColor(condition: isValid), radius: 2, x: 0.0, y: 0.0) 127 | .disableAutocorrection(true) 128 | .autocapitalization(.none) 129 | } 130 | } 131 | 132 | } 133 | 134 | struct PasswordTextView: View{ 135 | @Binding var name: String 136 | @Binding var isValid: Bool? 137 | @Binding var showPassword: Bool 138 | var body: some View { 139 | Group{ 140 | if !showPassword{ 141 | HStack { 142 | SecureField("Password",text: $name) 143 | .padding() 144 | .background(Color.background) 145 | .cornerRadius(16) 146 | .shadow(color: .borderColor(condition: isValid), radius: 2, x: 0.0, y: 0.0) 147 | .disableAutocorrection(true) 148 | .autocapitalization(.none) 149 | Button(action:{ 150 | withAnimation{ 151 | showPassword.toggle() 152 | } 153 | }){ 154 | Image(systemName: "eye") 155 | .imageScale(.large) 156 | } 157 | } 158 | } else { 159 | HStack { 160 | LoginTextView(name: $name,isValid: $isValid) 161 | Button(action:{showPassword.toggle()}){ 162 | Image(systemName: "eye.slash") 163 | .imageScale(.large) 164 | } 165 | } 166 | } 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /ModerneShopping/products.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id":1, 4 | "title":"Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops", 5 | "price":109.95, 6 | "description":"Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday", 7 | "category":"men's clothing", 8 | "image":"https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg", 9 | "rating":{ 10 | "rate":3.9, 11 | "count":120 12 | } 13 | }, 14 | { 15 | "id":2, 16 | "title":"Mens Casual Premium Slim Fit T-Shirts ", 17 | "price":22.3, 18 | "description":"Slim-fitting style, contrast raglan long sleeve, three-button henley placket, light weight & soft fabric for breathable and comfortable wearing. And Solid stitched shirts with round neck made for durability and a great fit for casual fashion wear and diehard baseball fans. The Henley style round neckline includes a three-button placket.", 19 | "category":"men's clothing", 20 | "image":"https://fakestoreapi.com/img/71-3HjGNDUL._AC_SY879._SX._UX._SY._UY_.jpg", 21 | "rating":{ 22 | "rate":4.1, 23 | "count":259 24 | } 25 | }, 26 | { 27 | "id":3, 28 | "title":"Mens Cotton Jacket", 29 | "price":55.99, 30 | "description":"great outerwear jackets for Spring/Autumn/Winter, suitable for many occasions, such as working, hiking, camping, mountain/rock climbing, cycling, traveling or other outdoors. Good gift choice for you or your family member. A warm hearted love to Father, husband or son in this thanksgiving or Christmas Day.", 31 | "category":"men's clothing", 32 | "image":"https://fakestoreapi.com/img/71li-ujtlUL._AC_UX679_.jpg", 33 | "rating":{ 34 | "rate":4.7, 35 | "count":500 36 | } 37 | }, 38 | { 39 | "id":4, 40 | "title":"Mens Casual Slim Fit", 41 | "price":15.99, 42 | "description":"The color could be slightly different between on the screen and in practice. / Please note that body builds vary by person, therefore, detailed size information should be reviewed below on the product description.", 43 | "category":"men's clothing", 44 | "image":"https://fakestoreapi.com/img/71YXzeOuslL._AC_UY879_.jpg", 45 | "rating":{ 46 | "rate":2.1, 47 | "count":430 48 | } 49 | }, 50 | { 51 | "id":5, 52 | "title":"John Hardy Women's Legends Naga Gold & Silver Dragon Station Chain Bracelet", 53 | "price":695, 54 | "description":"From our Legends Collection, the Naga was inspired by the mythical water dragon that protects the ocean's pearl. Wear facing inward to be bestowed with love and abundance, or outward for protection.", 55 | "category":"jewelery", 56 | "image":"https://fakestoreapi.com/img/71pWzhdJNwL._AC_UL640_QL65_ML3_.jpg", 57 | "rating":{ 58 | "rate":4.6, 59 | "count":400 60 | } 61 | }, 62 | { 63 | "id":6, 64 | "title":"Solid Gold Petite Micropave ", 65 | "price":168, 66 | "description":"Satisfaction Guaranteed. Return or exchange any order within 30 days.Designed and sold by Hafeez Center in the United States. Satisfaction Guaranteed. Return or exchange any order within 30 days.", 67 | "category":"jewelery", 68 | "image":"https://fakestoreapi.com/img/61sbMiUnoGL._AC_UL640_QL65_ML3_.jpg", 69 | "rating":{ 70 | "rate":3.9, 71 | "count":70 72 | } 73 | }, 74 | { 75 | "id":7, 76 | "title":"White Gold Plated Princess", 77 | "price":9.99, 78 | "description":"Classic Created Wedding Engagement Solitaire Diamond Promise Ring for Her. Gifts to spoil your love more for Engagement, Wedding, Anniversary, Valentine's Day...", 79 | "category":"jewelery", 80 | "image":"https://fakestoreapi.com/img/71YAIFU48IL._AC_UL640_QL65_ML3_.jpg", 81 | "rating":{ 82 | "rate":3, 83 | "count":400 84 | } 85 | }, 86 | { 87 | "id":8, 88 | "title":"Pierced Owl Rose Gold Plated Stainless Steel Double", 89 | "price":10.99, 90 | "description":"Rose Gold Plated Double Flared Tunnel Plug Earrings. Made of 316L Stainless Steel", 91 | "category":"jewelery", 92 | "image":"https://fakestoreapi.com/img/51UDEzMJVpL._AC_UL640_QL65_ML3_.jpg", 93 | "rating":{ 94 | "rate":1.9, 95 | "count":100 96 | } 97 | }, 98 | { 99 | "id":9, 100 | "title":"WD 2TB Elements Portable External Hard Drive - USB 3.0 ", 101 | "price":64, 102 | "description":"USB 3.0 and USB 2.0 Compatibility Fast data transfers Improve PC Performance High Capacity; Compatibility Formatted NTFS for Windows 10, Windows 8.1, Windows 7; Reformatting may be required for other operating systems; Compatibility may vary depending on user’s hardware configuration and operating system", 103 | "category":"electronics", 104 | "image":"https://fakestoreapi.com/img/61IBBVJvSDL._AC_SY879_.jpg", 105 | "rating":{ 106 | "rate":3.3, 107 | "count":203 108 | } 109 | }, 110 | { 111 | "id":10, 112 | "title":"SanDisk SSD PLUS 1TB Internal SSD - SATA III 6 Gb/s", 113 | "price":109, 114 | "description":"Easy upgrade for faster boot up, shutdown, application load and response (As compared to 5400 RPM SATA 2.5” hard drive; Based on published specifications and internal benchmarking tests using PCMark vantage scores) Boosts burst write performance, making it ideal for typical PC workloads The perfect balance of performance and reliability Read/write speeds of up to 535MB/s/450MB/s (Based on internal testing; Performance may vary depending upon drive capacity, host device, OS and application.)", 115 | "category":"electronics", 116 | "image":"https://fakestoreapi.com/img/61U7T1koQqL._AC_SX679_.jpg", 117 | "rating":{ 118 | "rate":2.9, 119 | "count":470 120 | } 121 | }, 122 | { 123 | "id":11, 124 | "title":"Silicon Power 256GB SSD 3D NAND A55 SLC Cache Performance Boost SATA III 2.5", 125 | "price":109, 126 | "description":"3D NAND flash are applied to deliver high transfer speeds Remarkable transfer speeds that enable faster bootup and improved overall system performance. The advanced SLC Cache Technology allows performance boost and longer lifespan 7mm slim design suitable for Ultrabooks and Ultra-slim notebooks. Supports TRIM command, Garbage Collection technology, RAID, and ECC (Error Checking & Correction) to provide the optimized performance and enhanced reliability.", 127 | "category":"electronics", 128 | "image":"https://fakestoreapi.com/img/71kWymZ+c+L._AC_SX679_.jpg", 129 | "rating":{ 130 | "rate":4.8, 131 | "count":319 132 | } 133 | }, 134 | { 135 | "id":12, 136 | "title":"WD 4TB Gaming Drive Works with Playstation 4 Portable External Hard Drive", 137 | "price":114, 138 | "description":"Expand your PS4 gaming experience, Play anywhere Fast and easy, setup Sleek design with high capacity, 3-year manufacturer's limited warranty", 139 | "category":"electronics", 140 | "image":"https://fakestoreapi.com/img/61mtL65D4cL._AC_SX679_.jpg", 141 | "rating":{ 142 | "rate":4.8, 143 | "count":400 144 | } 145 | }, 146 | { 147 | "id":13, 148 | "title":"Acer SB220Q bi 21.5 inches Full HD (1920 x 1080) IPS Ultra-Thin", 149 | "price":599, 150 | "description":"21. 5 inches Full HD (1920 x 1080) widescreen IPS display And Radeon free Sync technology. No compatibility for VESA Mount Refresh Rate: 75Hz - Using HDMI port Zero-frame design | ultra-thin | 4ms response time | IPS panel Aspect ratio - 16: 9. Color Supported - 16. 7 million colors. Brightness - 250 nit Tilt angle -5 degree to 15 degree. Horizontal viewing angle-178 degree. Vertical viewing angle-178 degree 75 hertz", 151 | "category":"electronics", 152 | "image":"https://fakestoreapi.com/img/81QpkIctqPL._AC_SX679_.jpg", 153 | "rating":{ 154 | "rate":2.9, 155 | "count":250 156 | } 157 | }, 158 | { 159 | "id":14, 160 | "title":"Samsung 49-Inch CHG90 144Hz Curved Gaming Monitor (LC49HG90DMNXZA) – Super Ultrawide Screen QLED ", 161 | "price":999.99, 162 | "description":"49 INCH SUPER ULTRAWIDE 32:9 CURVED GAMING MONITOR with dual 27 inch screen side by side QUANTUM DOT (QLED) TECHNOLOGY, HDR support and factory calibration provides stunningly realistic and accurate color and contrast 144HZ HIGH REFRESH RATE and 1ms ultra fast response time work to eliminate motion blur, ghosting, and reduce input lag", 163 | "category":"electronics", 164 | "image":"https://fakestoreapi.com/img/81Zt42ioCgL._AC_SX679_.jpg", 165 | "rating":{ 166 | "rate":2.2, 167 | "count":140 168 | } 169 | }, 170 | { 171 | "id":15, 172 | "title":"BIYLACLESEN Women's 3-in-1 Snowboard Jacket Winter Coats", 173 | "price":56.99, 174 | "description":"Note:The Jackets is US standard size, Please choose size as your usual wear Material: 100% Polyester; Detachable Liner Fabric: Warm Fleece. Detachable Functional Liner: Skin Friendly, Lightweigt and Warm.Stand Collar Liner jacket, keep you warm in cold weather. Zippered Pockets: 2 Zippered Hand Pockets, 2 Zippered Pockets on Chest (enough to keep cards or keys)and 1 Hidden Pocket Inside.Zippered Hand Pockets and Hidden Pocket keep your things secure. Humanized Design: Adjustable and Detachable Hood and Adjustable cuff to prevent the wind and water,for a comfortable fit. 3 in 1 Detachable Design provide more convenience, you can separate the coat and inner as needed, or wear it together. It is suitable for different season and help you adapt to different climates", 175 | "category":"women's clothing", 176 | "image":"https://fakestoreapi.com/img/51Y5NI-I5jL._AC_UX679_.jpg", 177 | "rating":{ 178 | "rate":2.6, 179 | "count":235 180 | } 181 | }, 182 | { 183 | "id":16, 184 | "title":"Lock and Love Women's Removable Hooded Faux Leather Moto Biker Jacket", 185 | "price":29.95, 186 | "description":"100% POLYURETHANE(shell) 100% POLYESTER(lining) 75% POLYESTER 25% COTTON (SWEATER), Faux leather material for style and comfort / 2 pockets of front, 2-For-One Hooded denim style faux leather jacket, Button detail on waist / Detail stitching at sides, HAND WASH ONLY / DO NOT BLEACH / LINE DRY / DO NOT IRON", 187 | "category":"women's clothing", 188 | "image":"https://fakestoreapi.com/img/81XH0e8fefL._AC_UY879_.jpg", 189 | "rating":{ 190 | "rate":2.9, 191 | "count":340 192 | } 193 | }, 194 | { 195 | "id":17, 196 | "title":"Rain Jacket Women Windbreaker Striped Climbing Raincoats", 197 | "price":39.99, 198 | "description":"Lightweight perfet for trip or casual wear---Long sleeve with hooded, adjustable drawstring waist design. Button and zipper front closure raincoat, fully stripes Lined and The Raincoat has 2 side pockets are a good size to hold all kinds of things, it covers the hips, and the hood is generous but doesn't overdo it.Attached Cotton Lined Hood with Adjustable Drawstrings give it a real styled look.", 199 | "category":"women's clothing", 200 | "image":"https://fakestoreapi.com/img/71HblAHs5xL._AC_UY879_-2.jpg", 201 | "rating":{ 202 | "rate":3.8, 203 | "count":679 204 | } 205 | }, 206 | { 207 | "id":18, 208 | "title":"MBJ Women's Solid Short Sleeve Boat Neck V ", 209 | "price":9.85, 210 | "description":"95% RAYON 5% SPANDEX, Made in USA or Imported, Do Not Bleach, Lightweight fabric with great stretch for comfort, Ribbed on sleeves and neckline / Double stitching on bottom hem", 211 | "category":"women's clothing", 212 | "image":"https://fakestoreapi.com/img/71z3kpMAYsL._AC_UY879_.jpg", 213 | "rating":{ 214 | "rate":4.7, 215 | "count":130 216 | } 217 | }, 218 | { 219 | "id":19, 220 | "title":"Opna Women's Short Sleeve Moisture", 221 | "price":7.95, 222 | "description":"100% Polyester, Machine wash, 100% cationic polyester interlock, Machine Wash & Pre Shrunk for a Great Fit, Lightweight, roomy and highly breathable with moisture wicking fabric which helps to keep moisture away, Soft Lightweight Fabric with comfortable V-neck collar and a slimmer fit, delivers a sleek, more feminine silhouette and Added Comfort", 223 | "category":"women's clothing", 224 | "image":"https://fakestoreapi.com/img/51eg55uWmdL._AC_UX679_.jpg", 225 | "rating":{ 226 | "rate":4.5, 227 | "count":146 228 | } 229 | }, 230 | { 231 | "id":20, 232 | "title":"DANVOUY Womens T Shirt Casual Cotton Short", 233 | "price":12.99, 234 | "description":"95%Cotton,5%Spandex, Features: Casual, Short Sleeve, Letter Print,V-Neck,Fashion Tees, The fabric is soft and has some stretch., Occasion: Casual/Office/Beach/School/Home/Street. Season: Spring,Summer,Autumn,Winter.", 235 | "category":"women's clothing", 236 | "image":"https://fakestoreapi.com/img/61pHAEJ4NML._AC_UX679_.jpg", 237 | "rating":{ 238 | "rate":3.6, 239 | "count":145 240 | } 241 | } 242 | ] 243 | -------------------------------------------------------------------------------- /ModerneShopping/Views/CartViews/LottieFiles/cartloading.json: -------------------------------------------------------------------------------- 1 | {"v":"5.6.2","fr":25,"ip":0,"op":153,"w":1000,"h":1000,"nm":"TROLLEY 2","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"ROUE Silhouettes","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.387],"y":[0.999]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[0]},{"t":127,"s":[3600]}],"ix":10},"p":{"a":0,"k":[372.337,768.478,0],"ix":2},"a":{"a":0,"k":[127.974,127.973,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,29.257],[29.256,0],[0,-29.256],[-29.256,0]],"o":[[0,-29.256],[-29.256,0],[0,29.257],[29.256,0]],"v":[[52.974,-0.001],[0,-52.973],[-52.974,-0.001],[0,52.973]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[127.973,127.974],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Groupe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.371],"y":[1.004]},"o":{"x":[0.953],"y":[0.023]},"t":12,"s":[100]},{"i":{"x":[0.743],"y":[1.813]},"o":{"x":[0.167],"y":[0]},"t":26,"s":[15]},{"i":{"x":[0],"y":[0.995]},"o":{"x":[1],"y":[-0.01]},"t":122,"s":[15]},{"t":135,"s":[100]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Réduire les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5,"op":258,"st":6,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"ROUE Silhouettes","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[443.073]},{"t":127,"s":[3600]}],"ix":10},"p":{"a":0,"k":[651.038,768.478,0],"ix":2},"a":{"a":0,"k":[127.974,127.973,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,29.257],[29.257,0],[0,-29.256],[-29.255,0]],"o":[[0,-29.256],[-29.255,0],[0,29.257],[29.257,0]],"v":[[52.974,-0.001],[0,-52.973],[-52.974,-0.001],[0,52.973]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[127.974,127.974],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Groupe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.043],"y":[0.974]},"o":{"x":[1],"y":[0.011]},"t":4,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":18,"s":[85]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":120,"s":[85]},{"t":131,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Réduire les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":250,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"BAC Silhouettes","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":11,"s":[-3]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[3]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":16,"s":[-2]},{"t":18,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11,"s":[308.353,463.466,0],"to":[-2,0,0],"ti":[2,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[296.353,463.466,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":41,"s":[296.353,463.466,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":44,"s":[296.353,463.466,0],"to":[0,1.833,0],"ti":[0,0.078,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":46,"s":[296.353,474.466,0],"to":[0,-0.078,0],"ti":[0,1.833,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":47,"s":[296.353,463,0],"to":[0,-1.833,0],"ti":[0,-0.078,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":51,"s":[296.353,463.466,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":54,"s":[296.353,463.466,0],"to":[0,1.833,0],"ti":[0,0.078,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":56,"s":[296.353,474.466,0],"to":[0,-0.078,0],"ti":[0,1.833,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":57,"s":[296.353,463,0],"to":[0,-1.833,0],"ti":[0,-0.078,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62,"s":[296.353,463.466,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65,"s":[296.353,463.466,0],"to":[0,1.833,0],"ti":[0,0.078,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":67,"s":[296.353,474.466,0],"to":[0,-0.078,0],"ti":[0,1.911,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68,"s":[296.353,463,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80,"s":[296.353,463,0],"to":[0,5.5,0],"ti":[0,2.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":82,"s":[296.353,496,0],"to":[0,-2.667,0],"ti":[0,2,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":83,"s":[296.353,447,0],"to":[0,-2,0],"ti":[0,-3.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86,"s":[296.353,484,0],"to":[0,3.333,0],"ti":[0,2.167,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88,"s":[296.353,467,0],"to":[0,-2.167,0],"ti":[0,-0.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":89,"s":[296.353,471,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":113,"s":[296.353,471,0],"to":[0,4.167,0],"ti":[0,4,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":115,"s":[296.353,496,0],"to":[0,-4,0],"ti":[0,2,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":116,"s":[296.353,447,0],"to":[0,-2,0],"ti":[0,-3.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":119,"s":[296.353,484,0],"to":[0,3.333,0],"ti":[0,2.167,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":121,"s":[296.353,467,0],"to":[0,-2.167,0],"ti":[0,-0.667,0]},{"t":122,"s":[296.353,471,0]}],"ix":2},"a":{"a":0,"k":[112.014,215.708,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-271.014,-129.708],[271.014,-129.708],[201.984,129.708],[-166.414,129.708]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[346.014,204.708],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Groupe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[0.999]},"o":{"x":[0.87],"y":[0.002]},"t":9,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[100]},{"i":{"x":[0.354],"y":[0.997]},"o":{"x":[0.681],"y":[0.01]},"t":136,"s":[100]},{"t":154,"s":[0]}],"ix":1},"e":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Réduire les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":250,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"CADRE Silhouettes","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[240.971,201.655,0],"ix":2},"a":{"a":0,"k":[364.87,285.003,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-28.347,-14.317],[-7.748,0],[0,0]],"o":[[0,0],[0,0],[6.406,31.105],[8.122,4.103],[0,0],[0,0]],"v":[[-289.87,-210.003],[-191.232,-210.003],[-121.12,130.452],[-66.796,203.166],[-42.605,210.003],[289.87,210.003]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[364.87,285.003],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Groupe 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.098],"y":[0.984]},"o":{"x":[0.825],"y":[0.011]},"t":0,"s":[0]},{"i":{"x":[0.746],"y":[1.898]},"o":{"x":[0.167],"y":[0]},"t":13,"s":[100]},{"i":{"x":[0.268],"y":[0.996]},"o":{"x":[0.738],"y":[0.008]},"t":130,"s":[100]},{"t":148,"s":[0]}],"ix":1},"e":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Réduire les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":250,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Calque de forme 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[315.661,252.242,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[303,-174],[-247,-172],[-197,87],[235,87]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8470588235294118,1,0.9607843137254902,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-1,-2],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":250,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Calque de forme 1","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":133,"s":[100]},{"t":148,"s":[0]}],"ix":11},"r":{"a":0,"k":-31,"ix":10},"p":{"a":1,"k":[{"i":{"x":1,"y":1},"o":{"x":1,"y":0},"t":34,"s":[215.661,-409.258,0],"to":[0,78,0],"ti":[0,-78,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0.167},"t":44,"s":[215.661,58.742,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0},"t":84,"s":[215.661,58.742,0],"to":[0,-6.667,0],"ti":[0,3.333,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0},"t":86,"s":[215.661,18.742,0],"to":[0,-3.333,0],"ti":[0,-3.333,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0.167},"t":88,"s":[215.661,38.742,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":1,"y":0.999},"o":{"x":0.167,"y":0},"t":118,"s":[215.661,38.742,0],"to":[0,-3.333,0],"ti":[0,0,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0},"t":120,"s":[215.661,18.742,0],"to":[0,0,0],"ti":[0,-27.333,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0},"t":122,"s":[215.661,38.742,0],"to":[0,27.333,0],"ti":[0,-24,0]},{"t":124,"s":[215.661,182.742,0]}],"ix":2},"a":{"a":0,"k":[-12,-327.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[106,183],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Tracé rectangulaire 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8470588235294118,1,0.9607843137254902,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-12,-327.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":250,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Calque de forme 3","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":133,"s":[100]},{"t":148,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":1,"y":1},"o":{"x":1,"y":0},"t":43,"s":[355.661,-409.258,0],"to":[0,82.333,0],"ti":[0,-82.333,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0.167},"t":53,"s":[355.661,84.742,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":1,"y":0.999},"o":{"x":0.167,"y":0},"t":84,"s":[355.661,84.742,0],"to":[0,-3.333,0],"ti":[0,0,0]},{"i":{"x":1,"y":0.999},"o":{"x":0.167,"y":0},"t":86,"s":[355.661,64.742,0],"to":[0,0,0],"ti":[0,-3.333,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0.167},"t":88,"s":[355.661,84.742,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":1,"y":0.999},"o":{"x":0.167,"y":0},"t":118,"s":[355.661,84.742,0],"to":[0,-3.333,0],"ti":[0,0,0]},{"i":{"x":1,"y":0.999},"o":{"x":0.167,"y":0},"t":120,"s":[355.661,64.742,0],"to":[0,0,0],"ti":[0,-17,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0},"t":122,"s":[355.661,84.742,0],"to":[0,17,0],"ti":[0,-13.667,0]},{"t":124,"s":[355.661,166.742,0]}],"ix":2},"a":{"a":0,"k":[23,-219,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[142,142],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Tracé d'ellipse 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8470588235294118,1,0.9607843137254902,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[23,-219],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":250,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Calque de forme 4","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":133,"s":[100]},{"t":148,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":1,"y":1},"o":{"x":1,"y":0},"t":55,"s":[512.322,-363.551,0],"to":[0,67.667,0],"ti":[0,-67.667,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0.167},"t":65,"s":[512.322,42.449,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0},"t":84,"s":[512.322,42.449,0],"to":[0,-10,0],"ti":[0,0,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0},"t":86,"s":[512.322,-17.551,0],"to":[0,0,0],"ti":[0,-10,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0.167},"t":88,"s":[512.322,42.449,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0},"t":118,"s":[512.322,42.449,0],"to":[0,-10,0],"ti":[0,0,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0},"t":120,"s":[512.322,-17.551,0],"to":[0,0,0],"ti":[0,-26.333,0]},{"i":{"x":1,"y":1},"o":{"x":0.167,"y":0},"t":122,"s":[512.322,42.449,0],"to":[0,26.333,0],"ti":[0,-16.333,0]},{"t":124,"s":[512.322,140.449,0]}],"ix":2},"a":{"a":0,"k":[306,-410,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[306,-458],[242,-362],[370,-362]],"c":true},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8470588235294118,1,0.9607843137254902,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":250,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Calque de forme 12","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":91,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":96,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[100]},{"t":129,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-319,272],[334,272]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.8509803921568627,0.8509803921568627,0.8509803921568627,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.365],"y":[1.011]},"o":{"x":[0.919],"y":[-0.013]},"t":91,"s":[100]},{"t":124,"s":[43]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1.006]},"o":{"x":[1],"y":[-0.002]},"t":96,"s":[100]},{"t":128,"s":[43]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Réduire les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"d":[{"n":"o","nm":"décalage","v":{"a":0,"k":2000,"ix":7}}],"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Groupe 1","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":76,"op":326,"st":76,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Calque de forme 11","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":84,"s":[100]},{"t":101,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-319,272],[334,272]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.8509803921568627,0.8509803921568627,0.8509803921568627,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.365],"y":[1.007]},"o":{"x":[0.919],"y":[-0.009]},"t":75,"s":[100]},{"t":97,"s":[43]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1.004]},"o":{"x":[1],"y":[-0.001]},"t":80,"s":[100]},{"t":101,"s":[43]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Réduire les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"d":[{"n":"o","nm":"décalage","v":{"a":0,"k":2000,"ix":7}}],"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Groupe 1","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":58,"op":308,"st":58,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Calque de forme 10","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69,"s":[100]},{"t":76,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-319,272],[334,272]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.8509803921568627,0.8509803921568627,0.8509803921568627,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.365],"y":[1.004]},"o":{"x":[0.919],"y":[-0.005]},"t":60,"s":[100]},{"t":72,"s":[43]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1.002]},"o":{"x":[1],"y":[-0.001]},"t":65,"s":[100]},{"t":76,"s":[43]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Réduire les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"d":[{"n":"o","nm":"décalage","v":{"a":0,"k":2000,"ix":7}}],"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Groupe 1","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":43,"op":293,"st":43,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"Calque de forme 9","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":55,"s":[100]},{"t":61,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-319,272],[334,272]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.8509803921568627,0.8509803921568627,0.8509803921568627,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.365],"y":[1.004]},"o":{"x":[0.919],"y":[-0.004]},"t":46,"s":[100]},{"t":57,"s":[43]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1.002]},"o":{"x":[1],"y":[0]},"t":51,"s":[100]},{"t":61,"s":[43]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Réduire les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"d":[{"n":"o","nm":"décalage","v":{"a":0,"k":2000,"ix":7}}],"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Groupe 1","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":29,"op":279,"st":29,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Calque de forme 8","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":38,"s":[100]},{"t":44,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-319,272],[334,272]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.8509803921568627,0.8509803921568627,0.8509803921568627,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.365],"y":[1.004]},"o":{"x":[0.919],"y":[-0.004]},"t":29,"s":[100]},{"t":40,"s":[43]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1.002]},"o":{"x":[1],"y":[0]},"t":34,"s":[100]},{"t":44,"s":[43]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Réduire les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"d":[{"n":"o","nm":"décalage","v":{"a":0,"k":2000,"ix":7}}],"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Groupe 1","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":12,"op":262,"st":12,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Calque de forme 6","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":26,"s":[100]},{"t":32,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500,500,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-319,272],[334,272]],"c":false},"ix":2},"nm":"Tracé 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.8509803921568627,0.8509803921568627,0.8509803921568627,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":30,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fond 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Forme 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.365],"y":[1.004]},"o":{"x":[0.919],"y":[-0.004]},"t":17,"s":[100]},{"t":28,"s":[43]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[1.002]},"o":{"x":[1],"y":[0]},"t":22,"s":[100]},{"t":32,"s":[43]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Réduire les tracés 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"d":[{"n":"o","nm":"décalage","v":{"a":0,"k":2000,"ix":7}}],"nm":"Contour 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformer "}],"nm":"Groupe 1","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":250,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /ModerneShopping.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9DACF93326E0297B00AD45B7 /* ModerneShoppingApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF93226E0297B00AD45B7 /* ModerneShoppingApp.swift */; }; 11 | 9DACF93526E0297B00AD45B7 /* ProductView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF93426E0297B00AD45B7 /* ProductView.swift */; }; 12 | 9DACF93726E0297C00AD45B7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9DACF93626E0297C00AD45B7 /* Assets.xcassets */; }; 13 | 9DACF93A26E0297C00AD45B7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9DACF93926E0297C00AD45B7 /* Preview Assets.xcassets */; }; 14 | 9DACF94226E02A1B00AD45B7 /* Product.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF94126E02A1B00AD45B7 /* Product.swift */; }; 15 | 9DACF94526E02B2700AD45B7 /* Bundle+LoadAndDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF94426E02B2700AD45B7 /* Bundle+LoadAndDecode.swift */; }; 16 | 9DACF94726E02B7F00AD45B7 /* products.json in Resources */ = {isa = PBXBuildFile; fileRef = 9DACF94626E02B7F00AD45B7 /* products.json */; }; 17 | 9DACF94926E02E4400AD45B7 /* ImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF94826E02E4400AD45B7 /* ImageLoader.swift */; }; 18 | 9DACF94C26E0373900AD45B7 /* ShoppingButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF94B26E0373900AD45B7 /* ShoppingButtonStyle.swift */; }; 19 | 9DACF94E26E147C600AD45B7 /* ProductList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF94D26E147C600AD45B7 /* ProductList.swift */; }; 20 | 9DACF95026E1494B00AD45B7 /* ProductListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF94F26E1494B00AD45B7 /* ProductListItem.swift */; }; 21 | 9DACF95426E14C2C00AD45B7 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF95326E14C2C00AD45B7 /* HomeView.swift */; }; 22 | 9DACF95726E1A41600AD45B7 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF95626E1A41600AD45B7 /* MainView.swift */; }; 23 | 9DACF95A26E1C4D300AD45B7 /* CartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF95926E1C4D300AD45B7 /* CartView.swift */; }; 24 | 9DACF95C26E1C54700AD45B7 /* CartListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF95B26E1C54700AD45B7 /* CartListItem.swift */; }; 25 | 9DACF95E26E1C55400AD45B7 /* CartListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF95D26E1C55400AD45B7 /* CartListView.swift */; }; 26 | 9DACF96226E1CA6500AD45B7 /* ProductListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF96126E1CA6500AD45B7 /* ProductListViewModel.swift */; }; 27 | 9DACF96426E1CBEA00AD45B7 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF96326E1CBEA00AD45B7 /* LoadingView.swift */; }; 28 | 9DACF96826E1CEE700AD45B7 /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF96726E1CEE700AD45B7 /* CustomPicker.swift */; }; 29 | 9DACF96A26E1DB6400AD45B7 /* CartViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF96926E1DB6400AD45B7 /* CartViewModel.swift */; }; 30 | 9DACF97026E27D3B00AD45B7 /* Extension+Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF96F26E27D3B00AD45B7 /* Extension+Colors.swift */; }; 31 | 9DACF99226E5AA9300AD45B7 /* ProfilView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF99126E5AA9300AD45B7 /* ProfilView.swift */; }; 32 | 9DACF99626E5C95500AD45B7 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF99526E5C95500AD45B7 /* User.swift */; }; 33 | 9DACF99926E670BB00AD45B7 /* CartLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF99826E670BB00AD45B7 /* CartLoading.swift */; }; 34 | 9DACF99C26E6710700AD45B7 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 9DACF99B26E6710700AD45B7 /* Lottie */; }; 35 | 9DACF99E26E6712300AD45B7 /* cartloading.json in Resources */ = {isa = PBXBuildFile; fileRef = 9DACF99D26E6712300AD45B7 /* cartloading.json */; }; 36 | 9DACF9A026E6715600AD45B7 /* CartLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF99F26E6715600AD45B7 /* CartLoadingView.swift */; }; 37 | 9DACF9A326E6729800AD45B7 /* ProductLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9A226E6729800AD45B7 /* ProductLoading.swift */; }; 38 | 9DACF9A526E672C600AD45B7 /* productLoading.json in Resources */ = {isa = PBXBuildFile; fileRef = 9DACF9A426E672C600AD45B7 /* productLoading.json */; }; 39 | 9DACF9A726E6757800AD45B7 /* UserViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9A626E6757800AD45B7 /* UserViewModel.swift */; }; 40 | 9DACF9AA26E677CA00AD45B7 /* LoggedInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9A926E677CA00AD45B7 /* LoggedInView.swift */; }; 41 | 9DACF9AC26E6E88100AD45B7 /* api.json in Resources */ = {isa = PBXBuildFile; fileRef = 9DACF9AB26E6E88100AD45B7 /* api.json */; }; 42 | 9DACF9AE26E6F09400AD45B7 /* ProfilButtonMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9AD26E6F09400AD45B7 /* ProfilButtonMenu.swift */; }; 43 | 9DACF9B026E7139400AD45B7 /* ProfilButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9AF26E7139400AD45B7 /* ProfilButtons.swift */; }; 44 | 9DACF9B226E7E96600AD45B7 /* CheckOutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9B126E7E96600AD45B7 /* CheckOutView.swift */; }; 45 | 9DACF9B426E940E600AD45B7 /* ProductCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9B326E940E600AD45B7 /* ProductCarousel.swift */; }; 46 | 9DACF9B626E9410F00AD45B7 /* ProductCarouselCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9B526E9410F00AD45B7 /* ProductCarouselCard.swift */; }; 47 | 9DACF9B826E99DFB00AD45B7 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9B726E99DFB00AD45B7 /* LoginView.swift */; }; 48 | 9DACF9BD26E9B26200AD45B7 /* LoginLottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9BC26E9B26200AD45B7 /* LoginLottieView.swift */; }; 49 | 9DACF9C326EA55FE00AD45B7 /* shoppingCart.json in Resources */ = {isa = PBXBuildFile; fileRef = 9DACF9C226EA55FE00AD45B7 /* shoppingCart.json */; }; 50 | 9DACF9ED26EBA9A000AD45B7 /* ModerneShoppingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9EC26EBA9A000AD45B7 /* ModerneShoppingTests.swift */; }; 51 | 9DACF9F526EBA9DF00AD45B7 /* ProductListObjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9F426EBA9DF00AD45B7 /* ProductListObjectTest.swift */; }; 52 | 9DACF9F826EBAAB900AD45B7 /* MockAPIServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9F726EBAAB900AD45B7 /* MockAPIServices.swift */; }; 53 | 9DACFA0026EBB1EC00AD45B7 /* ModerneShoppingUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF9FF26EBB1EC00AD45B7 /* ModerneShoppingUITests.swift */; }; 54 | 9DACFA0826ED38F000AD45B7 /* FillAndStrokeShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACFA0726ED38F000AD45B7 /* FillAndStrokeShape.swift */; }; 55 | 9DFC832126EDBF36003EE456 /* products.json in Resources */ = {isa = PBXBuildFile; fileRef = 9DACF94626E02B7F00AD45B7 /* products.json */; }; 56 | 9DFC832526EDC280003EE456 /* APIServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DACF95F26E1C78100AD45B7 /* APIServices.swift */; }; 57 | 9DFC832926EE53DF003EE456 /* UserAPITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DFC832826EE53DF003EE456 /* UserAPITest.swift */; }; 58 | /* End PBXBuildFile section */ 59 | 60 | /* Begin PBXContainerItemProxy section */ 61 | 9DACF9EF26EBA9A000AD45B7 /* PBXContainerItemProxy */ = { 62 | isa = PBXContainerItemProxy; 63 | containerPortal = 9DACF92726E0297B00AD45B7 /* Project object */; 64 | proxyType = 1; 65 | remoteGlobalIDString = 9DACF92E26E0297B00AD45B7; 66 | remoteInfo = ModerneShopping; 67 | }; 68 | 9DACFA0226EBB1EC00AD45B7 /* PBXContainerItemProxy */ = { 69 | isa = PBXContainerItemProxy; 70 | containerPortal = 9DACF92726E0297B00AD45B7 /* Project object */; 71 | proxyType = 1; 72 | remoteGlobalIDString = 9DACF92E26E0297B00AD45B7; 73 | remoteInfo = ModerneShopping; 74 | }; 75 | /* End PBXContainerItemProxy section */ 76 | 77 | /* Begin PBXFileReference section */ 78 | 9DACF92F26E0297B00AD45B7 /* ModerneShopping.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ModerneShopping.app; sourceTree = BUILT_PRODUCTS_DIR; }; 79 | 9DACF93226E0297B00AD45B7 /* ModerneShoppingApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModerneShoppingApp.swift; sourceTree = ""; }; 80 | 9DACF93426E0297B00AD45B7 /* ProductView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductView.swift; sourceTree = ""; }; 81 | 9DACF93626E0297C00AD45B7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 82 | 9DACF93926E0297C00AD45B7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 83 | 9DACF93B26E0297C00AD45B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 84 | 9DACF94126E02A1B00AD45B7 /* Product.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Product.swift; sourceTree = ""; }; 85 | 9DACF94426E02B2700AD45B7 /* Bundle+LoadAndDecode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+LoadAndDecode.swift"; sourceTree = ""; }; 86 | 9DACF94626E02B7F00AD45B7 /* products.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = products.json; sourceTree = ""; }; 87 | 9DACF94826E02E4400AD45B7 /* ImageLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageLoader.swift; sourceTree = ""; }; 88 | 9DACF94B26E0373900AD45B7 /* ShoppingButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShoppingButtonStyle.swift; sourceTree = ""; }; 89 | 9DACF94D26E147C600AD45B7 /* ProductList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductList.swift; sourceTree = ""; }; 90 | 9DACF94F26E1494B00AD45B7 /* ProductListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductListItem.swift; sourceTree = ""; }; 91 | 9DACF95326E14C2C00AD45B7 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; 92 | 9DACF95626E1A41600AD45B7 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; 93 | 9DACF95926E1C4D300AD45B7 /* CartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartView.swift; sourceTree = ""; }; 94 | 9DACF95B26E1C54700AD45B7 /* CartListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartListItem.swift; sourceTree = ""; }; 95 | 9DACF95D26E1C55400AD45B7 /* CartListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartListView.swift; sourceTree = ""; }; 96 | 9DACF95F26E1C78100AD45B7 /* APIServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIServices.swift; sourceTree = ""; }; 97 | 9DACF96126E1CA6500AD45B7 /* ProductListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductListViewModel.swift; sourceTree = ""; }; 98 | 9DACF96326E1CBEA00AD45B7 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = ""; }; 99 | 9DACF96726E1CEE700AD45B7 /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = ""; }; 100 | 9DACF96926E1DB6400AD45B7 /* CartViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartViewModel.swift; sourceTree = ""; }; 101 | 9DACF96F26E27D3B00AD45B7 /* Extension+Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+Colors.swift"; sourceTree = ""; }; 102 | 9DACF99126E5AA9300AD45B7 /* ProfilView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilView.swift; sourceTree = ""; }; 103 | 9DACF99526E5C95500AD45B7 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 104 | 9DACF99826E670BB00AD45B7 /* CartLoading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartLoading.swift; sourceTree = ""; }; 105 | 9DACF99D26E6712300AD45B7 /* cartloading.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = cartloading.json; sourceTree = ""; }; 106 | 9DACF99F26E6715600AD45B7 /* CartLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartLoadingView.swift; sourceTree = ""; }; 107 | 9DACF9A226E6729800AD45B7 /* ProductLoading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductLoading.swift; sourceTree = ""; }; 108 | 9DACF9A426E672C600AD45B7 /* productLoading.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = productLoading.json; sourceTree = ""; }; 109 | 9DACF9A626E6757800AD45B7 /* UserViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserViewModel.swift; sourceTree = ""; }; 110 | 9DACF9A926E677CA00AD45B7 /* LoggedInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedInView.swift; sourceTree = ""; }; 111 | 9DACF9AB26E6E88100AD45B7 /* api.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = api.json; sourceTree = ""; }; 112 | 9DACF9AD26E6F09400AD45B7 /* ProfilButtonMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilButtonMenu.swift; sourceTree = ""; }; 113 | 9DACF9AF26E7139400AD45B7 /* ProfilButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilButtons.swift; sourceTree = ""; }; 114 | 9DACF9B126E7E96600AD45B7 /* CheckOutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckOutView.swift; sourceTree = ""; }; 115 | 9DACF9B326E940E600AD45B7 /* ProductCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductCarousel.swift; sourceTree = ""; }; 116 | 9DACF9B526E9410F00AD45B7 /* ProductCarouselCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductCarouselCard.swift; sourceTree = ""; }; 117 | 9DACF9B726E99DFB00AD45B7 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; 118 | 9DACF9BC26E9B26200AD45B7 /* LoginLottieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginLottieView.swift; sourceTree = ""; }; 119 | 9DACF9C226EA55FE00AD45B7 /* shoppingCart.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = shoppingCart.json; sourceTree = ""; }; 120 | 9DACF9EA26EBA9A000AD45B7 /* ModerneShoppingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ModerneShoppingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 121 | 9DACF9EC26EBA9A000AD45B7 /* ModerneShoppingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModerneShoppingTests.swift; sourceTree = ""; }; 122 | 9DACF9EE26EBA9A000AD45B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 123 | 9DACF9F426EBA9DF00AD45B7 /* ProductListObjectTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductListObjectTest.swift; sourceTree = ""; }; 124 | 9DACF9F726EBAAB900AD45B7 /* MockAPIServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAPIServices.swift; sourceTree = ""; }; 125 | 9DACF9FD26EBB1EC00AD45B7 /* ModerneShoppingUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ModerneShoppingUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 126 | 9DACF9FF26EBB1EC00AD45B7 /* ModerneShoppingUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModerneShoppingUITests.swift; sourceTree = ""; }; 127 | 9DACFA0126EBB1EC00AD45B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 128 | 9DACFA0726ED38F000AD45B7 /* FillAndStrokeShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FillAndStrokeShape.swift; sourceTree = ""; }; 129 | 9DFC832826EE53DF003EE456 /* UserAPITest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAPITest.swift; sourceTree = ""; }; 130 | /* End PBXFileReference section */ 131 | 132 | /* Begin PBXFrameworksBuildPhase section */ 133 | 9DACF92C26E0297B00AD45B7 /* Frameworks */ = { 134 | isa = PBXFrameworksBuildPhase; 135 | buildActionMask = 2147483647; 136 | files = ( 137 | 9DACF99C26E6710700AD45B7 /* Lottie in Frameworks */, 138 | ); 139 | runOnlyForDeploymentPostprocessing = 0; 140 | }; 141 | 9DACF9E726EBA9A000AD45B7 /* Frameworks */ = { 142 | isa = PBXFrameworksBuildPhase; 143 | buildActionMask = 2147483647; 144 | files = ( 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | }; 148 | 9DACF9FA26EBB1EC00AD45B7 /* Frameworks */ = { 149 | isa = PBXFrameworksBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | /* End PBXFrameworksBuildPhase section */ 156 | 157 | /* Begin PBXGroup section */ 158 | 9DACF92626E0297B00AD45B7 = { 159 | isa = PBXGroup; 160 | children = ( 161 | 9DACF93126E0297B00AD45B7 /* ModerneShopping */, 162 | 9DACF9EB26EBA9A000AD45B7 /* ModerneShoppingTests */, 163 | 9DACF9FE26EBB1EC00AD45B7 /* ModerneShoppingUITests */, 164 | 9DACF93026E0297B00AD45B7 /* Products */, 165 | ); 166 | sourceTree = ""; 167 | }; 168 | 9DACF93026E0297B00AD45B7 /* Products */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 9DACF92F26E0297B00AD45B7 /* ModerneShopping.app */, 172 | 9DACF9EA26EBA9A000AD45B7 /* ModerneShoppingTests.xctest */, 173 | 9DACF9FD26EBB1EC00AD45B7 /* ModerneShoppingUITests.xctest */, 174 | ); 175 | name = Products; 176 | sourceTree = ""; 177 | }; 178 | 9DACF93126E0297B00AD45B7 /* ModerneShopping */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | 9DACF93226E0297B00AD45B7 /* ModerneShoppingApp.swift */, 182 | 9DACF96526E1CEC500AD45B7 /* Views */, 183 | 9DACF95826E1C49500AD45B7 /* ViewModel */, 184 | 9DACF95526E1516300AD45B7 /* Model */, 185 | 9DACF94A26E0372200AD45B7 /* Utilities */, 186 | 9DACF94326E02B1100AD45B7 /* Extensions */, 187 | 9DACF93626E0297C00AD45B7 /* Assets.xcassets */, 188 | 9DACF9AB26E6E88100AD45B7 /* api.json */, 189 | 9DACF94626E02B7F00AD45B7 /* products.json */, 190 | 9DACF93B26E0297C00AD45B7 /* Info.plist */, 191 | 9DACF93826E0297C00AD45B7 /* Preview Content */, 192 | ); 193 | path = ModerneShopping; 194 | sourceTree = ""; 195 | }; 196 | 9DACF93826E0297C00AD45B7 /* Preview Content */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | 9DACF93926E0297C00AD45B7 /* Preview Assets.xcassets */, 200 | ); 201 | path = "Preview Content"; 202 | sourceTree = ""; 203 | }; 204 | 9DACF94326E02B1100AD45B7 /* Extensions */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | 9DACF94426E02B2700AD45B7 /* Bundle+LoadAndDecode.swift */, 208 | 9DACF96F26E27D3B00AD45B7 /* Extension+Colors.swift */, 209 | 9DACF94826E02E4400AD45B7 /* ImageLoader.swift */, 210 | ); 211 | path = Extensions; 212 | sourceTree = ""; 213 | }; 214 | 9DACF94A26E0372200AD45B7 /* Utilities */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | 9DACF96726E1CEE700AD45B7 /* CustomPicker.swift */, 218 | 9DACF94B26E0373900AD45B7 /* ShoppingButtonStyle.swift */, 219 | 9DACF9AD26E6F09400AD45B7 /* ProfilButtonMenu.swift */, 220 | ); 221 | path = Utilities; 222 | sourceTree = ""; 223 | }; 224 | 9DACF95526E1516300AD45B7 /* Model */ = { 225 | isa = PBXGroup; 226 | children = ( 227 | 9DACF94126E02A1B00AD45B7 /* Product.swift */, 228 | 9DACF99526E5C95500AD45B7 /* User.swift */, 229 | ); 230 | path = Model; 231 | sourceTree = ""; 232 | }; 233 | 9DACF95826E1C49500AD45B7 /* ViewModel */ = { 234 | isa = PBXGroup; 235 | children = ( 236 | 9DACF95F26E1C78100AD45B7 /* APIServices.swift */, 237 | 9DACF96126E1CA6500AD45B7 /* ProductListViewModel.swift */, 238 | 9DACF96926E1DB6400AD45B7 /* CartViewModel.swift */, 239 | 9DACF9A626E6757800AD45B7 /* UserViewModel.swift */, 240 | ); 241 | path = ViewModel; 242 | sourceTree = ""; 243 | }; 244 | 9DACF96526E1CEC500AD45B7 /* Views */ = { 245 | isa = PBXGroup; 246 | children = ( 247 | 9DACF95626E1A41600AD45B7 /* MainView.swift */, 248 | 9DACF95326E14C2C00AD45B7 /* HomeView.swift */, 249 | 9DACF99126E5AA9300AD45B7 /* ProfilView.swift */, 250 | 9DACF9A826E677A700AD45B7 /* ProfilViews */, 251 | 9DACF99026E5AA8100AD45B7 /* ProductViews */, 252 | 9DACF96626E1CECE00AD45B7 /* CartViews */, 253 | ); 254 | path = Views; 255 | sourceTree = ""; 256 | }; 257 | 9DACF96626E1CECE00AD45B7 /* CartViews */ = { 258 | isa = PBXGroup; 259 | children = ( 260 | 9DACF95926E1C4D300AD45B7 /* CartView.swift */, 261 | 9DACFA0726ED38F000AD45B7 /* FillAndStrokeShape.swift */, 262 | 9DACF95D26E1C55400AD45B7 /* CartListView.swift */, 263 | 9DACF95B26E1C54700AD45B7 /* CartListItem.swift */, 264 | 9DACF99F26E6715600AD45B7 /* CartLoadingView.swift */, 265 | 9DACF9B126E7E96600AD45B7 /* CheckOutView.swift */, 266 | 9DACF99726E670AE00AD45B7 /* LottieFiles */, 267 | ); 268 | path = CartViews; 269 | sourceTree = ""; 270 | }; 271 | 9DACF99026E5AA8100AD45B7 /* ProductViews */ = { 272 | isa = PBXGroup; 273 | children = ( 274 | 9DACF94D26E147C600AD45B7 /* ProductList.swift */, 275 | 9DACF94F26E1494B00AD45B7 /* ProductListItem.swift */, 276 | 9DACF93426E0297B00AD45B7 /* ProductView.swift */, 277 | 9DACF9B326E940E600AD45B7 /* ProductCarousel.swift */, 278 | 9DACF9B526E9410F00AD45B7 /* ProductCarouselCard.swift */, 279 | 9DACF96326E1CBEA00AD45B7 /* LoadingView.swift */, 280 | 9DACF9A126E6727F00AD45B7 /* LottieFiles */, 281 | ); 282 | path = ProductViews; 283 | sourceTree = ""; 284 | }; 285 | 9DACF99726E670AE00AD45B7 /* LottieFiles */ = { 286 | isa = PBXGroup; 287 | children = ( 288 | 9DACF99826E670BB00AD45B7 /* CartLoading.swift */, 289 | 9DACF99D26E6712300AD45B7 /* cartloading.json */, 290 | ); 291 | path = LottieFiles; 292 | sourceTree = ""; 293 | }; 294 | 9DACF9A126E6727F00AD45B7 /* LottieFiles */ = { 295 | isa = PBXGroup; 296 | children = ( 297 | 9DACF9A226E6729800AD45B7 /* ProductLoading.swift */, 298 | 9DACF9A426E672C600AD45B7 /* productLoading.json */, 299 | ); 300 | path = LottieFiles; 301 | sourceTree = ""; 302 | }; 303 | 9DACF9A826E677A700AD45B7 /* ProfilViews */ = { 304 | isa = PBXGroup; 305 | children = ( 306 | 9DACF9B726E99DFB00AD45B7 /* LoginView.swift */, 307 | 9DACF9A926E677CA00AD45B7 /* LoggedInView.swift */, 308 | 9DACF9AF26E7139400AD45B7 /* ProfilButtons.swift */, 309 | 9DACF9B926E9B23B00AD45B7 /* LottieFile */, 310 | ); 311 | path = ProfilViews; 312 | sourceTree = ""; 313 | }; 314 | 9DACF9B926E9B23B00AD45B7 /* LottieFile */ = { 315 | isa = PBXGroup; 316 | children = ( 317 | 9DACF9C226EA55FE00AD45B7 /* shoppingCart.json */, 318 | 9DACF9BC26E9B26200AD45B7 /* LoginLottieView.swift */, 319 | ); 320 | path = LottieFile; 321 | sourceTree = ""; 322 | }; 323 | 9DACF9EB26EBA9A000AD45B7 /* ModerneShoppingTests */ = { 324 | isa = PBXGroup; 325 | children = ( 326 | 9DACF9EC26EBA9A000AD45B7 /* ModerneShoppingTests.swift */, 327 | 9DACF9F426EBA9DF00AD45B7 /* ProductListObjectTest.swift */, 328 | 9DFC832826EE53DF003EE456 /* UserAPITest.swift */, 329 | 9DACF9F726EBAAB900AD45B7 /* MockAPIServices.swift */, 330 | 9DACF9EE26EBA9A000AD45B7 /* Info.plist */, 331 | ); 332 | path = ModerneShoppingTests; 333 | sourceTree = ""; 334 | }; 335 | 9DACF9FE26EBB1EC00AD45B7 /* ModerneShoppingUITests */ = { 336 | isa = PBXGroup; 337 | children = ( 338 | 9DACF9FF26EBB1EC00AD45B7 /* ModerneShoppingUITests.swift */, 339 | 9DACFA0126EBB1EC00AD45B7 /* Info.plist */, 340 | ); 341 | path = ModerneShoppingUITests; 342 | sourceTree = ""; 343 | }; 344 | /* End PBXGroup section */ 345 | 346 | /* Begin PBXNativeTarget section */ 347 | 9DACF92E26E0297B00AD45B7 /* ModerneShopping */ = { 348 | isa = PBXNativeTarget; 349 | buildConfigurationList = 9DACF93E26E0297C00AD45B7 /* Build configuration list for PBXNativeTarget "ModerneShopping" */; 350 | buildPhases = ( 351 | 9DACF92B26E0297B00AD45B7 /* Sources */, 352 | 9DACF92C26E0297B00AD45B7 /* Frameworks */, 353 | 9DACF92D26E0297B00AD45B7 /* Resources */, 354 | ); 355 | buildRules = ( 356 | ); 357 | dependencies = ( 358 | ); 359 | name = ModerneShopping; 360 | packageProductDependencies = ( 361 | 9DACF99B26E6710700AD45B7 /* Lottie */, 362 | ); 363 | productName = ModerneShopping; 364 | productReference = 9DACF92F26E0297B00AD45B7 /* ModerneShopping.app */; 365 | productType = "com.apple.product-type.application"; 366 | }; 367 | 9DACF9E926EBA9A000AD45B7 /* ModerneShoppingTests */ = { 368 | isa = PBXNativeTarget; 369 | buildConfigurationList = 9DACF9F126EBA9A000AD45B7 /* Build configuration list for PBXNativeTarget "ModerneShoppingTests" */; 370 | buildPhases = ( 371 | 9DACF9E626EBA9A000AD45B7 /* Sources */, 372 | 9DACF9E726EBA9A000AD45B7 /* Frameworks */, 373 | 9DACF9E826EBA9A000AD45B7 /* Resources */, 374 | ); 375 | buildRules = ( 376 | ); 377 | dependencies = ( 378 | 9DACF9F026EBA9A000AD45B7 /* PBXTargetDependency */, 379 | ); 380 | name = ModerneShoppingTests; 381 | productName = ModerneShoppingTests; 382 | productReference = 9DACF9EA26EBA9A000AD45B7 /* ModerneShoppingTests.xctest */; 383 | productType = "com.apple.product-type.bundle.unit-test"; 384 | }; 385 | 9DACF9FC26EBB1EC00AD45B7 /* ModerneShoppingUITests */ = { 386 | isa = PBXNativeTarget; 387 | buildConfigurationList = 9DACFA0426EBB1EC00AD45B7 /* Build configuration list for PBXNativeTarget "ModerneShoppingUITests" */; 388 | buildPhases = ( 389 | 9DACF9F926EBB1EC00AD45B7 /* Sources */, 390 | 9DACF9FA26EBB1EC00AD45B7 /* Frameworks */, 391 | 9DACF9FB26EBB1EC00AD45B7 /* Resources */, 392 | ); 393 | buildRules = ( 394 | ); 395 | dependencies = ( 396 | 9DACFA0326EBB1EC00AD45B7 /* PBXTargetDependency */, 397 | ); 398 | name = ModerneShoppingUITests; 399 | productName = ModerneShoppingUITests; 400 | productReference = 9DACF9FD26EBB1EC00AD45B7 /* ModerneShoppingUITests.xctest */; 401 | productType = "com.apple.product-type.bundle.ui-testing"; 402 | }; 403 | /* End PBXNativeTarget section */ 404 | 405 | /* Begin PBXProject section */ 406 | 9DACF92726E0297B00AD45B7 /* Project object */ = { 407 | isa = PBXProject; 408 | attributes = { 409 | LastSwiftUpdateCheck = 1250; 410 | LastUpgradeCheck = 1250; 411 | TargetAttributes = { 412 | 9DACF92E26E0297B00AD45B7 = { 413 | CreatedOnToolsVersion = 12.5.1; 414 | }; 415 | 9DACF9E926EBA9A000AD45B7 = { 416 | CreatedOnToolsVersion = 12.5.1; 417 | TestTargetID = 9DACF92E26E0297B00AD45B7; 418 | }; 419 | 9DACF9FC26EBB1EC00AD45B7 = { 420 | CreatedOnToolsVersion = 12.5.1; 421 | TestTargetID = 9DACF92E26E0297B00AD45B7; 422 | }; 423 | }; 424 | }; 425 | buildConfigurationList = 9DACF92A26E0297B00AD45B7 /* Build configuration list for PBXProject "ModerneShopping" */; 426 | compatibilityVersion = "Xcode 9.3"; 427 | developmentRegion = en; 428 | hasScannedForEncodings = 0; 429 | knownRegions = ( 430 | en, 431 | Base, 432 | ); 433 | mainGroup = 9DACF92626E0297B00AD45B7; 434 | packageReferences = ( 435 | 9DACF99A26E6710700AD45B7 /* XCRemoteSwiftPackageReference "lottie-ios" */, 436 | ); 437 | productRefGroup = 9DACF93026E0297B00AD45B7 /* Products */; 438 | projectDirPath = ""; 439 | projectRoot = ""; 440 | targets = ( 441 | 9DACF92E26E0297B00AD45B7 /* ModerneShopping */, 442 | 9DACF9E926EBA9A000AD45B7 /* ModerneShoppingTests */, 443 | 9DACF9FC26EBB1EC00AD45B7 /* ModerneShoppingUITests */, 444 | ); 445 | }; 446 | /* End PBXProject section */ 447 | 448 | /* Begin PBXResourcesBuildPhase section */ 449 | 9DACF92D26E0297B00AD45B7 /* Resources */ = { 450 | isa = PBXResourcesBuildPhase; 451 | buildActionMask = 2147483647; 452 | files = ( 453 | 9DACF94726E02B7F00AD45B7 /* products.json in Resources */, 454 | 9DACF9AC26E6E88100AD45B7 /* api.json in Resources */, 455 | 9DACF93A26E0297C00AD45B7 /* Preview Assets.xcassets in Resources */, 456 | 9DACF9A526E672C600AD45B7 /* productLoading.json in Resources */, 457 | 9DACF9C326EA55FE00AD45B7 /* shoppingCart.json in Resources */, 458 | 9DACF93726E0297C00AD45B7 /* Assets.xcassets in Resources */, 459 | 9DACF99E26E6712300AD45B7 /* cartloading.json in Resources */, 460 | ); 461 | runOnlyForDeploymentPostprocessing = 0; 462 | }; 463 | 9DACF9E826EBA9A000AD45B7 /* Resources */ = { 464 | isa = PBXResourcesBuildPhase; 465 | buildActionMask = 2147483647; 466 | files = ( 467 | 9DFC832126EDBF36003EE456 /* products.json in Resources */, 468 | ); 469 | runOnlyForDeploymentPostprocessing = 0; 470 | }; 471 | 9DACF9FB26EBB1EC00AD45B7 /* Resources */ = { 472 | isa = PBXResourcesBuildPhase; 473 | buildActionMask = 2147483647; 474 | files = ( 475 | ); 476 | runOnlyForDeploymentPostprocessing = 0; 477 | }; 478 | /* End PBXResourcesBuildPhase section */ 479 | 480 | /* Begin PBXSourcesBuildPhase section */ 481 | 9DACF92B26E0297B00AD45B7 /* Sources */ = { 482 | isa = PBXSourcesBuildPhase; 483 | buildActionMask = 2147483647; 484 | files = ( 485 | 9DACF95426E14C2C00AD45B7 /* HomeView.swift in Sources */, 486 | 9DACF94226E02A1B00AD45B7 /* Product.swift in Sources */, 487 | 9DACF96826E1CEE700AD45B7 /* CustomPicker.swift in Sources */, 488 | 9DACF9B426E940E600AD45B7 /* ProductCarousel.swift in Sources */, 489 | 9DACF95E26E1C55400AD45B7 /* CartListView.swift in Sources */, 490 | 9DACF94C26E0373900AD45B7 /* ShoppingButtonStyle.swift in Sources */, 491 | 9DACF95726E1A41600AD45B7 /* MainView.swift in Sources */, 492 | 9DACF9AE26E6F09400AD45B7 /* ProfilButtonMenu.swift in Sources */, 493 | 9DACF99226E5AA9300AD45B7 /* ProfilView.swift in Sources */, 494 | 9DACF97026E27D3B00AD45B7 /* Extension+Colors.swift in Sources */, 495 | 9DACF96A26E1DB6400AD45B7 /* CartViewModel.swift in Sources */, 496 | 9DACF96426E1CBEA00AD45B7 /* LoadingView.swift in Sources */, 497 | 9DACF94526E02B2700AD45B7 /* Bundle+LoadAndDecode.swift in Sources */, 498 | 9DACF9AA26E677CA00AD45B7 /* LoggedInView.swift in Sources */, 499 | 9DACF94926E02E4400AD45B7 /* ImageLoader.swift in Sources */, 500 | 9DACF9B226E7E96600AD45B7 /* CheckOutView.swift in Sources */, 501 | 9DACFA0826ED38F000AD45B7 /* FillAndStrokeShape.swift in Sources */, 502 | 9DACF95A26E1C4D300AD45B7 /* CartView.swift in Sources */, 503 | 9DFC832526EDC280003EE456 /* APIServices.swift in Sources */, 504 | 9DACF95026E1494B00AD45B7 /* ProductListItem.swift in Sources */, 505 | 9DACF9A326E6729800AD45B7 /* ProductLoading.swift in Sources */, 506 | 9DACF9B026E7139400AD45B7 /* ProfilButtons.swift in Sources */, 507 | 9DACF94E26E147C600AD45B7 /* ProductList.swift in Sources */, 508 | 9DACF93526E0297B00AD45B7 /* ProductView.swift in Sources */, 509 | 9DACF96226E1CA6500AD45B7 /* ProductListViewModel.swift in Sources */, 510 | 9DACF95C26E1C54700AD45B7 /* CartListItem.swift in Sources */, 511 | 9DACF9A026E6715600AD45B7 /* CartLoadingView.swift in Sources */, 512 | 9DACF9B626E9410F00AD45B7 /* ProductCarouselCard.swift in Sources */, 513 | 9DACF9BD26E9B26200AD45B7 /* LoginLottieView.swift in Sources */, 514 | 9DACF93326E0297B00AD45B7 /* ModerneShoppingApp.swift in Sources */, 515 | 9DACF99926E670BB00AD45B7 /* CartLoading.swift in Sources */, 516 | 9DACF9A726E6757800AD45B7 /* UserViewModel.swift in Sources */, 517 | 9DACF9B826E99DFB00AD45B7 /* LoginView.swift in Sources */, 518 | 9DACF99626E5C95500AD45B7 /* User.swift in Sources */, 519 | ); 520 | runOnlyForDeploymentPostprocessing = 0; 521 | }; 522 | 9DACF9E626EBA9A000AD45B7 /* Sources */ = { 523 | isa = PBXSourcesBuildPhase; 524 | buildActionMask = 2147483647; 525 | files = ( 526 | 9DACF9F526EBA9DF00AD45B7 /* ProductListObjectTest.swift in Sources */, 527 | 9DFC832926EE53DF003EE456 /* UserAPITest.swift in Sources */, 528 | 9DACF9F826EBAAB900AD45B7 /* MockAPIServices.swift in Sources */, 529 | 9DACF9ED26EBA9A000AD45B7 /* ModerneShoppingTests.swift in Sources */, 530 | ); 531 | runOnlyForDeploymentPostprocessing = 0; 532 | }; 533 | 9DACF9F926EBB1EC00AD45B7 /* Sources */ = { 534 | isa = PBXSourcesBuildPhase; 535 | buildActionMask = 2147483647; 536 | files = ( 537 | 9DACFA0026EBB1EC00AD45B7 /* ModerneShoppingUITests.swift in Sources */, 538 | ); 539 | runOnlyForDeploymentPostprocessing = 0; 540 | }; 541 | /* End PBXSourcesBuildPhase section */ 542 | 543 | /* Begin PBXTargetDependency section */ 544 | 9DACF9F026EBA9A000AD45B7 /* PBXTargetDependency */ = { 545 | isa = PBXTargetDependency; 546 | target = 9DACF92E26E0297B00AD45B7 /* ModerneShopping */; 547 | targetProxy = 9DACF9EF26EBA9A000AD45B7 /* PBXContainerItemProxy */; 548 | }; 549 | 9DACFA0326EBB1EC00AD45B7 /* PBXTargetDependency */ = { 550 | isa = PBXTargetDependency; 551 | target = 9DACF92E26E0297B00AD45B7 /* ModerneShopping */; 552 | targetProxy = 9DACFA0226EBB1EC00AD45B7 /* PBXContainerItemProxy */; 553 | }; 554 | /* End PBXTargetDependency section */ 555 | 556 | /* Begin XCBuildConfiguration section */ 557 | 9DACF93C26E0297C00AD45B7 /* Debug */ = { 558 | isa = XCBuildConfiguration; 559 | buildSettings = { 560 | ALWAYS_SEARCH_USER_PATHS = NO; 561 | CLANG_ANALYZER_NONNULL = YES; 562 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 563 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 564 | CLANG_CXX_LIBRARY = "libc++"; 565 | CLANG_ENABLE_MODULES = YES; 566 | CLANG_ENABLE_OBJC_ARC = YES; 567 | CLANG_ENABLE_OBJC_WEAK = YES; 568 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 569 | CLANG_WARN_BOOL_CONVERSION = YES; 570 | CLANG_WARN_COMMA = YES; 571 | CLANG_WARN_CONSTANT_CONVERSION = YES; 572 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 573 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 574 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 575 | CLANG_WARN_EMPTY_BODY = YES; 576 | CLANG_WARN_ENUM_CONVERSION = YES; 577 | CLANG_WARN_INFINITE_RECURSION = YES; 578 | CLANG_WARN_INT_CONVERSION = YES; 579 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 580 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 581 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 582 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 583 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 584 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 585 | CLANG_WARN_STRICT_PROTOTYPES = YES; 586 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 587 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 588 | CLANG_WARN_UNREACHABLE_CODE = YES; 589 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 590 | COPY_PHASE_STRIP = NO; 591 | DEBUG_INFORMATION_FORMAT = dwarf; 592 | ENABLE_STRICT_OBJC_MSGSEND = YES; 593 | ENABLE_TESTABILITY = YES; 594 | GCC_C_LANGUAGE_STANDARD = gnu11; 595 | GCC_DYNAMIC_NO_PIC = NO; 596 | GCC_NO_COMMON_BLOCKS = YES; 597 | GCC_OPTIMIZATION_LEVEL = 0; 598 | GCC_PREPROCESSOR_DEFINITIONS = ( 599 | "DEBUG=1", 600 | "$(inherited)", 601 | ); 602 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 603 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 604 | GCC_WARN_UNDECLARED_SELECTOR = YES; 605 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 606 | GCC_WARN_UNUSED_FUNCTION = YES; 607 | GCC_WARN_UNUSED_VARIABLE = YES; 608 | IPHONEOS_DEPLOYMENT_TARGET = 14.1; 609 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 610 | MTL_FAST_MATH = YES; 611 | ONLY_ACTIVE_ARCH = YES; 612 | SDKROOT = iphoneos; 613 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 614 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 615 | }; 616 | name = Debug; 617 | }; 618 | 9DACF93D26E0297C00AD45B7 /* Release */ = { 619 | isa = XCBuildConfiguration; 620 | buildSettings = { 621 | ALWAYS_SEARCH_USER_PATHS = NO; 622 | CLANG_ANALYZER_NONNULL = YES; 623 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 624 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 625 | CLANG_CXX_LIBRARY = "libc++"; 626 | CLANG_ENABLE_MODULES = YES; 627 | CLANG_ENABLE_OBJC_ARC = YES; 628 | CLANG_ENABLE_OBJC_WEAK = YES; 629 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 630 | CLANG_WARN_BOOL_CONVERSION = YES; 631 | CLANG_WARN_COMMA = YES; 632 | CLANG_WARN_CONSTANT_CONVERSION = YES; 633 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 634 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 635 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 636 | CLANG_WARN_EMPTY_BODY = YES; 637 | CLANG_WARN_ENUM_CONVERSION = YES; 638 | CLANG_WARN_INFINITE_RECURSION = YES; 639 | CLANG_WARN_INT_CONVERSION = YES; 640 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 641 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 642 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 643 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 644 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 645 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 646 | CLANG_WARN_STRICT_PROTOTYPES = YES; 647 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 648 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 649 | CLANG_WARN_UNREACHABLE_CODE = YES; 650 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 651 | COPY_PHASE_STRIP = NO; 652 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 653 | ENABLE_NS_ASSERTIONS = NO; 654 | ENABLE_STRICT_OBJC_MSGSEND = YES; 655 | GCC_C_LANGUAGE_STANDARD = gnu11; 656 | GCC_NO_COMMON_BLOCKS = YES; 657 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 658 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 659 | GCC_WARN_UNDECLARED_SELECTOR = YES; 660 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 661 | GCC_WARN_UNUSED_FUNCTION = YES; 662 | GCC_WARN_UNUSED_VARIABLE = YES; 663 | IPHONEOS_DEPLOYMENT_TARGET = 14.1; 664 | MTL_ENABLE_DEBUG_INFO = NO; 665 | MTL_FAST_MATH = YES; 666 | SDKROOT = iphoneos; 667 | SWIFT_COMPILATION_MODE = wholemodule; 668 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 669 | VALIDATE_PRODUCT = YES; 670 | }; 671 | name = Release; 672 | }; 673 | 9DACF93F26E0297C00AD45B7 /* Debug */ = { 674 | isa = XCBuildConfiguration; 675 | buildSettings = { 676 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 677 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 678 | CODE_SIGN_STYLE = Automatic; 679 | DEVELOPMENT_ASSET_PATHS = "\"ModerneShopping/Preview Content\""; 680 | DEVELOPMENT_TEAM = PR37X8G2V5; 681 | ENABLE_PREVIEWS = YES; 682 | INFOPLIST_FILE = ModerneShopping/Info.plist; 683 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 684 | LD_RUNPATH_SEARCH_PATHS = ( 685 | "$(inherited)", 686 | "@executable_path/Frameworks", 687 | ); 688 | PRODUCT_BUNDLE_IDENTIFIER = com.djallilelkebir.ModerneShopping; 689 | PRODUCT_NAME = "$(TARGET_NAME)"; 690 | SWIFT_VERSION = 5.0; 691 | TARGETED_DEVICE_FAMILY = "1,2"; 692 | }; 693 | name = Debug; 694 | }; 695 | 9DACF94026E0297C00AD45B7 /* Release */ = { 696 | isa = XCBuildConfiguration; 697 | buildSettings = { 698 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 699 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 700 | CODE_SIGN_STYLE = Automatic; 701 | DEVELOPMENT_ASSET_PATHS = "\"ModerneShopping/Preview Content\""; 702 | DEVELOPMENT_TEAM = PR37X8G2V5; 703 | ENABLE_PREVIEWS = YES; 704 | INFOPLIST_FILE = ModerneShopping/Info.plist; 705 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 706 | LD_RUNPATH_SEARCH_PATHS = ( 707 | "$(inherited)", 708 | "@executable_path/Frameworks", 709 | ); 710 | PRODUCT_BUNDLE_IDENTIFIER = com.djallilelkebir.ModerneShopping; 711 | PRODUCT_NAME = "$(TARGET_NAME)"; 712 | SWIFT_VERSION = 5.0; 713 | TARGETED_DEVICE_FAMILY = "1,2"; 714 | }; 715 | name = Release; 716 | }; 717 | 9DACF9F226EBA9A000AD45B7 /* Debug */ = { 718 | isa = XCBuildConfiguration; 719 | buildSettings = { 720 | BUNDLE_LOADER = "$(TEST_HOST)"; 721 | CODE_SIGN_STYLE = Automatic; 722 | DEVELOPMENT_TEAM = PR37X8G2V5; 723 | INFOPLIST_FILE = ModerneShoppingTests/Info.plist; 724 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 725 | LD_RUNPATH_SEARCH_PATHS = ( 726 | "$(inherited)", 727 | "@executable_path/Frameworks", 728 | "@loader_path/Frameworks", 729 | ); 730 | PRODUCT_BUNDLE_IDENTIFIER = com.djallilelkebir.ModerneShoppingTests; 731 | PRODUCT_NAME = "$(TARGET_NAME)"; 732 | SWIFT_VERSION = 5.0; 733 | TARGETED_DEVICE_FAMILY = "1,2"; 734 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ModerneShopping.app/ModerneShopping"; 735 | }; 736 | name = Debug; 737 | }; 738 | 9DACF9F326EBA9A000AD45B7 /* Release */ = { 739 | isa = XCBuildConfiguration; 740 | buildSettings = { 741 | BUNDLE_LOADER = "$(TEST_HOST)"; 742 | CODE_SIGN_STYLE = Automatic; 743 | DEVELOPMENT_TEAM = PR37X8G2V5; 744 | INFOPLIST_FILE = ModerneShoppingTests/Info.plist; 745 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 746 | LD_RUNPATH_SEARCH_PATHS = ( 747 | "$(inherited)", 748 | "@executable_path/Frameworks", 749 | "@loader_path/Frameworks", 750 | ); 751 | PRODUCT_BUNDLE_IDENTIFIER = com.djallilelkebir.ModerneShoppingTests; 752 | PRODUCT_NAME = "$(TARGET_NAME)"; 753 | SWIFT_VERSION = 5.0; 754 | TARGETED_DEVICE_FAMILY = "1,2"; 755 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ModerneShopping.app/ModerneShopping"; 756 | }; 757 | name = Release; 758 | }; 759 | 9DACFA0526EBB1EC00AD45B7 /* Debug */ = { 760 | isa = XCBuildConfiguration; 761 | buildSettings = { 762 | CODE_SIGN_STYLE = Automatic; 763 | DEVELOPMENT_TEAM = PR37X8G2V5; 764 | INFOPLIST_FILE = ModerneShoppingUITests/Info.plist; 765 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 766 | LD_RUNPATH_SEARCH_PATHS = ( 767 | "$(inherited)", 768 | "@executable_path/Frameworks", 769 | "@loader_path/Frameworks", 770 | ); 771 | PRODUCT_BUNDLE_IDENTIFIER = com.djallilelkebir.ModerneShoppingUITests; 772 | PRODUCT_NAME = "$(TARGET_NAME)"; 773 | SWIFT_VERSION = 5.0; 774 | TARGETED_DEVICE_FAMILY = "1,2"; 775 | TEST_TARGET_NAME = ModerneShopping; 776 | }; 777 | name = Debug; 778 | }; 779 | 9DACFA0626EBB1EC00AD45B7 /* Release */ = { 780 | isa = XCBuildConfiguration; 781 | buildSettings = { 782 | CODE_SIGN_STYLE = Automatic; 783 | DEVELOPMENT_TEAM = PR37X8G2V5; 784 | INFOPLIST_FILE = ModerneShoppingUITests/Info.plist; 785 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 786 | LD_RUNPATH_SEARCH_PATHS = ( 787 | "$(inherited)", 788 | "@executable_path/Frameworks", 789 | "@loader_path/Frameworks", 790 | ); 791 | PRODUCT_BUNDLE_IDENTIFIER = com.djallilelkebir.ModerneShoppingUITests; 792 | PRODUCT_NAME = "$(TARGET_NAME)"; 793 | SWIFT_VERSION = 5.0; 794 | TARGETED_DEVICE_FAMILY = "1,2"; 795 | TEST_TARGET_NAME = ModerneShopping; 796 | }; 797 | name = Release; 798 | }; 799 | /* End XCBuildConfiguration section */ 800 | 801 | /* Begin XCConfigurationList section */ 802 | 9DACF92A26E0297B00AD45B7 /* Build configuration list for PBXProject "ModerneShopping" */ = { 803 | isa = XCConfigurationList; 804 | buildConfigurations = ( 805 | 9DACF93C26E0297C00AD45B7 /* Debug */, 806 | 9DACF93D26E0297C00AD45B7 /* Release */, 807 | ); 808 | defaultConfigurationIsVisible = 0; 809 | defaultConfigurationName = Release; 810 | }; 811 | 9DACF93E26E0297C00AD45B7 /* Build configuration list for PBXNativeTarget "ModerneShopping" */ = { 812 | isa = XCConfigurationList; 813 | buildConfigurations = ( 814 | 9DACF93F26E0297C00AD45B7 /* Debug */, 815 | 9DACF94026E0297C00AD45B7 /* Release */, 816 | ); 817 | defaultConfigurationIsVisible = 0; 818 | defaultConfigurationName = Release; 819 | }; 820 | 9DACF9F126EBA9A000AD45B7 /* Build configuration list for PBXNativeTarget "ModerneShoppingTests" */ = { 821 | isa = XCConfigurationList; 822 | buildConfigurations = ( 823 | 9DACF9F226EBA9A000AD45B7 /* Debug */, 824 | 9DACF9F326EBA9A000AD45B7 /* Release */, 825 | ); 826 | defaultConfigurationIsVisible = 0; 827 | defaultConfigurationName = Release; 828 | }; 829 | 9DACFA0426EBB1EC00AD45B7 /* Build configuration list for PBXNativeTarget "ModerneShoppingUITests" */ = { 830 | isa = XCConfigurationList; 831 | buildConfigurations = ( 832 | 9DACFA0526EBB1EC00AD45B7 /* Debug */, 833 | 9DACFA0626EBB1EC00AD45B7 /* Release */, 834 | ); 835 | defaultConfigurationIsVisible = 0; 836 | defaultConfigurationName = Release; 837 | }; 838 | /* End XCConfigurationList section */ 839 | 840 | /* Begin XCRemoteSwiftPackageReference section */ 841 | 9DACF99A26E6710700AD45B7 /* XCRemoteSwiftPackageReference "lottie-ios" */ = { 842 | isa = XCRemoteSwiftPackageReference; 843 | repositoryURL = "https://github.com/airbnb/lottie-ios"; 844 | requirement = { 845 | kind = upToNextMajorVersion; 846 | minimumVersion = 3.2.3; 847 | }; 848 | }; 849 | /* End XCRemoteSwiftPackageReference section */ 850 | 851 | /* Begin XCSwiftPackageProductDependency section */ 852 | 9DACF99B26E6710700AD45B7 /* Lottie */ = { 853 | isa = XCSwiftPackageProductDependency; 854 | package = 9DACF99A26E6710700AD45B7 /* XCRemoteSwiftPackageReference "lottie-ios" */; 855 | productName = Lottie; 856 | }; 857 | /* End XCSwiftPackageProductDependency section */ 858 | }; 859 | rootObject = 9DACF92726E0297B00AD45B7 /* Project object */; 860 | } 861 | --------------------------------------------------------------------------------