├── 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 | 
15 | 
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 |
--------------------------------------------------------------------------------