├── .gitignore
├── App Clip
├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
├── Info.plist
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
└── ScooterRentalAppClip.entitlements
├── App
├── Info.plist
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
└── ScooterRentalApp.entitlements
├── Assets
└── header.png
├── README.md
├── ScooterRentalApp.swift
├── ScooterRentalApp.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── xcshareddata
│ └── xcschemes
│ ├── Scooter Rental App Clip.xcscheme
│ ├── Scooter Rental App Widget Extension.xcscheme
│ └── Scooter Rental App.xcscheme
├── ScooterRentalAppWidgetExtensionExtension.entitlements
├── Shared
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── icon-ios-1024@1x.png
│ │ ├── icon-ios-20@2x.png
│ │ ├── icon-ios-20@3x.png
│ │ ├── icon-ios-29@2x.png
│ │ ├── icon-ios-29@3x.png
│ │ ├── icon-ios-40@2x.png
│ │ ├── icon-ios-40@3x.png
│ │ ├── icon-ios-60@2x.png
│ │ └── icon-ios-60@3x.png
│ ├── Contents.json
│ ├── scooter-yellow.imageset
│ │ ├── Contents.json
│ │ └── scooter-yellow.pdf
│ └── scooter.imageset
│ │ ├── Contents.json
│ │ └── scooter.pdf
├── Components
│ ├── ChatView.swift
│ ├── ContentView.swift
│ ├── FareView.swift
│ ├── MainView.swift
│ ├── MapView.swift
│ ├── PopupView.swift
│ ├── RentView.swift
│ ├── ScooterIconView.swift
│ ├── SupportChatView.swift
│ ├── UnlockView.swift
│ └── UnlockedView.swift
├── Extensions
│ └── AnyTransition+Extensions.swift
└── Model
│ ├── AppModel.swift
│ ├── Scooter.swift
│ └── ScooterAnnotation.swift
└── Widget Extension
├── Assets.xcassets
├── AccentColor.colorset
│ └── Contents.json
├── AppIcon.appiconset
│ └── Contents.json
├── Contents.json
└── WidgetBackground.colorset
│ └── Contents.json
├── Info.plist
├── ScooterRentalAppWidgetExtension.intentdefinition
├── ScooterRentalAppWidgetExtensionBundle.swift
├── Timeline Providers
├── Entries
│ ├── ScooterEntry.swift
│ └── ScooterListEntry.swift
├── ListTimelineProvider.swift
└── RideTimelineProvider.swift
├── Views
├── ListWidgetEntryView.swift
└── RideWidgetEntryView.swift
└── Widgets
├── ListWidget.swift
└── RideWidget.swift
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/swift,macos,carthage,cocoapods
3 | # Edit at https://www.gitignore.io/?templates=swift,macos,carthage,cocoapods
4 |
5 | ### Carthage ###
6 | # Carthage
7 | #
8 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
9 | # Carthage/Checkouts
10 |
11 | Carthage/Build
12 |
13 | ### CocoaPods ###
14 | ## CocoaPods GitIgnore Template
15 |
16 | # CocoaPods - Only use to conserve bandwidth / Save time on Pushing
17 | # - Also handy if you have a large number of dependant pods
18 | # - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE
19 | Pods/
20 |
21 | ### macOS ###
22 | # General
23 | .DS_Store
24 | .AppleDouble
25 | .LSOverride
26 |
27 | # Icon must end with two \r
28 | Icon
29 |
30 | # Thumbnails
31 | ._*
32 |
33 | # Files that might appear in the root of a volume
34 | .DocumentRevisions-V100
35 | .fseventsd
36 | .Spotlight-V100
37 | .TemporaryItems
38 | .Trashes
39 | .VolumeIcon.icns
40 | .com.apple.timemachine.donotpresent
41 |
42 | # Directories potentially created on remote AFP share
43 | .AppleDB
44 | .AppleDesktop
45 | Network Trash Folder
46 | Temporary Items
47 | .apdisk
48 |
49 | ### Swift ###
50 | # Xcode
51 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
52 |
53 | ## Build generated
54 | build/
55 | DerivedData/
56 |
57 | ## Various settings
58 | *.pbxuser
59 | !default.pbxuser
60 | *.mode1v3
61 | !default.mode1v3
62 | *.mode2v3
63 | !default.mode2v3
64 | *.perspectivev3
65 | !default.perspectivev3
66 | xcuserdata/
67 |
68 | ## Other
69 | *.moved-aside
70 | *.xccheckout
71 | *.xcscmblueprint
72 |
73 | ## Obj-C/Swift specific
74 | *.hmap
75 | *.ipa
76 | *.dSYM.zip
77 | *.dSYM
78 |
79 | ## Playgrounds
80 | timeline.xctimeline
81 | playground.xcworkspace
82 |
83 | # Swift Package Manager
84 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
85 | # Packages/
86 | # Package.pins
87 | # Package.resolved
88 | .build/
89 | # Add this line if you want to avoid checking in Xcode SPM integration.
90 | # .swiftpm/xcode
91 |
92 | # CocoaPods
93 | # We recommend against adding the Pods directory to your .gitignore. However
94 | # you should judge for yourself, the pros and cons are mentioned at:
95 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
96 | # Pods/
97 | # Add this line if you want to avoid checking in source code from the Xcode workspace
98 | # *.xcworkspace
99 |
100 | # Carthage
101 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
102 | # Carthage/Checkouts
103 |
104 |
105 | # Accio dependency management
106 | Dependencies/
107 | .accio/
108 |
109 | # fastlane
110 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
111 | # screenshots whenever they are needed.
112 | # For more information about the recommended setup visit:
113 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
114 |
115 | fastlane/report.xml
116 | fastlane/Preview.html
117 | fastlane/screenshots/**/*.png
118 | fastlane/test_output
119 |
120 | # Code Injection
121 | # After new code Injection tools there's a generated folder /iOSInjectionProject
122 | # https://github.com/johnno1962/injectionforxcode
123 |
124 | iOSInjectionProject/
125 |
126 | # End of https://www.gitignore.io/api/swift,macos,carthage,cocoapods
127 |
128 |
129 |
--------------------------------------------------------------------------------
/App Clip/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/App Clip/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 |
--------------------------------------------------------------------------------
/App Clip/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/App Clip/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Scooter Rental
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | LSRequiresIPhoneOS
24 |
25 | UIApplicationSceneManifest
26 |
27 | UIApplicationSupportsMultipleScenes
28 |
29 |
30 | UIApplicationSupportsIndirectInputEvents
31 |
32 | UILaunchScreen
33 |
34 | UIRequiredDeviceCapabilities
35 |
36 | armv7
37 |
38 | UISupportedInterfaceOrientations
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UISupportedInterfaceOrientations~ipad
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationPortraitUpsideDown
48 | UIInterfaceOrientationLandscapeLeft
49 | UIInterfaceOrientationLandscapeRight
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/App Clip/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/App Clip/ScooterRentalAppClip.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.developer.associated-domains
6 |
7 | applinks:scooter.app
8 |
9 | com.apple.developer.parent-application-identifiers
10 |
11 | $(AppIdentifierPrefix)com.company.ScooterRentalApp
12 |
13 | com.apple.security.application-groups
14 |
15 | group.company.ScooterRentalApp
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/App/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Scooter Rental
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | LSRequiresIPhoneOS
24 |
25 | NSUserActivityTypes
26 |
27 | ConfigurationIntent
28 |
29 | UIApplicationSceneManifest
30 |
31 | UIApplicationSupportsMultipleScenes
32 |
33 |
34 | UIApplicationSupportsIndirectInputEvents
35 |
36 | UILaunchScreen
37 |
38 | UIRequiredDeviceCapabilities
39 |
40 | armv7
41 |
42 | UISupportedInterfaceOrientations
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | UISupportedInterfaceOrientations~ipad
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationPortraitUpsideDown
52 | UIInterfaceOrientationLandscapeLeft
53 | UIInterfaceOrientationLandscapeRight
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/App/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/App/ScooterRentalApp.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.developer.associated-domains
6 |
7 | applinks:scooter.app
8 |
9 | com.apple.security.application-groups
10 |
11 | group.company.ScooterRentalApp
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Assets/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/wwdc2020-tutorials/d32c405404a982a1058177fb32b8cf96849e4dbc/Assets/header.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # WWDC 2020 Tutorials
4 |
5 | ## How to Create App Clips
6 |
7 | With the iOS 14 release Apple added a new way to experience your app's features called App Clips.
8 |
9 | Read Article »
10 |
11 | ## How to Create Widgets With WidgetKit
12 |
13 | In 2020 Apple came up with an updated vision of widgets and unified their API across platforms. The new WidgetKit framework made widgets more accessible, interactive and increased SwiftUI adoption.
14 |
15 | Read Article »
16 |
17 | # Credits
18 |
19 | - [Transport 3 Icon Set](https://www.iconfinder.com/icons/3362526/scooter_bike_push_scooter_kick_icon) by Josy Dom Alexis
20 | - [WHCompare Servers & Web Hosting Icon Set](https://www.iconfinder.com/icons/4263521/chat_24/7_operator_support_live_icon) by Alexiuz AS
21 |
--------------------------------------------------------------------------------
/ScooterRentalApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | struct ScooterRentalApp: App {
5 |
6 | @StateObject private var model = AppModel()
7 |
8 | var body: some Scene {
9 | WindowGroup {
10 | #if APPCLIP
11 | ContentView()
12 | .environmentObject(model)
13 | .onContinueUserActivity(NSUserActivityTypeBrowsingWeb, perform: handleUserActivity)
14 | #else
15 | ContentView()
16 | .environmentObject(model)
17 | .onOpenURL(perform: { url in
18 | guard let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true) else {
19 | return
20 | }
21 | if let queryItems = components.queryItems,
22 | let scooterId = queryItems.first(where: { $0.name == "id" })?.value {
23 | model.selectedScooter = model.findScooterById(Int(scooterId) ?? 0)
24 | }
25 | })
26 | #endif
27 | }
28 | }
29 |
30 | #if APPCLIP
31 | func handleUserActivity(_ userActivity: NSUserActivity) {
32 | guard let incomingURL = userActivity.webpageURL,
33 | let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true) else {
34 | return
35 | }
36 | switch components.path {
37 | case "/help":
38 | model.isChatPresented = true
39 | case "/scooters":
40 | if let queryItems = components.queryItems,
41 | let scooterId = queryItems.first(where: { $0.name == "id" })?.value {
42 | model.selectedScooter = model.findScooterById(Int(scooterId) ?? 0)
43 | }
44 | default:
45 | break
46 | }
47 | }
48 | #endif
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/ScooterRentalApp.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 66576BF524B850FD0010E1E8 /* ScooterRentalApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66576BF424B850FD0010E1E8 /* ScooterRentalApp.swift */; };
11 | 66576BF724B850FD0010E1E8 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66576BF624B850FD0010E1E8 /* ContentView.swift */; };
12 | 66576BF924B850FE0010E1E8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 66576BF824B850FE0010E1E8 /* Assets.xcassets */; };
13 | 66576BFC24B850FE0010E1E8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 66576BFB24B850FE0010E1E8 /* Preview Assets.xcassets */; };
14 | 666A45E425AEC1030055B96E /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 666A45E325AEC1030055B96E /* WidgetKit.framework */; };
15 | 666A45E625AEC1030055B96E /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 666A45E525AEC1030055B96E /* SwiftUI.framework */; };
16 | 666A45EC25AEC1040055B96E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 666A45EB25AEC1040055B96E /* Assets.xcassets */; };
17 | 666A45EE25AEC1040055B96E /* ScooterRentalAppWidgetExtension.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 666A45EA25AEC1030055B96E /* ScooterRentalAppWidgetExtension.intentdefinition */; };
18 | 666A45EF25AEC1040055B96E /* ScooterRentalAppWidgetExtension.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 666A45EA25AEC1030055B96E /* ScooterRentalAppWidgetExtension.intentdefinition */; };
19 | 666A45F225AEC1040055B96E /* ScooterRentalAppWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 666A45E125AEC1030055B96E /* ScooterRentalAppWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
20 | 66758A0825BACD1100D9A2E9 /* ListWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66758A0325BACD0A00D9A2E9 /* ListWidget.swift */; };
21 | 66862DC02536F4E1004D4412 /* AppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DBF2536F4E1004D4412 /* AppModel.swift */; };
22 | 66862DC32536F4F2004D4412 /* AppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DBF2536F4E1004D4412 /* AppModel.swift */; };
23 | 66862DC92536F5AB004D4412 /* Scooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DC82536F5AB004D4412 /* Scooter.swift */; };
24 | 66862DCC2536F5CD004D4412 /* Scooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DC82536F5AB004D4412 /* Scooter.swift */; };
25 | 66862DD02536F5DE004D4412 /* ScooterIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DCF2536F5DE004D4412 /* ScooterIconView.swift */; };
26 | 66862DD32536F5E1004D4412 /* ScooterIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DCF2536F5DE004D4412 /* ScooterIconView.swift */; };
27 | 66862DD92536F634004D4412 /* AnyTransition+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DD82536F634004D4412 /* AnyTransition+Extensions.swift */; };
28 | 66862DDD2536F668004D4412 /* FareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DDC2536F668004D4412 /* FareView.swift */; };
29 | 66862DE02536F687004D4412 /* FareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DDC2536F668004D4412 /* FareView.swift */; };
30 | 66862DE42536F6CD004D4412 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DE32536F6CD004D4412 /* MainView.swift */; };
31 | 66862DE72536F6EA004D4412 /* AnyTransition+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DD82536F634004D4412 /* AnyTransition+Extensions.swift */; };
32 | 66862DEA2536F6ED004D4412 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DE32536F6CD004D4412 /* MainView.swift */; };
33 | 66862DEE2536F729004D4412 /* ScooterAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DED2536F729004D4412 /* ScooterAnnotation.swift */; };
34 | 66862DF12536F73C004D4412 /* ScooterAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DED2536F729004D4412 /* ScooterAnnotation.swift */; };
35 | 66862DF52536F75F004D4412 /* SupportChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DF42536F75F004D4412 /* SupportChatView.swift */; };
36 | 66862DF62536F75F004D4412 /* SupportChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DF42536F75F004D4412 /* SupportChatView.swift */; };
37 | 66862DFA2536F783004D4412 /* UnlockedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DF92536F783004D4412 /* UnlockedView.swift */; };
38 | 66862DFB2536F783004D4412 /* UnlockedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DF92536F783004D4412 /* UnlockedView.swift */; };
39 | 66862DFF2536F7A8004D4412 /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DFE2536F7A8004D4412 /* MapView.swift */; };
40 | 66862E002536F7A8004D4412 /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DFE2536F7A8004D4412 /* MapView.swift */; };
41 | 66862E042536F7D0004D4412 /* PopupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862E032536F7D0004D4412 /* PopupView.swift */; };
42 | 66862E052536F7D0004D4412 /* PopupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862E032536F7D0004D4412 /* PopupView.swift */; };
43 | 66A75A6D2535BB340034A468 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66576BF624B850FD0010E1E8 /* ContentView.swift */; };
44 | 66B2C05825B6C459003790C1 /* Scooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DC82536F5AB004D4412 /* Scooter.swift */; };
45 | 66B2C05925B6C459003790C1 /* AppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66862DBF2536F4E1004D4412 /* AppModel.swift */; };
46 | 66B2C06125B6E498003790C1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 66576BF824B850FE0010E1E8 /* Assets.xcassets */; };
47 | 66D99CC425FA08B600BFAD9F /* ScooterEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D99CC325FA08B600BFAD9F /* ScooterEntry.swift */; };
48 | 66D99CCA25FA090100BFAD9F /* RideTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D99CC925FA090100BFAD9F /* RideTimelineProvider.swift */; };
49 | 66D99CCF25FA095600BFAD9F /* RideWidgetEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D99CCE25FA095600BFAD9F /* RideWidgetEntryView.swift */; };
50 | 66D99CD525FA09AE00BFAD9F /* RideWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D99CD425FA09AE00BFAD9F /* RideWidget.swift */; };
51 | 66D99CE125FA0A4200BFAD9F /* ListTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D99CE025FA0A4200BFAD9F /* ListTimelineProvider.swift */; };
52 | 66D99CE925FA0A9A00BFAD9F /* ListWidgetEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D99CE825FA0A9A00BFAD9F /* ListWidgetEntryView.swift */; };
53 | 66D99CEF25FA0ACD00BFAD9F /* ScooterListEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D99CEE25FA0ACD00BFAD9F /* ScooterListEntry.swift */; };
54 | 66D99CF725FA0BC000BFAD9F /* ScooterRentalAppWidgetExtensionBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D99CF625FA0BC000BFAD9F /* ScooterRentalAppWidgetExtensionBundle.swift */; };
55 | 66FE1FF724BC7AE50090F669 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 66FE1FF624BC7AE50090F669 /* Preview Assets.xcassets */; };
56 | 66FE1FFC24BC7AE50090F669 /* ScooterRentalAppClip.app in Embed App Clips */ = {isa = PBXBuildFile; fileRef = 66FE1FED24BC7AE30090F669 /* ScooterRentalAppClip.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
57 | 66FE200524BC7D310090F669 /* ScooterRentalApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66576BF424B850FD0010E1E8 /* ScooterRentalApp.swift */; };
58 | 66FE200624BC85B40090F669 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 66576BF824B850FE0010E1E8 /* Assets.xcassets */; };
59 | /* End PBXBuildFile section */
60 |
61 | /* Begin PBXContainerItemProxy section */
62 | 666A45F025AEC1040055B96E /* PBXContainerItemProxy */ = {
63 | isa = PBXContainerItemProxy;
64 | containerPortal = 66576BE924B850FD0010E1E8 /* Project object */;
65 | proxyType = 1;
66 | remoteGlobalIDString = 666A45E025AEC1030055B96E;
67 | remoteInfo = ScooterRentalAppWidgetExtensionExtension;
68 | };
69 | 66FE1FFA24BC7AE50090F669 /* PBXContainerItemProxy */ = {
70 | isa = PBXContainerItemProxy;
71 | containerPortal = 66576BE924B850FD0010E1E8 /* Project object */;
72 | proxyType = 1;
73 | remoteGlobalIDString = 66FE1FEC24BC7AE30090F669;
74 | remoteInfo = ScooterAppClip;
75 | };
76 | /* End PBXContainerItemProxy section */
77 |
78 | /* Begin PBXCopyFilesBuildPhase section */
79 | 666A45F625AEC1040055B96E /* Embed App Extensions */ = {
80 | isa = PBXCopyFilesBuildPhase;
81 | buildActionMask = 2147483647;
82 | dstPath = "";
83 | dstSubfolderSpec = 13;
84 | files = (
85 | 666A45F225AEC1040055B96E /* ScooterRentalAppWidgetExtension.appex in Embed App Extensions */,
86 | );
87 | name = "Embed App Extensions";
88 | runOnlyForDeploymentPostprocessing = 0;
89 | };
90 | 66FE1FFD24BC7AE50090F669 /* Embed App Clips */ = {
91 | isa = PBXCopyFilesBuildPhase;
92 | buildActionMask = 2147483647;
93 | dstPath = "$(CONTENTS_FOLDER_PATH)/AppClips";
94 | dstSubfolderSpec = 16;
95 | files = (
96 | 66FE1FFC24BC7AE50090F669 /* ScooterRentalAppClip.app in Embed App Clips */,
97 | );
98 | name = "Embed App Clips";
99 | runOnlyForDeploymentPostprocessing = 0;
100 | };
101 | /* End PBXCopyFilesBuildPhase section */
102 |
103 | /* Begin PBXFileReference section */
104 | 66576BF124B850FD0010E1E8 /* ScooterRentalApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScooterRentalApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
105 | 66576BF424B850FD0010E1E8 /* ScooterRentalApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScooterRentalApp.swift; sourceTree = ""; };
106 | 66576BF624B850FD0010E1E8 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
107 | 66576BF824B850FE0010E1E8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
108 | 66576BFB24B850FE0010E1E8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
109 | 66576BFD24B850FE0010E1E8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
110 | 666A45E125AEC1030055B96E /* ScooterRentalAppWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ScooterRentalAppWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
111 | 666A45E325AEC1030055B96E /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
112 | 666A45E525AEC1030055B96E /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
113 | 666A45EA25AEC1030055B96E /* ScooterRentalAppWidgetExtension.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = ScooterRentalAppWidgetExtension.intentdefinition; sourceTree = ""; };
114 | 666A45EB25AEC1040055B96E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
115 | 666A45ED25AEC1040055B96E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
116 | 66758A0325BACD0A00D9A2E9 /* ListWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWidget.swift; sourceTree = ""; };
117 | 66862DBF2536F4E1004D4412 /* AppModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppModel.swift; sourceTree = ""; };
118 | 66862DC82536F5AB004D4412 /* Scooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scooter.swift; sourceTree = ""; };
119 | 66862DCF2536F5DE004D4412 /* ScooterIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScooterIconView.swift; sourceTree = ""; };
120 | 66862DD82536F634004D4412 /* AnyTransition+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AnyTransition+Extensions.swift"; sourceTree = ""; };
121 | 66862DDC2536F668004D4412 /* FareView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FareView.swift; sourceTree = ""; };
122 | 66862DE32536F6CD004D4412 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; };
123 | 66862DED2536F729004D4412 /* ScooterAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScooterAnnotation.swift; sourceTree = ""; };
124 | 66862DF42536F75F004D4412 /* SupportChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportChatView.swift; sourceTree = ""; };
125 | 66862DF92536F783004D4412 /* UnlockedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnlockedView.swift; sourceTree = ""; };
126 | 66862DFE2536F7A8004D4412 /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; };
127 | 66862E032536F7D0004D4412 /* PopupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupView.swift; sourceTree = ""; };
128 | 66862E152536F8F8004D4412 /* ScooterRentalAppClip.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ScooterRentalAppClip.entitlements; sourceTree = ""; };
129 | 66862E162536F9A8004D4412 /* ScooterRentalApp.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = ScooterRentalApp.entitlements; sourceTree = ""; };
130 | 66B2C05D25B6C604003790C1 /* ScooterRentalAppWidgetExtensionExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ScooterRentalAppWidgetExtensionExtension.entitlements; sourceTree = ""; };
131 | 66D99CC325FA08B600BFAD9F /* ScooterEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScooterEntry.swift; sourceTree = ""; };
132 | 66D99CC925FA090100BFAD9F /* RideTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RideTimelineProvider.swift; sourceTree = ""; };
133 | 66D99CCE25FA095600BFAD9F /* RideWidgetEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RideWidgetEntryView.swift; sourceTree = ""; };
134 | 66D99CD425FA09AE00BFAD9F /* RideWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RideWidget.swift; sourceTree = ""; };
135 | 66D99CE025FA0A4200BFAD9F /* ListTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTimelineProvider.swift; sourceTree = ""; };
136 | 66D99CE825FA0A9A00BFAD9F /* ListWidgetEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWidgetEntryView.swift; sourceTree = ""; };
137 | 66D99CEE25FA0ACD00BFAD9F /* ScooterListEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScooterListEntry.swift; sourceTree = ""; };
138 | 66D99CF625FA0BC000BFAD9F /* ScooterRentalAppWidgetExtensionBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScooterRentalAppWidgetExtensionBundle.swift; sourceTree = ""; };
139 | 66FE1FED24BC7AE30090F669 /* ScooterRentalAppClip.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScooterRentalAppClip.app; sourceTree = BUILT_PRODUCTS_DIR; };
140 | 66FE1FF624BC7AE50090F669 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
141 | 66FE1FF824BC7AE50090F669 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
142 | /* End PBXFileReference section */
143 |
144 | /* Begin PBXFrameworksBuildPhase section */
145 | 66576BEE24B850FD0010E1E8 /* Frameworks */ = {
146 | isa = PBXFrameworksBuildPhase;
147 | buildActionMask = 2147483647;
148 | files = (
149 | );
150 | runOnlyForDeploymentPostprocessing = 0;
151 | };
152 | 666A45DE25AEC1030055B96E /* Frameworks */ = {
153 | isa = PBXFrameworksBuildPhase;
154 | buildActionMask = 2147483647;
155 | files = (
156 | 666A45E625AEC1030055B96E /* SwiftUI.framework in Frameworks */,
157 | 666A45E425AEC1030055B96E /* WidgetKit.framework in Frameworks */,
158 | );
159 | runOnlyForDeploymentPostprocessing = 0;
160 | };
161 | 66FE1FEA24BC7AE30090F669 /* Frameworks */ = {
162 | isa = PBXFrameworksBuildPhase;
163 | buildActionMask = 2147483647;
164 | files = (
165 | );
166 | runOnlyForDeploymentPostprocessing = 0;
167 | };
168 | /* End PBXFrameworksBuildPhase section */
169 |
170 | /* Begin PBXGroup section */
171 | 66576BE824B850FD0010E1E8 = {
172 | isa = PBXGroup;
173 | children = (
174 | 66B2C05D25B6C604003790C1 /* ScooterRentalAppWidgetExtensionExtension.entitlements */,
175 | 66862E102536F83B004D4412 /* Shared */,
176 | 66576BF424B850FD0010E1E8 /* ScooterRentalApp.swift */,
177 | 66576BF324B850FD0010E1E8 /* App */,
178 | 66FE1FEE24BC7AE30090F669 /* App Clip */,
179 | 666A45E725AEC1030055B96E /* Widget Extension */,
180 | 666A45E225AEC1030055B96E /* Frameworks */,
181 | 66576BF224B850FD0010E1E8 /* Products */,
182 | );
183 | sourceTree = "";
184 | };
185 | 66576BF224B850FD0010E1E8 /* Products */ = {
186 | isa = PBXGroup;
187 | children = (
188 | 66576BF124B850FD0010E1E8 /* ScooterRentalApp.app */,
189 | 66FE1FED24BC7AE30090F669 /* ScooterRentalAppClip.app */,
190 | 666A45E125AEC1030055B96E /* ScooterRentalAppWidgetExtension.appex */,
191 | );
192 | name = Products;
193 | sourceTree = "";
194 | };
195 | 66576BF324B850FD0010E1E8 /* App */ = {
196 | isa = PBXGroup;
197 | children = (
198 | 66862E162536F9A8004D4412 /* ScooterRentalApp.entitlements */,
199 | 66576BFD24B850FE0010E1E8 /* Info.plist */,
200 | 66576BFA24B850FE0010E1E8 /* Preview Content */,
201 | );
202 | path = App;
203 | sourceTree = "";
204 | };
205 | 66576BFA24B850FE0010E1E8 /* Preview Content */ = {
206 | isa = PBXGroup;
207 | children = (
208 | 66576BFB24B850FE0010E1E8 /* Preview Assets.xcassets */,
209 | );
210 | path = "Preview Content";
211 | sourceTree = "";
212 | };
213 | 666A45E225AEC1030055B96E /* Frameworks */ = {
214 | isa = PBXGroup;
215 | children = (
216 | 666A45E325AEC1030055B96E /* WidgetKit.framework */,
217 | 666A45E525AEC1030055B96E /* SwiftUI.framework */,
218 | );
219 | name = Frameworks;
220 | sourceTree = "";
221 | };
222 | 666A45E725AEC1030055B96E /* Widget Extension */ = {
223 | isa = PBXGroup;
224 | children = (
225 | 66D99CDC25FA09FB00BFAD9F /* Timeline Providers */,
226 | 66D99CD325FA09A600BFAD9F /* Widgets */,
227 | 66D99CC825FA08EC00BFAD9F /* Views */,
228 | 666A45EA25AEC1030055B96E /* ScooterRentalAppWidgetExtension.intentdefinition */,
229 | 666A45EB25AEC1040055B96E /* Assets.xcassets */,
230 | 666A45ED25AEC1040055B96E /* Info.plist */,
231 | 66D99CF625FA0BC000BFAD9F /* ScooterRentalAppWidgetExtensionBundle.swift */,
232 | );
233 | path = "Widget Extension";
234 | sourceTree = "";
235 | };
236 | 66862DBE2536F4D2004D4412 /* Model */ = {
237 | isa = PBXGroup;
238 | children = (
239 | 66862DBF2536F4E1004D4412 /* AppModel.swift */,
240 | 66862DC82536F5AB004D4412 /* Scooter.swift */,
241 | 66862DED2536F729004D4412 /* ScooterAnnotation.swift */,
242 | );
243 | path = Model;
244 | sourceTree = "";
245 | };
246 | 66862DC62536F51C004D4412 /* Components */ = {
247 | isa = PBXGroup;
248 | children = (
249 | 66862DCF2536F5DE004D4412 /* ScooterIconView.swift */,
250 | 66862DDC2536F668004D4412 /* FareView.swift */,
251 | 66862DE32536F6CD004D4412 /* MainView.swift */,
252 | 66862DF42536F75F004D4412 /* SupportChatView.swift */,
253 | 66862DF92536F783004D4412 /* UnlockedView.swift */,
254 | 66862DFE2536F7A8004D4412 /* MapView.swift */,
255 | 66862E032536F7D0004D4412 /* PopupView.swift */,
256 | 66576BF624B850FD0010E1E8 /* ContentView.swift */,
257 | );
258 | path = Components;
259 | sourceTree = "";
260 | };
261 | 66862DC72536F52D004D4412 /* Extensions */ = {
262 | isa = PBXGroup;
263 | children = (
264 | 66862DD82536F634004D4412 /* AnyTransition+Extensions.swift */,
265 | );
266 | path = Extensions;
267 | sourceTree = "";
268 | };
269 | 66862E102536F83B004D4412 /* Shared */ = {
270 | isa = PBXGroup;
271 | children = (
272 | 66862DC72536F52D004D4412 /* Extensions */,
273 | 66862DC62536F51C004D4412 /* Components */,
274 | 66862DBE2536F4D2004D4412 /* Model */,
275 | 66576BF824B850FE0010E1E8 /* Assets.xcassets */,
276 | );
277 | path = Shared;
278 | sourceTree = "";
279 | };
280 | 66D99CC825FA08EC00BFAD9F /* Views */ = {
281 | isa = PBXGroup;
282 | children = (
283 | 66D99CCE25FA095600BFAD9F /* RideWidgetEntryView.swift */,
284 | 66D99CE825FA0A9A00BFAD9F /* ListWidgetEntryView.swift */,
285 | );
286 | path = Views;
287 | sourceTree = "";
288 | };
289 | 66D99CD325FA09A600BFAD9F /* Widgets */ = {
290 | isa = PBXGroup;
291 | children = (
292 | 66D99CD425FA09AE00BFAD9F /* RideWidget.swift */,
293 | 66758A0325BACD0A00D9A2E9 /* ListWidget.swift */,
294 | );
295 | path = Widgets;
296 | sourceTree = "";
297 | };
298 | 66D99CDC25FA09FB00BFAD9F /* Timeline Providers */ = {
299 | isa = PBXGroup;
300 | children = (
301 | 66D99CED25FA0ABA00BFAD9F /* Entries */,
302 | 66D99CC925FA090100BFAD9F /* RideTimelineProvider.swift */,
303 | 66D99CE025FA0A4200BFAD9F /* ListTimelineProvider.swift */,
304 | );
305 | path = "Timeline Providers";
306 | sourceTree = "";
307 | };
308 | 66D99CED25FA0ABA00BFAD9F /* Entries */ = {
309 | isa = PBXGroup;
310 | children = (
311 | 66D99CC325FA08B600BFAD9F /* ScooterEntry.swift */,
312 | 66D99CEE25FA0ACD00BFAD9F /* ScooterListEntry.swift */,
313 | );
314 | path = Entries;
315 | sourceTree = "";
316 | };
317 | 66FE1FEE24BC7AE30090F669 /* App Clip */ = {
318 | isa = PBXGroup;
319 | children = (
320 | 66FE1FF824BC7AE50090F669 /* Info.plist */,
321 | 66862E152536F8F8004D4412 /* ScooterRentalAppClip.entitlements */,
322 | 66FE1FF524BC7AE50090F669 /* Preview Content */,
323 | );
324 | path = "App Clip";
325 | sourceTree = "";
326 | };
327 | 66FE1FF524BC7AE50090F669 /* Preview Content */ = {
328 | isa = PBXGroup;
329 | children = (
330 | 66FE1FF624BC7AE50090F669 /* Preview Assets.xcassets */,
331 | );
332 | path = "Preview Content";
333 | sourceTree = "";
334 | };
335 | /* End PBXGroup section */
336 |
337 | /* Begin PBXNativeTarget section */
338 | 66576BF024B850FD0010E1E8 /* ScooterRentalApp */ = {
339 | isa = PBXNativeTarget;
340 | buildConfigurationList = 66576C0024B850FE0010E1E8 /* Build configuration list for PBXNativeTarget "ScooterRentalApp" */;
341 | buildPhases = (
342 | 66576BED24B850FD0010E1E8 /* Sources */,
343 | 66576BEE24B850FD0010E1E8 /* Frameworks */,
344 | 66576BEF24B850FD0010E1E8 /* Resources */,
345 | 66FE1FFD24BC7AE50090F669 /* Embed App Clips */,
346 | 666A45F625AEC1040055B96E /* Embed App Extensions */,
347 | );
348 | buildRules = (
349 | );
350 | dependencies = (
351 | 66FE1FFB24BC7AE50090F669 /* PBXTargetDependency */,
352 | 666A45F125AEC1040055B96E /* PBXTargetDependency */,
353 | );
354 | name = ScooterRentalApp;
355 | productName = ScooterAppClipDemo;
356 | productReference = 66576BF124B850FD0010E1E8 /* ScooterRentalApp.app */;
357 | productType = "com.apple.product-type.application";
358 | };
359 | 666A45E025AEC1030055B96E /* ScooterRentalAppWidgetExtension */ = {
360 | isa = PBXNativeTarget;
361 | buildConfigurationList = 666A45F325AEC1040055B96E /* Build configuration list for PBXNativeTarget "ScooterRentalAppWidgetExtension" */;
362 | buildPhases = (
363 | 666A45DD25AEC1030055B96E /* Sources */,
364 | 666A45DE25AEC1030055B96E /* Frameworks */,
365 | 666A45DF25AEC1030055B96E /* Resources */,
366 | );
367 | buildRules = (
368 | );
369 | dependencies = (
370 | );
371 | name = ScooterRentalAppWidgetExtension;
372 | productName = ScooterRentalAppWidgetExtensionExtension;
373 | productReference = 666A45E125AEC1030055B96E /* ScooterRentalAppWidgetExtension.appex */;
374 | productType = "com.apple.product-type.app-extension";
375 | };
376 | 66FE1FEC24BC7AE30090F669 /* ScooterRentalAppClip */ = {
377 | isa = PBXNativeTarget;
378 | buildConfigurationList = 66FE200024BC7AE50090F669 /* Build configuration list for PBXNativeTarget "ScooterRentalAppClip" */;
379 | buildPhases = (
380 | 66FE1FE924BC7AE30090F669 /* Sources */,
381 | 66FE1FEA24BC7AE30090F669 /* Frameworks */,
382 | 66FE1FEB24BC7AE30090F669 /* Resources */,
383 | );
384 | buildRules = (
385 | );
386 | dependencies = (
387 | );
388 | name = ScooterRentalAppClip;
389 | productName = ScooterAppClip;
390 | productReference = 66FE1FED24BC7AE30090F669 /* ScooterRentalAppClip.app */;
391 | productType = "com.apple.product-type.application.on-demand-install-capable";
392 | };
393 | /* End PBXNativeTarget section */
394 |
395 | /* Begin PBXProject section */
396 | 66576BE924B850FD0010E1E8 /* Project object */ = {
397 | isa = PBXProject;
398 | attributes = {
399 | LastSwiftUpdateCheck = 1220;
400 | LastUpgradeCheck = 1200;
401 | TargetAttributes = {
402 | 66576BF024B850FD0010E1E8 = {
403 | CreatedOnToolsVersion = 12.0;
404 | };
405 | 666A45E025AEC1030055B96E = {
406 | CreatedOnToolsVersion = 12.2;
407 | };
408 | 66FE1FEC24BC7AE30090F669 = {
409 | CreatedOnToolsVersion = 12.0;
410 | };
411 | };
412 | };
413 | buildConfigurationList = 66576BEC24B850FD0010E1E8 /* Build configuration list for PBXProject "ScooterRentalApp" */;
414 | compatibilityVersion = "Xcode 9.3";
415 | developmentRegion = en;
416 | hasScannedForEncodings = 0;
417 | knownRegions = (
418 | en,
419 | Base,
420 | );
421 | mainGroup = 66576BE824B850FD0010E1E8;
422 | productRefGroup = 66576BF224B850FD0010E1E8 /* Products */;
423 | projectDirPath = "";
424 | projectRoot = "";
425 | targets = (
426 | 66576BF024B850FD0010E1E8 /* ScooterRentalApp */,
427 | 66FE1FEC24BC7AE30090F669 /* ScooterRentalAppClip */,
428 | 666A45E025AEC1030055B96E /* ScooterRentalAppWidgetExtension */,
429 | );
430 | };
431 | /* End PBXProject section */
432 |
433 | /* Begin PBXResourcesBuildPhase section */
434 | 66576BEF24B850FD0010E1E8 /* Resources */ = {
435 | isa = PBXResourcesBuildPhase;
436 | buildActionMask = 2147483647;
437 | files = (
438 | 66576BFC24B850FE0010E1E8 /* Preview Assets.xcassets in Resources */,
439 | 66576BF924B850FE0010E1E8 /* Assets.xcassets in Resources */,
440 | );
441 | runOnlyForDeploymentPostprocessing = 0;
442 | };
443 | 666A45DF25AEC1030055B96E /* Resources */ = {
444 | isa = PBXResourcesBuildPhase;
445 | buildActionMask = 2147483647;
446 | files = (
447 | 66B2C06125B6E498003790C1 /* Assets.xcassets in Resources */,
448 | 666A45EC25AEC1040055B96E /* Assets.xcassets in Resources */,
449 | );
450 | runOnlyForDeploymentPostprocessing = 0;
451 | };
452 | 66FE1FEB24BC7AE30090F669 /* Resources */ = {
453 | isa = PBXResourcesBuildPhase;
454 | buildActionMask = 2147483647;
455 | files = (
456 | 66FE200624BC85B40090F669 /* Assets.xcassets in Resources */,
457 | 66FE1FF724BC7AE50090F669 /* Preview Assets.xcassets in Resources */,
458 | );
459 | runOnlyForDeploymentPostprocessing = 0;
460 | };
461 | /* End PBXResourcesBuildPhase section */
462 |
463 | /* Begin PBXSourcesBuildPhase section */
464 | 66576BED24B850FD0010E1E8 /* Sources */ = {
465 | isa = PBXSourcesBuildPhase;
466 | buildActionMask = 2147483647;
467 | files = (
468 | 66862DC92536F5AB004D4412 /* Scooter.swift in Sources */,
469 | 66862DFA2536F783004D4412 /* UnlockedView.swift in Sources */,
470 | 666A45EF25AEC1040055B96E /* ScooterRentalAppWidgetExtension.intentdefinition in Sources */,
471 | 66862DDD2536F668004D4412 /* FareView.swift in Sources */,
472 | 66862DFF2536F7A8004D4412 /* MapView.swift in Sources */,
473 | 66576BF724B850FD0010E1E8 /* ContentView.swift in Sources */,
474 | 66862DF52536F75F004D4412 /* SupportChatView.swift in Sources */,
475 | 66862E042536F7D0004D4412 /* PopupView.swift in Sources */,
476 | 66862DEE2536F729004D4412 /* ScooterAnnotation.swift in Sources */,
477 | 66862DD92536F634004D4412 /* AnyTransition+Extensions.swift in Sources */,
478 | 66576BF524B850FD0010E1E8 /* ScooterRentalApp.swift in Sources */,
479 | 66862DC02536F4E1004D4412 /* AppModel.swift in Sources */,
480 | 66862DE42536F6CD004D4412 /* MainView.swift in Sources */,
481 | 66862DD02536F5DE004D4412 /* ScooterIconView.swift in Sources */,
482 | );
483 | runOnlyForDeploymentPostprocessing = 0;
484 | };
485 | 666A45DD25AEC1030055B96E /* Sources */ = {
486 | isa = PBXSourcesBuildPhase;
487 | buildActionMask = 2147483647;
488 | files = (
489 | 66758A0825BACD1100D9A2E9 /* ListWidget.swift in Sources */,
490 | 66D99CCF25FA095600BFAD9F /* RideWidgetEntryView.swift in Sources */,
491 | 66D99CE925FA0A9A00BFAD9F /* ListWidgetEntryView.swift in Sources */,
492 | 666A45EE25AEC1040055B96E /* ScooterRentalAppWidgetExtension.intentdefinition in Sources */,
493 | 66B2C05825B6C459003790C1 /* Scooter.swift in Sources */,
494 | 66D99CE125FA0A4200BFAD9F /* ListTimelineProvider.swift in Sources */,
495 | 66D99CD525FA09AE00BFAD9F /* RideWidget.swift in Sources */,
496 | 66D99CC425FA08B600BFAD9F /* ScooterEntry.swift in Sources */,
497 | 66D99CCA25FA090100BFAD9F /* RideTimelineProvider.swift in Sources */,
498 | 66D99CEF25FA0ACD00BFAD9F /* ScooterListEntry.swift in Sources */,
499 | 66B2C05925B6C459003790C1 /* AppModel.swift in Sources */,
500 | 66D99CF725FA0BC000BFAD9F /* ScooterRentalAppWidgetExtensionBundle.swift in Sources */,
501 | );
502 | runOnlyForDeploymentPostprocessing = 0;
503 | };
504 | 66FE1FE924BC7AE30090F669 /* Sources */ = {
505 | isa = PBXSourcesBuildPhase;
506 | buildActionMask = 2147483647;
507 | files = (
508 | 66862DCC2536F5CD004D4412 /* Scooter.swift in Sources */,
509 | 66862DFB2536F783004D4412 /* UnlockedView.swift in Sources */,
510 | 66862DE02536F687004D4412 /* FareView.swift in Sources */,
511 | 66862E002536F7A8004D4412 /* MapView.swift in Sources */,
512 | 66FE200524BC7D310090F669 /* ScooterRentalApp.swift in Sources */,
513 | 66862DF62536F75F004D4412 /* SupportChatView.swift in Sources */,
514 | 66862E052536F7D0004D4412 /* PopupView.swift in Sources */,
515 | 66862DF12536F73C004D4412 /* ScooterAnnotation.swift in Sources */,
516 | 66A75A6D2535BB340034A468 /* ContentView.swift in Sources */,
517 | 66862DC32536F4F2004D4412 /* AppModel.swift in Sources */,
518 | 66862DD32536F5E1004D4412 /* ScooterIconView.swift in Sources */,
519 | 66862DEA2536F6ED004D4412 /* MainView.swift in Sources */,
520 | 66862DE72536F6EA004D4412 /* AnyTransition+Extensions.swift in Sources */,
521 | );
522 | runOnlyForDeploymentPostprocessing = 0;
523 | };
524 | /* End PBXSourcesBuildPhase section */
525 |
526 | /* Begin PBXTargetDependency section */
527 | 666A45F125AEC1040055B96E /* PBXTargetDependency */ = {
528 | isa = PBXTargetDependency;
529 | target = 666A45E025AEC1030055B96E /* ScooterRentalAppWidgetExtension */;
530 | targetProxy = 666A45F025AEC1040055B96E /* PBXContainerItemProxy */;
531 | };
532 | 66FE1FFB24BC7AE50090F669 /* PBXTargetDependency */ = {
533 | isa = PBXTargetDependency;
534 | target = 66FE1FEC24BC7AE30090F669 /* ScooterRentalAppClip */;
535 | targetProxy = 66FE1FFA24BC7AE50090F669 /* PBXContainerItemProxy */;
536 | };
537 | /* End PBXTargetDependency section */
538 |
539 | /* Begin XCBuildConfiguration section */
540 | 66576BFE24B850FE0010E1E8 /* Debug */ = {
541 | isa = XCBuildConfiguration;
542 | buildSettings = {
543 | ALWAYS_SEARCH_USER_PATHS = NO;
544 | CLANG_ANALYZER_NONNULL = YES;
545 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
546 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
547 | CLANG_CXX_LIBRARY = "libc++";
548 | CLANG_ENABLE_MODULES = YES;
549 | CLANG_ENABLE_OBJC_ARC = YES;
550 | CLANG_ENABLE_OBJC_WEAK = YES;
551 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
552 | CLANG_WARN_BOOL_CONVERSION = YES;
553 | CLANG_WARN_COMMA = YES;
554 | CLANG_WARN_CONSTANT_CONVERSION = YES;
555 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
556 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
557 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
558 | CLANG_WARN_EMPTY_BODY = YES;
559 | CLANG_WARN_ENUM_CONVERSION = YES;
560 | CLANG_WARN_INFINITE_RECURSION = YES;
561 | CLANG_WARN_INT_CONVERSION = YES;
562 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
563 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
564 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
565 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
566 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
567 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
568 | CLANG_WARN_STRICT_PROTOTYPES = YES;
569 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
570 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
571 | CLANG_WARN_UNREACHABLE_CODE = YES;
572 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
573 | COPY_PHASE_STRIP = NO;
574 | DEBUG_INFORMATION_FORMAT = dwarf;
575 | ENABLE_STRICT_OBJC_MSGSEND = YES;
576 | ENABLE_TESTABILITY = YES;
577 | GCC_C_LANGUAGE_STANDARD = gnu11;
578 | GCC_DYNAMIC_NO_PIC = NO;
579 | GCC_NO_COMMON_BLOCKS = YES;
580 | GCC_OPTIMIZATION_LEVEL = 0;
581 | GCC_PREPROCESSOR_DEFINITIONS = (
582 | "DEBUG=1",
583 | "$(inherited)",
584 | );
585 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
586 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
587 | GCC_WARN_UNDECLARED_SELECTOR = YES;
588 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
589 | GCC_WARN_UNUSED_FUNCTION = YES;
590 | GCC_WARN_UNUSED_VARIABLE = YES;
591 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
592 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
593 | MTL_FAST_MATH = YES;
594 | ONLY_ACTIVE_ARCH = YES;
595 | SDKROOT = iphoneos;
596 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
597 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
598 | };
599 | name = Debug;
600 | };
601 | 66576BFF24B850FE0010E1E8 /* Release */ = {
602 | isa = XCBuildConfiguration;
603 | buildSettings = {
604 | ALWAYS_SEARCH_USER_PATHS = NO;
605 | CLANG_ANALYZER_NONNULL = YES;
606 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
607 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
608 | CLANG_CXX_LIBRARY = "libc++";
609 | CLANG_ENABLE_MODULES = YES;
610 | CLANG_ENABLE_OBJC_ARC = YES;
611 | CLANG_ENABLE_OBJC_WEAK = YES;
612 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
613 | CLANG_WARN_BOOL_CONVERSION = YES;
614 | CLANG_WARN_COMMA = YES;
615 | CLANG_WARN_CONSTANT_CONVERSION = YES;
616 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
617 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
618 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
619 | CLANG_WARN_EMPTY_BODY = YES;
620 | CLANG_WARN_ENUM_CONVERSION = YES;
621 | CLANG_WARN_INFINITE_RECURSION = YES;
622 | CLANG_WARN_INT_CONVERSION = YES;
623 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
624 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
625 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
626 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
627 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
628 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
629 | CLANG_WARN_STRICT_PROTOTYPES = YES;
630 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
631 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
632 | CLANG_WARN_UNREACHABLE_CODE = YES;
633 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
634 | COPY_PHASE_STRIP = NO;
635 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
636 | ENABLE_NS_ASSERTIONS = NO;
637 | ENABLE_STRICT_OBJC_MSGSEND = YES;
638 | GCC_C_LANGUAGE_STANDARD = gnu11;
639 | GCC_NO_COMMON_BLOCKS = YES;
640 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
641 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
642 | GCC_WARN_UNDECLARED_SELECTOR = YES;
643 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
644 | GCC_WARN_UNUSED_FUNCTION = YES;
645 | GCC_WARN_UNUSED_VARIABLE = YES;
646 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
647 | MTL_ENABLE_DEBUG_INFO = NO;
648 | MTL_FAST_MATH = YES;
649 | SDKROOT = iphoneos;
650 | SWIFT_COMPILATION_MODE = wholemodule;
651 | SWIFT_OPTIMIZATION_LEVEL = "-O";
652 | VALIDATE_PRODUCT = YES;
653 | };
654 | name = Release;
655 | };
656 | 66576C0124B850FE0010E1E8 /* Debug */ = {
657 | isa = XCBuildConfiguration;
658 | buildSettings = {
659 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
660 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
661 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
662 | CODE_SIGN_ENTITLEMENTS = App/ScooterRentalApp.entitlements;
663 | CODE_SIGN_IDENTITY = "Apple Development";
664 | CODE_SIGN_STYLE = Automatic;
665 | CURRENT_PROJECT_VERSION = 1;
666 | DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\"";
667 | DEVELOPMENT_TEAM = "";
668 | ENABLE_PREVIEWS = YES;
669 | INFOPLIST_FILE = App/Info.plist;
670 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
671 | LD_RUNPATH_SEARCH_PATHS = (
672 | "$(inherited)",
673 | "@executable_path/Frameworks",
674 | );
675 | PRODUCT_BUNDLE_IDENTIFIER = com.company.ScooterRentalApp;
676 | PRODUCT_NAME = "$(TARGET_NAME)";
677 | PROVISIONING_PROFILE_SPECIFIER = "";
678 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
679 | SWIFT_VERSION = 5.0;
680 | TARGETED_DEVICE_FAMILY = 1;
681 | };
682 | name = Debug;
683 | };
684 | 66576C0224B850FE0010E1E8 /* Release */ = {
685 | isa = XCBuildConfiguration;
686 | buildSettings = {
687 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
688 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
689 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
690 | CODE_SIGN_ENTITLEMENTS = App/ScooterRentalApp.entitlements;
691 | CODE_SIGN_IDENTITY = "Apple Distribution";
692 | CODE_SIGN_STYLE = Automatic;
693 | CURRENT_PROJECT_VERSION = 1;
694 | DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\"";
695 | DEVELOPMENT_TEAM = "";
696 | ENABLE_PREVIEWS = YES;
697 | INFOPLIST_FILE = App/Info.plist;
698 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
699 | LD_RUNPATH_SEARCH_PATHS = (
700 | "$(inherited)",
701 | "@executable_path/Frameworks",
702 | );
703 | PRODUCT_BUNDLE_IDENTIFIER = com.company.ScooterRentalApp;
704 | PRODUCT_NAME = "$(TARGET_NAME)";
705 | PROVISIONING_PROFILE_SPECIFIER = "";
706 | SWIFT_VERSION = 5.0;
707 | TARGETED_DEVICE_FAMILY = 1;
708 | };
709 | name = Release;
710 | };
711 | 666A45F425AEC1040055B96E /* Debug */ = {
712 | isa = XCBuildConfiguration;
713 | buildSettings = {
714 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
715 | ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
716 | CODE_SIGN_ENTITLEMENTS = ScooterRentalAppWidgetExtensionExtension.entitlements;
717 | CODE_SIGN_IDENTITY = "Apple Development";
718 | CODE_SIGN_STYLE = Automatic;
719 | DEVELOPMENT_TEAM = "";
720 | INFOPLIST_FILE = "Widget Extension/Info.plist";
721 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
722 | LD_RUNPATH_SEARCH_PATHS = (
723 | "$(inherited)",
724 | "@executable_path/Frameworks",
725 | "@executable_path/../../Frameworks",
726 | );
727 | PRODUCT_BUNDLE_IDENTIFIER = com.company.ScooterRentalApp.WidgetExtension;
728 | PRODUCT_NAME = "$(TARGET_NAME)";
729 | PROVISIONING_PROFILE_SPECIFIER = "";
730 | SKIP_INSTALL = YES;
731 | SWIFT_VERSION = 5.0;
732 | TARGETED_DEVICE_FAMILY = "1,2";
733 | };
734 | name = Debug;
735 | };
736 | 666A45F525AEC1040055B96E /* Release */ = {
737 | isa = XCBuildConfiguration;
738 | buildSettings = {
739 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
740 | ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
741 | CODE_SIGN_ENTITLEMENTS = ScooterRentalAppWidgetExtensionExtension.entitlements;
742 | CODE_SIGN_IDENTITY = "Apple Distribution";
743 | CODE_SIGN_STYLE = Automatic;
744 | DEVELOPMENT_TEAM = "";
745 | INFOPLIST_FILE = "Widget Extension/Info.plist";
746 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
747 | LD_RUNPATH_SEARCH_PATHS = (
748 | "$(inherited)",
749 | "@executable_path/Frameworks",
750 | "@executable_path/../../Frameworks",
751 | );
752 | PRODUCT_BUNDLE_IDENTIFIER = com.company.ScooterRentalApp.WidgetExtension;
753 | PRODUCT_NAME = "$(TARGET_NAME)";
754 | PROVISIONING_PROFILE_SPECIFIER = "";
755 | SKIP_INSTALL = YES;
756 | SWIFT_VERSION = 5.0;
757 | TARGETED_DEVICE_FAMILY = "1,2";
758 | };
759 | name = Release;
760 | };
761 | 66FE1FFE24BC7AE50090F669 /* Debug */ = {
762 | isa = XCBuildConfiguration;
763 | buildSettings = {
764 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
765 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
766 | CODE_SIGN_ENTITLEMENTS = "App Clip/ScooterRentalAppClip.entitlements";
767 | CODE_SIGN_IDENTITY = "Apple Development";
768 | CODE_SIGN_STYLE = Automatic;
769 | CURRENT_PROJECT_VERSION = 1;
770 | DEVELOPMENT_ASSET_PATHS = "\"App Clip/Preview Content\"";
771 | DEVELOPMENT_TEAM = "";
772 | ENABLE_PREVIEWS = YES;
773 | INFOPLIST_FILE = "App Clip/Info.plist";
774 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
775 | LD_RUNPATH_SEARCH_PATHS = (
776 | "$(inherited)",
777 | "@executable_path/Frameworks",
778 | );
779 | PRODUCT_BUNDLE_IDENTIFIER = com.company.ScooterRentalApp.AppClip;
780 | PRODUCT_NAME = "$(TARGET_NAME)";
781 | PROVISIONING_PROFILE_SPECIFIER = "";
782 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG APPCLIP";
783 | SWIFT_VERSION = 5.0;
784 | TARGETED_DEVICE_FAMILY = 1;
785 | };
786 | name = Debug;
787 | };
788 | 66FE1FFF24BC7AE50090F669 /* Release */ = {
789 | isa = XCBuildConfiguration;
790 | buildSettings = {
791 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
792 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
793 | CODE_SIGN_ENTITLEMENTS = "App Clip/ScooterRentalAppClip.entitlements";
794 | CODE_SIGN_IDENTITY = "Apple Distribution";
795 | CODE_SIGN_STYLE = Automatic;
796 | CURRENT_PROJECT_VERSION = 1;
797 | DEVELOPMENT_ASSET_PATHS = "\"App Clip/Preview Content\"";
798 | DEVELOPMENT_TEAM = "";
799 | ENABLE_PREVIEWS = YES;
800 | INFOPLIST_FILE = "App Clip/Info.plist";
801 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
802 | LD_RUNPATH_SEARCH_PATHS = (
803 | "$(inherited)",
804 | "@executable_path/Frameworks",
805 | );
806 | PRODUCT_BUNDLE_IDENTIFIER = com.company.ScooterRentalApp.AppClip;
807 | PRODUCT_NAME = "$(TARGET_NAME)";
808 | PROVISIONING_PROFILE_SPECIFIER = "";
809 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = APPCLIP;
810 | SWIFT_VERSION = 5.0;
811 | TARGETED_DEVICE_FAMILY = 1;
812 | };
813 | name = Release;
814 | };
815 | /* End XCBuildConfiguration section */
816 |
817 | /* Begin XCConfigurationList section */
818 | 66576BEC24B850FD0010E1E8 /* Build configuration list for PBXProject "ScooterRentalApp" */ = {
819 | isa = XCConfigurationList;
820 | buildConfigurations = (
821 | 66576BFE24B850FE0010E1E8 /* Debug */,
822 | 66576BFF24B850FE0010E1E8 /* Release */,
823 | );
824 | defaultConfigurationIsVisible = 0;
825 | defaultConfigurationName = Release;
826 | };
827 | 66576C0024B850FE0010E1E8 /* Build configuration list for PBXNativeTarget "ScooterRentalApp" */ = {
828 | isa = XCConfigurationList;
829 | buildConfigurations = (
830 | 66576C0124B850FE0010E1E8 /* Debug */,
831 | 66576C0224B850FE0010E1E8 /* Release */,
832 | );
833 | defaultConfigurationIsVisible = 0;
834 | defaultConfigurationName = Release;
835 | };
836 | 666A45F325AEC1040055B96E /* Build configuration list for PBXNativeTarget "ScooterRentalAppWidgetExtension" */ = {
837 | isa = XCConfigurationList;
838 | buildConfigurations = (
839 | 666A45F425AEC1040055B96E /* Debug */,
840 | 666A45F525AEC1040055B96E /* Release */,
841 | );
842 | defaultConfigurationIsVisible = 0;
843 | defaultConfigurationName = Release;
844 | };
845 | 66FE200024BC7AE50090F669 /* Build configuration list for PBXNativeTarget "ScooterRentalAppClip" */ = {
846 | isa = XCConfigurationList;
847 | buildConfigurations = (
848 | 66FE1FFE24BC7AE50090F669 /* Debug */,
849 | 66FE1FFF24BC7AE50090F669 /* Release */,
850 | );
851 | defaultConfigurationIsVisible = 0;
852 | defaultConfigurationName = Release;
853 | };
854 | /* End XCConfigurationList section */
855 | };
856 | rootObject = 66576BE924B850FD0010E1E8 /* Project object */;
857 | }
858 |
--------------------------------------------------------------------------------
/ScooterRentalApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ScooterRentalApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ScooterRentalApp.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ScooterRentalApp.xcodeproj/xcshareddata/xcschemes/Scooter Rental App Clip.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
58 |
59 |
60 |
61 |
67 |
69 |
75 |
76 |
77 |
78 |
80 |
81 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/ScooterRentalApp.xcodeproj/xcshareddata/xcschemes/Scooter Rental App Widget Extension.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
10 |
16 |
22 |
23 |
24 |
30 |
36 |
37 |
38 |
39 |
40 |
45 |
46 |
47 |
48 |
60 |
63 |
69 |
70 |
71 |
72 |
78 |
79 |
80 |
81 |
85 |
86 |
90 |
91 |
95 |
96 |
97 |
98 |
106 |
108 |
114 |
115 |
116 |
117 |
119 |
120 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/ScooterRentalApp.xcodeproj/xcshareddata/xcschemes/Scooter Rental App.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/ScooterRentalAppWidgetExtensionExtension.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.application-groups
6 |
7 | group.company.ScooterRentalApp
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "icon-ios-20@2x.png",
5 | "idiom" : "iphone",
6 | "scale" : "2x",
7 | "size" : "20x20"
8 | },
9 | {
10 | "filename" : "icon-ios-20@3x.png",
11 | "idiom" : "iphone",
12 | "scale" : "3x",
13 | "size" : "20x20"
14 | },
15 | {
16 | "filename" : "icon-ios-29@2x.png",
17 | "idiom" : "iphone",
18 | "scale" : "2x",
19 | "size" : "29x29"
20 | },
21 | {
22 | "filename" : "icon-ios-29@3x.png",
23 | "idiom" : "iphone",
24 | "scale" : "3x",
25 | "size" : "29x29"
26 | },
27 | {
28 | "filename" : "icon-ios-40@2x.png",
29 | "idiom" : "iphone",
30 | "scale" : "2x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "filename" : "icon-ios-40@3x.png",
35 | "idiom" : "iphone",
36 | "scale" : "3x",
37 | "size" : "40x40"
38 | },
39 | {
40 | "filename" : "icon-ios-60@2x.png",
41 | "idiom" : "iphone",
42 | "scale" : "2x",
43 | "size" : "60x60"
44 | },
45 | {
46 | "filename" : "icon-ios-60@3x.png",
47 | "idiom" : "iphone",
48 | "scale" : "3x",
49 | "size" : "60x60"
50 | },
51 | {
52 | "idiom" : "ipad",
53 | "scale" : "1x",
54 | "size" : "20x20"
55 | },
56 | {
57 | "idiom" : "ipad",
58 | "scale" : "2x",
59 | "size" : "20x20"
60 | },
61 | {
62 | "idiom" : "ipad",
63 | "scale" : "1x",
64 | "size" : "29x29"
65 | },
66 | {
67 | "idiom" : "ipad",
68 | "scale" : "2x",
69 | "size" : "29x29"
70 | },
71 | {
72 | "idiom" : "ipad",
73 | "scale" : "1x",
74 | "size" : "40x40"
75 | },
76 | {
77 | "idiom" : "ipad",
78 | "scale" : "2x",
79 | "size" : "40x40"
80 | },
81 | {
82 | "idiom" : "ipad",
83 | "scale" : "1x",
84 | "size" : "76x76"
85 | },
86 | {
87 | "idiom" : "ipad",
88 | "scale" : "2x",
89 | "size" : "76x76"
90 | },
91 | {
92 | "idiom" : "ipad",
93 | "scale" : "2x",
94 | "size" : "83.5x83.5"
95 | },
96 | {
97 | "filename" : "icon-ios-1024@1x.png",
98 | "idiom" : "ios-marketing",
99 | "scale" : "1x",
100 | "size" : "1024x1024"
101 | }
102 | ],
103 | "info" : {
104 | "author" : "xcode",
105 | "version" : 1
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/wwdc2020-tutorials/d32c405404a982a1058177fb32b8cf96849e4dbc/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-1024@1x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/wwdc2020-tutorials/d32c405404a982a1058177fb32b8cf96849e4dbc/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-20@2x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/wwdc2020-tutorials/d32c405404a982a1058177fb32b8cf96849e4dbc/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-20@3x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/wwdc2020-tutorials/d32c405404a982a1058177fb32b8cf96849e4dbc/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-29@2x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/wwdc2020-tutorials/d32c405404a982a1058177fb32b8cf96849e4dbc/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-29@3x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/wwdc2020-tutorials/d32c405404a982a1058177fb32b8cf96849e4dbc/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-40@2x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/wwdc2020-tutorials/d32c405404a982a1058177fb32b8cf96849e4dbc/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-40@3x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/wwdc2020-tutorials/d32c405404a982a1058177fb32b8cf96849e4dbc/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-60@2x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/wwdc2020-tutorials/d32c405404a982a1058177fb32b8cf96849e4dbc/Shared/Assets.xcassets/AppIcon.appiconset/icon-ios-60@3x.png
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/scooter-yellow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "scooter-yellow.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/scooter-yellow.imageset/scooter-yellow.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << >>
5 | endobj
6 |
7 | 2 0 obj
8 | << /Length 3 0 R >>
9 | stream
10 | /DeviceRGB CS
11 | /DeviceRGB cs
12 | q
13 | 1.000000 0.000000 -0.000000 1.000000 2.213196 27.096497 cm
14 | 0.970833 0.863233 0.586545 scn
15 | 545.000000 272.500000 m
16 | 545.000000 122.002411 422.997589 0.000000 272.500000 0.000000 c
17 | 122.002403 0.000000 0.000000 122.002411 0.000000 272.500000 c
18 | 0.000000 422.997589 122.002403 545.000000 272.500000 545.000000 c
19 | 422.997589 545.000000 545.000000 422.997589 545.000000 272.500000 c
20 | h
21 | f
22 | n
23 | Q
24 | q
25 | 0.707107 0.707107 -0.707107 0.707107 130.593475 -32.036789 cm
26 | 0.000000 0.000000 0.000000 scn
27 | 64.380280 114.996994 m
28 | 64.380280 101.361008 53.326130 90.306854 39.690140 90.306854 c
29 | 39.690140 60.306854 l
30 | 69.894669 60.306854 94.380280 84.792465 94.380280 114.996994 c
31 | 64.380280 114.996994 l
32 | h
33 | 39.690140 90.306854 m
34 | 26.054152 90.306854 15.000000 101.361008 15.000000 114.996994 c
35 | -15.000000 114.996994 l
36 | -15.000000 84.792465 9.485608 60.306854 39.690140 60.306854 c
37 | 39.690140 90.306854 l
38 | h
39 | 15.000000 114.996994 m
40 | 15.000000 128.632980 26.054152 139.687134 39.690140 139.687134 c
41 | 39.690140 169.687134 l
42 | 9.485608 169.687134 -15.000000 145.201523 -15.000000 114.996994 c
43 | 15.000000 114.996994 l
44 | h
45 | 39.690140 139.687134 m
46 | 53.326130 139.687134 64.380280 128.632980 64.380280 114.996994 c
47 | 94.380280 114.996994 l
48 | 94.380280 145.201523 69.894669 169.687134 39.690140 169.687134 c
49 | 39.690140 139.687134 l
50 | h
51 | f
52 | n
53 | Q
54 | q
55 | 0.707113 0.707101 -0.707113 0.707101 547.330994 -32.049835 cm
56 | 0.000000 0.000000 0.000000 scn
57 | 64.380188 114.995575 m
58 | 64.380188 101.359612 53.326057 90.305481 39.690094 90.305481 c
59 | 39.690094 60.305481 l
60 | 69.894600 60.305481 94.380188 84.791069 94.380188 114.995575 c
61 | 64.380188 114.995575 l
62 | h
63 | 39.690094 90.305481 m
64 | 26.054131 90.305481 15.000000 101.359612 15.000000 114.995575 c
65 | -15.000000 114.995575 l
66 | -15.000000 84.791069 9.485588 60.305481 39.690094 60.305481 c
67 | 39.690094 90.305481 l
68 | h
69 | 15.000000 114.995575 m
70 | 15.000000 128.631531 26.054131 139.685669 39.690094 139.685669 c
71 | 39.690094 169.685669 l
72 | 9.485588 169.685669 -15.000000 145.200073 -15.000000 114.995575 c
73 | 15.000000 114.995575 l
74 | h
75 | 39.690094 139.685669 m
76 | 53.326057 139.685669 64.380188 128.631531 64.380188 114.995575 c
77 | 94.380188 114.995575 l
78 | 94.380188 145.200073 69.894600 169.685669 39.690094 169.685669 c
79 | 39.690094 139.685669 l
80 | h
81 | f
82 | n
83 | Q
84 | q
85 | 1.000000 0.000000 -0.000000 1.000000 77.205933 45.211365 cm
86 | 0.000000 0.000000 0.000000 scn
87 | 388.709015 121.502487 m
88 | 393.890900 127.966003 392.851959 137.406479 386.388428 142.588379 c
89 | 379.924896 147.770264 370.484436 146.731308 365.302521 140.267776 c
90 | 388.709015 121.502487 l
91 | h
92 | 297.636139 31.885132 m
93 | 297.636139 16.885132 l
94 | 302.187408 16.885132 306.492523 18.951515 309.339386 22.502487 c
95 | 297.636139 31.885132 l
96 | h
97 | 99.212044 31.885132 m
98 | 84.212044 31.885132 l
99 | 84.212044 23.600861 90.927773 16.885132 99.212044 16.885132 c
100 | 99.212044 31.885132 l
101 | h
102 | 0.000000 145.885132 m
103 | -8.284271 145.885132 -15.000000 139.169403 -15.000000 130.885132 c
104 | -15.000000 122.600861 -8.284271 115.885132 0.000000 115.885132 c
105 | 0.000000 145.885132 l
106 | h
107 | 365.302521 140.267776 m
108 | 285.932892 41.267784 l
109 | 309.339386 22.502487 l
110 | 388.709015 121.502487 l
111 | 365.302521 140.267776 l
112 | h
113 | 297.636139 46.885132 m
114 | 99.212044 46.885132 l
115 | 99.212044 16.885132 l
116 | 297.636139 16.885132 l
117 | 297.636139 46.885132 l
118 | h
119 | 114.212044 31.885139 m
120 | 114.212044 62.130188 102.171211 91.129982 80.748756 112.506645 c
121 | 59.558262 91.270767 l
122 | 75.347542 75.515228 84.212044 54.152954 84.212044 31.885132 c
123 | 114.212044 31.885139 l
124 | h
125 | 80.748756 112.506645 m
126 | 59.327389 133.882233 30.280573 145.885132 0.000000 145.885132 c
127 | 0.000000 115.885132 l
128 | 22.344769 115.885132 43.767895 107.027374 59.558262 91.270767 c
129 | 80.748756 112.506645 l
130 | h
131 | f
132 | n
133 | Q
134 | q
135 | 1.000000 0.000000 -0.000000 1.000000 375.210449 122.149994 cm
136 | 0.000000 0.000000 0.000000 scn
137 | 14.619748 415.302551 m
138 | 12.766260 423.376801 4.718228 428.419739 -3.356035 426.566254 c
139 | -11.430299 424.712769 -16.473234 416.664734 -14.619748 408.590454 c
140 | 14.619748 415.302551 l
141 | h
142 | 72.381584 29.590454 m
143 | 74.235069 21.516205 82.283104 16.473267 90.357368 18.326752 c
144 | 98.431625 20.180237 103.474564 28.228271 101.621078 36.302521 c
145 | 72.381584 29.590454 l
146 | h
147 | -14.619748 408.590454 m
148 | 72.381584 29.590454 l
149 | 101.621078 36.302521 l
150 | 14.619748 415.302551 l
151 | -14.619748 408.590454 l
152 | h
153 | f
154 | n
155 | Q
156 | q
157 | 1.000000 0.000000 -0.000000 1.000000 434.211365 515.116394 cm
158 | 0.000000 0.000000 0.000000 scn
159 | -3.408444 47.587723 m
160 | -11.476009 45.705288 -16.490049 37.639225 -14.607618 29.571659 c
161 | -12.725186 21.504095 -4.659121 16.490053 3.408444 18.372484 c
162 | -3.408444 47.587723 l
163 | h
164 | 63.408443 32.372482 m
165 | 71.476006 34.254917 76.490051 42.320984 74.607620 50.388546 c
166 | 72.725182 58.456112 64.659119 63.470154 56.591553 61.587723 c
167 | 63.408443 32.372482 l
168 | h
169 | 3.408444 18.372484 m
170 | 63.408443 32.372482 l
171 | 56.591553 61.587723 l
172 | -3.408444 47.587723 l
173 | 3.408444 18.372484 l
174 | h
175 | f
176 | n
177 | Q
178 | q
179 | 1.000000 0.000000 -0.000000 1.000000 257.208679 473.078125 cm
180 | 0.000000 0.000000 0.000000 scn
181 | -3.468867 47.611759 m
182 | -11.528572 45.695957 -16.509190 37.609211 -14.593388 29.549503 c
183 | -12.677586 21.489799 -4.590838 16.509182 3.468867 18.424984 c
184 | -3.468867 47.611759 l
185 | h
186 | 125.470734 47.424984 m
187 | 133.530441 49.340786 138.511047 57.427532 136.595245 65.487236 c
188 | 134.679443 73.546944 126.592705 78.527557 118.532997 76.611755 c
189 | 125.470734 47.424984 l
190 | h
191 | 3.468867 18.424984 m
192 | 125.470734 47.424984 l
193 | 118.532997 76.611755 l
194 | -3.468867 47.611759 l
195 | 3.468867 18.424984 l
196 | h
197 | f
198 | n
199 | Q
200 |
201 | endstream
202 | endobj
203 |
204 | 3 0 obj
205 | 5252
206 | endobj
207 |
208 | 4 0 obj
209 | << /Annots []
210 | /Type /Page
211 | /MediaBox [ 0.000000 0.000000 550.212280 577.099854 ]
212 | /Resources 1 0 R
213 | /Contents 2 0 R
214 | /Parent 5 0 R
215 | >>
216 | endobj
217 |
218 | 5 0 obj
219 | << /Kids [ 4 0 R ]
220 | /Count 1
221 | /Type /Pages
222 | >>
223 | endobj
224 |
225 | 6 0 obj
226 | << /Type /Catalog
227 | /Pages 5 0 R
228 | >>
229 | endobj
230 |
231 | xref
232 | 0 7
233 | 0000000000 65535 f
234 | 0000000010 00000 n
235 | 0000000034 00000 n
236 | 0000005342 00000 n
237 | 0000005365 00000 n
238 | 0000005540 00000 n
239 | 0000005614 00000 n
240 | trailer
241 | << /ID [ (some) (id) ]
242 | /Root 6 0 R
243 | /Size 7
244 | >>
245 | startxref
246 | 5673
247 | %%EOF
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/scooter.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "scooter.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Shared/Assets.xcassets/scooter.imageset/scooter.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << /BBox [ 0.000000 0.000000 32.000000 32.000000 ]
5 | /Resources << >>
6 | /Subtype /Form
7 | /Length 2 0 R
8 | /Group << /Type /Group
9 | /S /Transparency
10 | >>
11 | /Type /XObject
12 | >>
13 | stream
14 | /DeviceRGB CS
15 | /DeviceRGB cs
16 | q
17 | 1.000000 0.000000 -0.000000 1.000000 -0.000005 -0.249161 cm
18 | 0.972549 0.972549 0.972549 scn
19 | 24.590006 3.839161 m
20 | 24.379932 4.063416 24.224356 4.333057 24.135351 4.627165 c
21 | 24.046347 4.921272 24.026306 5.231932 24.076786 5.535036 c
22 | 24.127266 5.838140 24.246910 6.125538 24.426430 6.374924 c
23 | 24.605949 6.624310 24.840519 6.828976 25.111935 6.973040 c
24 | 25.383350 7.117104 25.684309 7.196693 25.991459 7.205627 c
25 | 26.298609 7.214561 26.603685 7.152605 26.883017 7.024561 c
26 | 27.162348 6.896517 27.408417 6.705830 27.602131 6.467302 c
27 | 27.795845 6.228775 27.931992 5.948818 28.000006 5.649160 c
28 | 29.711868 7.590233 30.927311 9.917603 31.542068 12.431625 c
29 | 32.156826 14.945648 32.152550 17.571278 31.529608 20.083282 c
30 | 30.906666 22.595291 29.683649 24.918690 27.965473 26.854177 c
31 | 26.247297 28.789663 24.085251 30.279465 21.664797 31.195780 c
32 | 19.244343 32.112095 16.637730 32.427570 14.068561 32.115150 c
33 | 11.499392 31.802731 9.044357 30.871737 6.914052 29.402031 c
34 | 4.783746 27.932325 3.041761 25.967779 1.837527 23.676914 c
35 | 0.633293 21.386051 0.002756 18.837254 0.000005 16.249161 c
36 | -0.002857 12.685291 1.190930 9.223663 3.390005 6.419161 c
37 | 3.555955 6.661686 3.773310 6.864658 4.026609 7.013645 c
38 | 4.279909 7.162632 4.562927 7.253969 4.855533 7.281158 c
39 | 5.148138 7.308348 5.443141 7.270723 5.719554 7.170959 c
40 | 5.995967 7.071196 6.247000 6.911745 6.454795 6.703951 c
41 | 6.662590 6.496157 6.822040 6.245123 6.921803 5.968710 c
42 | 7.021567 5.692297 7.059192 5.397295 7.032002 5.104689 c
43 | 7.004813 4.812082 6.913476 4.529064 6.764490 4.275764 c
44 | 6.615504 4.022465 6.412529 3.805109 6.170005 3.639160 c
45 | 8.905820 1.494228 12.268532 0.303759 15.744518 0.249588 c
46 | 19.220505 0.195415 22.618681 1.280521 25.420006 3.339161 c
47 | 25.106537 3.435274 24.821507 3.606979 24.590006 3.839161 c
48 | 24.590006 3.839161 l
49 | h
50 | f
51 | n
52 | Q
53 | q
54 | 0.707107 0.707107 -0.707107 0.707107 8.168990 -0.996350 cm
55 | 0.000000 0.000000 0.000000 scn
56 | 3.000000 6.485283 m
57 | 3.000000 5.932998 2.552285 5.485283 2.000000 5.485283 c
58 | 2.000000 3.485283 l
59 | 3.656854 3.485283 5.000000 4.828428 5.000000 6.485283 c
60 | 3.000000 6.485283 l
61 | h
62 | 2.000000 5.485283 m
63 | 1.447715 5.485283 1.000000 5.932998 1.000000 6.485283 c
64 | -1.000000 6.485283 l
65 | -1.000000 4.828428 0.343146 3.485283 2.000000 3.485283 c
66 | 2.000000 5.485283 l
67 | h
68 | 1.000000 6.485283 m
69 | 1.000000 7.037568 1.447715 7.485283 2.000000 7.485283 c
70 | 2.000000 9.485283 l
71 | 0.343146 9.485283 -1.000000 8.142138 -1.000000 6.485283 c
72 | 1.000000 6.485283 l
73 | h
74 | 2.000000 7.485283 m
75 | 2.552285 7.485283 3.000000 7.037568 3.000000 6.485283 c
76 | 5.000000 6.485283 l
77 | 5.000000 8.142138 3.656854 9.485283 2.000000 9.485283 c
78 | 2.000000 7.485283 l
79 | h
80 | f
81 | n
82 | Q
83 | q
84 | 0.707107 0.707107 -0.707107 0.707107 29.168232 -0.997109 cm
85 | 0.000000 0.000000 0.000000 scn
86 | 3.000000 6.485283 m
87 | 3.000000 5.932998 2.552285 5.485283 2.000000 5.485283 c
88 | 2.000000 3.485283 l
89 | 3.656854 3.485283 5.000000 4.828428 5.000000 6.485283 c
90 | 3.000000 6.485283 l
91 | h
92 | 2.000000 5.485283 m
93 | 1.447715 5.485283 1.000000 5.932998 1.000000 6.485283 c
94 | -1.000000 6.485283 l
95 | -1.000000 4.828428 0.343146 3.485283 2.000000 3.485283 c
96 | 2.000000 5.485283 l
97 | h
98 | 1.000000 6.485283 m
99 | 1.000000 7.037568 1.447715 7.485283 2.000000 7.485283 c
100 | 2.000000 9.485283 l
101 | 0.343146 9.485283 -1.000000 8.142138 -1.000000 6.485283 c
102 | 1.000000 6.485283 l
103 | h
104 | 2.000000 7.485283 m
105 | 2.552285 7.485283 3.000000 7.037568 3.000000 6.485283 c
106 | 5.000000 6.485283 l
107 | 5.000000 8.142138 3.656854 9.485283 2.000000 9.485283 c
108 | 2.000000 7.485283 l
109 | h
110 | f
111 | n
112 | Q
113 | q
114 | 1.000000 0.000000 -0.000000 1.000000 5.000000 2.874123 cm
115 | 0.000000 0.000000 0.000000 scn
116 | 19.780869 6.501183 m
117 | 20.125879 6.932444 20.055958 7.561737 19.624695 7.906746 c
118 | 19.193434 8.251756 18.564140 8.181834 18.219131 7.750572 c
119 | 19.780869 6.501183 l
120 | h
121 | 15.000000 2.125877 m
122 | 15.000000 1.125877 l
123 | 15.303783 1.125877 15.591097 1.263967 15.780869 1.501183 c
124 | 15.000000 2.125877 l
125 | h
126 | 5.000000 2.125877 m
127 | 4.000000 2.125877 l
128 | 4.000000 1.573593 4.447715 1.125877 5.000000 1.125877 c
129 | 5.000000 2.125877 l
130 | h
131 | 0.000000 8.125877 m
132 | -0.552285 8.125877 -1.000000 7.678162 -1.000000 7.125877 c
133 | -1.000000 6.573593 -0.552285 6.125877 0.000000 6.125877 c
134 | 0.000000 8.125877 l
135 | h
136 | 18.219131 7.750572 m
137 | 14.219131 2.750572 l
138 | 15.780869 1.501183 l
139 | 19.780869 6.501183 l
140 | 18.219131 7.750572 l
141 | h
142 | 15.000000 3.125877 m
143 | 5.000000 3.125877 l
144 | 5.000000 1.125877 l
145 | 15.000000 1.125877 l
146 | 15.000000 3.125877 l
147 | h
148 | 6.000000 2.125877 m
149 | 6.000000 3.717176 5.367859 5.243299 4.242640 6.368518 c
150 | 2.828427 4.954305 l
151 | 3.578573 4.204159 4.000000 3.186743 4.000000 2.125877 c
152 | 6.000000 2.125877 l
153 | h
154 | 4.242640 6.368518 m
155 | 3.117422 7.493736 1.591299 8.125877 0.000000 8.125877 c
156 | 0.000000 6.125877 l
157 | 1.060866 6.125877 2.078282 5.704450 2.828427 4.954305 c
158 | 4.242640 6.368518 l
159 | h
160 | f
161 | n
162 | Q
163 | q
164 | 1.000000 0.000000 -0.000000 1.000000 20.000000 6.713015 cm
165 | 0.000000 0.000000 0.000000 scn
166 | 0.974451 21.511583 m
167 | 0.850409 22.049759 0.313576 22.385479 -0.224599 22.261436 c
168 | -0.762773 22.137394 -1.098494 21.600561 -0.974451 21.062387 c
169 | 0.974451 21.511583 l
170 | h
171 | 3.425548 1.972387 m
172 | 3.549591 1.434212 4.086423 1.098492 4.624598 1.222534 c
173 | 5.162773 1.346577 5.498493 1.883410 5.374451 2.421583 c
174 | 3.425548 1.972387 l
175 | h
176 | -0.974451 21.062387 m
177 | 3.425548 1.972387 l
178 | 5.374451 2.421583 l
179 | 0.974451 21.511583 l
180 | -0.974451 21.062387 l
181 | h
182 | f
183 | n
184 | Q
185 | q
186 | 1.000000 0.000000 -0.000000 1.000000 22.980000 26.508820 cm
187 | 0.000000 0.000000 0.000000 scn
188 | -0.231186 3.174089 m
189 | -0.768509 3.046408 -1.100590 2.507316 -0.972910 1.969993 c
190 | -0.845229 1.432670 -0.306137 1.100590 0.231186 1.228270 c
191 | -0.231186 3.174089 l
192 | h
193 | 3.261187 1.948270 m
194 | 3.798510 2.075951 4.130591 2.615043 4.002910 3.152366 c
195 | 3.875229 3.689689 3.336137 4.021770 2.798814 3.894089 c
196 | 3.261187 1.948270 l
197 | h
198 | 0.231186 1.228270 m
199 | 3.261187 1.948270 l
200 | 2.798814 3.894089 l
201 | -0.231186 3.174089 l
202 | 0.231186 1.228270 l
203 | h
204 | f
205 | n
206 | Q
207 | q
208 | 1.000000 0.000000 -0.000000 1.000000 14.070000 24.388725 cm
209 | 0.000000 0.000000 0.000000 scn
210 | -0.231335 3.174148 m
211 | -0.768638 3.046386 -1.100637 2.507243 -0.972874 1.969939 c
212 | -0.845111 1.432636 -0.305969 1.100637 0.231335 1.228400 c
213 | -0.231335 3.174148 l
214 | h
215 | 6.371334 2.688400 m
216 | 6.908638 2.816163 7.240636 3.355305 7.112874 3.892609 c
217 | 6.985111 4.429913 6.445968 4.761911 5.908665 4.634148 c
218 | 6.371334 2.688400 l
219 | h
220 | 0.231335 1.228400 m
221 | 6.371334 2.688400 l
222 | 5.908665 4.634148 l
223 | -0.231335 3.174148 l
224 | 0.231335 1.228400 l
225 | h
226 | f
227 | n
228 | Q
229 |
230 | endstream
231 | endobj
232 |
233 | 2 0 obj
234 | 6187
235 | endobj
236 |
237 | 3 0 obj
238 | << /BBox [ 0.000000 0.000000 32.000000 32.000000 ]
239 | /Resources << >>
240 | /Subtype /Form
241 | /Length 4 0 R
242 | /Group << /Type /Group
243 | /S /Transparency
244 | >>
245 | /Type /XObject
246 | >>
247 | stream
248 | /DeviceRGB CS
249 | /DeviceRGB cs
250 | q
251 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
252 | 0.000000 0.000000 0.000000 scn
253 | 0.000000 32.000000 m
254 | 32.000000 32.000000 l
255 | 32.000000 0.000000 l
256 | 0.000000 0.000000 l
257 | 0.000000 32.000000 l
258 | h
259 | f
260 | n
261 | Q
262 |
263 | endstream
264 | endobj
265 |
266 | 4 0 obj
267 | 232
268 | endobj
269 |
270 | 5 0 obj
271 | << /XObject << /X1 1 0 R >>
272 | /ExtGState << /E1 << /SMask << /Type /Mask
273 | /G 3 0 R
274 | /S /Alpha
275 | >>
276 | /Type /ExtGState
277 | >> >>
278 | >>
279 | endobj
280 |
281 | 6 0 obj
282 | << /Length 7 0 R >>
283 | stream
284 | /DeviceRGB CS
285 | /DeviceRGB cs
286 | q
287 | /E1 gs
288 | /X1 Do
289 | Q
290 |
291 | endstream
292 | endobj
293 |
294 | 7 0 obj
295 | 46
296 | endobj
297 |
298 | 8 0 obj
299 | << /Annots []
300 | /Type /Page
301 | /MediaBox [ 0.000000 0.000000 32.000000 32.000000 ]
302 | /Resources 5 0 R
303 | /Contents 6 0 R
304 | /Parent 9 0 R
305 | >>
306 | endobj
307 |
308 | 9 0 obj
309 | << /Kids [ 8 0 R ]
310 | /Count 1
311 | /Type /Pages
312 | >>
313 | endobj
314 |
315 | 10 0 obj
316 | << /Type /Catalog
317 | /Pages 9 0 R
318 | >>
319 | endobj
320 |
321 | xref
322 | 0 11
323 | 0000000000 65535 f
324 | 0000000010 00000 n
325 | 0000006445 00000 n
326 | 0000006468 00000 n
327 | 0000006948 00000 n
328 | 0000006970 00000 n
329 | 0000007268 00000 n
330 | 0000007370 00000 n
331 | 0000007391 00000 n
332 | 0000007564 00000 n
333 | 0000007638 00000 n
334 | trailer
335 | << /ID [ (some) (id) ]
336 | /Root 10 0 R
337 | /Size 11
338 | >>
339 | startxref
340 | 7698
341 | %%EOF
--------------------------------------------------------------------------------
/Shared/Components/ChatView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct SupportChatView: View {
4 |
5 | @Environment(\.presentationMode) var presentationMode
6 | @State var textValue: String = ""
7 |
8 | var body: some View {
9 | NavigationView {
10 | VStack(spacing: 10) {
11 | VStack(spacing: 20) {
12 | HStack {
13 | VStack(alignment: .trailing, spacing: 5) {
14 | Text("Hello. How can I help you? 🙂")
15 | Text("18:06").font(Font.system(size: 12)).foregroundColor(Color.white.opacity(0.5))
16 | }
17 | .padding(10)
18 | .background(Color.blue.opacity(0.8))
19 | .foregroundColor(Color.white)
20 | .cornerRadius(10)
21 | Spacer()
22 | }
23 | HStack {
24 | Spacer()
25 | VStack(alignment: .trailing, spacing: 5) {
26 | Text("I'm not able to link my credit card 😞")
27 | Text("18:06").font(Font.system(size: 12)).foregroundColor(Color.white.opacity(0.5))
28 | }
29 | .padding(10)
30 | .background(Color.green.opacity(0.8))
31 | .foregroundColor(Color.white)
32 | .cornerRadius(10)
33 | }
34 | }
35 | Spacer()
36 | Divider()
37 | HStack {
38 | TextField("Write a message...", text: $textValue)
39 | Button(action: {}) {
40 | Image(systemName: "paperplane.fill").resizable()
41 | .aspectRatio(contentMode: .fit)
42 | .padding(EdgeInsets(top: 12, leading: 10, bottom: 10, trailing: 12))
43 | .frame(width: 40, height: 40)
44 | .background(Color.blue)
45 | .foregroundColor(Color.white)
46 | .clipShape(Circle())
47 | .shadow(color: Color.blue.opacity(0.1), radius: 3, x: 0, y: 0)
48 | }
49 | }
50 | }.padding(15).navigationTitle("Chat").navigationBarItems(trailing: Button(action: {
51 | presentationMode.wrappedValue.dismiss()
52 | }) {
53 | HStack(spacing: 10) {
54 | Image(systemName: "xmark")
55 | }.padding(10).background(Color.black.opacity(0.4)).clipShape(Circle()).accentColor(.white)
56 | })
57 | }
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/Shared/Components/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import MapKit
3 |
4 | struct ContentView: View {
5 |
6 | @EnvironmentObject private var model: AppModel
7 |
8 | var body: some View {
9 | NavigationView {
10 | MainView()
11 | .navigationBarItems(
12 | leading: Text("")
13 | .foregroundColor(.black)
14 | .font(Font.system(size: 36, weight: .bold, design: .rounded)),
15 | trailing: Button(action: { model.isChatPresented.toggle() }) {
16 | HStack(spacing: 10) {
17 | Image(systemName: "questionmark.circle.fill")
18 | }
19 | .padding(10)
20 | .background(Color.white)
21 | .clipShape(Circle())
22 | .overlay(Circle().stroke(Color.gray.opacity(0.2), lineWidth: 0.5))
23 | .shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 0)
24 | .accentColor(Color.black.opacity(0.7))
25 | }
26 | .fullScreenCover(isPresented: $model.isChatPresented, content: SupportChatView.init)
27 | )
28 | .environmentObject(model)
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Shared/Components/FareView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct FareView: View {
4 |
5 | @Binding var selectedScooter: Scooter?
6 | @State private var isPresented = false
7 |
8 | var body: some View {
9 | HStack(alignment: .top) {
10 | VStack(alignment: .leading, spacing: 5) {
11 | Text("Starts from ")
12 | .font(Font.system(size: 16, weight: .regular, design: .default))
13 | .foregroundColor(Color.secondary)
14 | + Text(String(format: "$%.0f", selectedScooter?.reservationFee ?? 0))
15 | .font(Font.system(size: 16, weight: .medium, design: .default))
16 | Text(String(format: "$%.0f ", selectedScooter?.costPerMinute ?? 0))
17 | .font(Font.system(size: 16, weight: .medium, design: .default))
18 | + Text("each minute")
19 | .font(Font.system(size: 16, weight: .regular, design: .default))
20 | .foregroundColor(Color.secondary)
21 | }
22 | }
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/Shared/Components/MainView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct MainView: View {
4 |
5 | @EnvironmentObject private var model: AppModel
6 | @State var selectedScooter: Scooter?
7 |
8 | var body: some View {
9 | ZStack(alignment: .bottom) {
10 | MapView(scooters: $model.scooters, selectedScooter: $model.selectedScooter)
11 | .zIndex(0)
12 | .edgesIgnoringSafeArea(.all)
13 |
14 | if model.selectedScooter != nil {
15 | PopupView(selectedScooter: $model.selectedScooter)
16 | .zIndex(1)
17 | .transition(.moveAndFade)
18 | .environmentObject(model)
19 | }
20 | }
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/Shared/Components/MapView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import MapKit
3 |
4 | struct MapView: UIViewRepresentable {
5 |
6 | @Binding var scooters: [Scooter]
7 | @Binding var selectedScooter: Scooter?
8 |
9 | func makeCoordinator() -> Coordinator {
10 | Coordinator(self, selectedScooter: $selectedScooter)
11 | }
12 |
13 |
14 | func makeUIView(context: Context) -> MKMapView {
15 | MKMapView(frame: .zero)
16 | }
17 |
18 | func updateUIView(_ uiView: MKMapView, context: Context) {
19 | uiView.delegate = context.coordinator
20 |
21 | let coordinate = CLLocationCoordinate2D(latitude: 40.736212, longitude: -73.993406)
22 | let span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02)
23 | let region = MKCoordinateRegion(center: coordinate, span: span)
24 | uiView.setRegion(region, animated: true)
25 |
26 | if scooters.count != uiView.annotations.count {
27 | uiView.removeAnnotations(uiView.annotations)
28 | uiView.addAnnotations(scooters.map {
29 | let annotation = ScooterAnnotation()
30 | annotation.coordinate = $0.location
31 | annotation.scooter = $0
32 | return annotation
33 | })
34 | }
35 | }
36 |
37 | class Coordinator: NSObject, MKMapViewDelegate {
38 |
39 | var parent: MapView
40 | @Binding var selectedScooter: Scooter?
41 |
42 | init(_ parent: MapView, selectedScooter: Binding) {
43 | self.parent = parent
44 | self._selectedScooter = selectedScooter
45 | }
46 |
47 | func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
48 | guard !(annotation is MKUserLocation) else {
49 | return .none
50 | }
51 | let identifier = "ScooterAnnotation"
52 | let annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
53 | annotationView.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
54 | annotationView.canShowCallout = false
55 | return annotationView
56 | }
57 |
58 | func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
59 | guard let annotation = view.annotation as? ScooterAnnotation else {
60 | return
61 | }
62 | withAnimation {
63 | selectedScooter = annotation.scooter
64 | }
65 | mapView.setCenter(view.annotation!.coordinate, animated: true)
66 | }
67 |
68 | func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
69 | withAnimation {
70 | selectedScooter = nil
71 | }
72 | }
73 |
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/Shared/Components/PopupView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import WidgetKit
3 |
4 | struct PopupView: View {
5 |
6 | @EnvironmentObject private var model: AppModel
7 |
8 | @Binding var selectedScooter: Scooter?
9 | @State var isPresented: Bool = false
10 |
11 | var body: some View {
12 | ZStack(alignment: .top) {
13 | VStack {
14 | VStack(alignment: .center) {
15 | VStack(alignment: .center, spacing: 15) {
16 | Text(selectedScooter?.name ?? "")
17 | .font(Font.system(size: 22, weight: .semibold, design: .rounded))
18 | Text((selectedScooter?.speed.rawValue ?? "").uppercased())
19 | .foregroundColor(selectedScooter?.speed == .normal ? Color.orange : Color.green)
20 | .font(Font.system(size: 14, weight: .bold, design: .rounded))
21 | .padding(5)
22 | .overlay(RoundedRectangle(cornerRadius: 5).stroke(selectedScooter?.speed == .normal ? Color.orange : Color.green, lineWidth: 2))
23 | }
24 | .padding(.bottom, 15)
25 | .padding(.top, 50)
26 |
27 | HStack(alignment: .top) {
28 | FareView(selectedScooter: $selectedScooter)
29 | Spacer()
30 | VStack(alignment: .trailing, spacing: 5) {
31 | HStack {
32 | Image(systemName: "battery.100.bolt").foregroundColor(getBatteryStatusColor(selectedScooter?.batteryCharge ?? 0))
33 | (Text("\(selectedScooter?.batteryCharge ?? 0)") + Text("%"))
34 | .foregroundColor(getBatteryStatusColor(selectedScooter?.batteryCharge ?? 0))
35 | .font(Font.system(size: 16, weight: .medium, design: .rounded))
36 | }
37 | Text("≈ 15 minutes or 8 km")
38 | .font(Font.system(size: 16, weight: .regular, design: .default))
39 | .foregroundColor(Color.secondary)
40 | }
41 | }
42 |
43 | }
44 | .padding()
45 |
46 | Button("RIDE") {
47 | if let scooterId = selectedScooter?.id {
48 | model.setSelectedScooter(scooterId)
49 | }
50 | model.setRideStartDate(Date())
51 | WidgetCenter.shared.reloadTimelines(ofKind: "ScooterRideWidget")
52 | isPresented.toggle()
53 | }
54 | .frame(width: 90, height: 90)
55 | .foregroundColor(Color.white)
56 | .font(Font.system(size: 20, weight: .bold, design: .rounded))
57 | .background(Color.orange)
58 | .clipShape(Circle())
59 | .shadow(color: Color.orange.opacity(0.4), radius: 15, x: 0, y: 0)
60 | .padding([.top, .bottom], 25)
61 | .popover(isPresented: $isPresented, content: UnlockedView.init)
62 | }
63 | .background(Color.white)
64 | .cornerRadius(20)
65 | .shadow(color: Color.black.opacity(0.3), radius: 25, x: 0, y: 5)
66 |
67 | ScooterIconView()
68 | .overlay(Circle().stroke(selectedScooter?.speed == .normal ? Color.orange.opacity(0.5) : Color.green.opacity(0.5), lineWidth: 3))
69 | .padding(.top, -45)
70 | }.padding([.leading, .trailing], 15)
71 | }
72 |
73 | func getBatteryStatusColor(_ charge: Int) -> Color {
74 | if charge >= 90 {
75 | return Color.green.opacity(0.7)
76 | }
77 | if charge < 90, charge >= 30 {
78 | return Color.yellow
79 | }
80 | return Color.red.opacity(0.7)
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/Shared/Components/RentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct MainView: View {
4 |
5 | @EnvironmentObject private var model: AppModel
6 | @State var selectedScooter: Scooter?
7 |
8 | var body: some View {
9 | ZStack(alignment: .bottom) {
10 | MapView(scooters: $model.scooters, selectedScooter: $model.selectedScooter)
11 | .zIndex(0)
12 | .edgesIgnoringSafeArea(.all)
13 |
14 | if model.selectedScooter != nil {
15 | PopupView(selectedScooter: $model.selectedScooter)
16 | .zIndex(1)
17 | .transition(.moveAndFade)
18 | }
19 | }
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/Shared/Components/ScooterIconView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ScooterIconView: View {
4 |
5 | var body: some View {
6 | Image("scooter")
7 | .resizable()
8 | .aspectRatio(contentMode: .fit)
9 | .padding(20)
10 | .frame(width: 100, height: 100)
11 | .background(Color.white)
12 | .clipShape(Circle())
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/Shared/Components/SupportChatView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct SupportChatView: View {
4 |
5 | @Environment(\.presentationMode) var presentationMode
6 | @State var textValue: String = ""
7 |
8 | var body: some View {
9 | NavigationView {
10 | VStack(spacing: 10) {
11 | VStack(spacing: 20) {
12 | HStack {
13 | VStack(alignment: .trailing, spacing: 5) {
14 | Text("Hello. How can I help you? 🙂")
15 | Text("18:06")
16 | .font(Font.system(size: 12))
17 | .foregroundColor(Color.white.opacity(0.5))
18 | }
19 | .padding(10)
20 | .background(Color.blue.opacity(0.8))
21 | .foregroundColor(Color.white)
22 | .cornerRadius(10)
23 | Spacer()
24 | }
25 | HStack {
26 | Spacer()
27 | VStack(alignment: .trailing, spacing: 5) {
28 | Text("I'm not able to link my credit card 😞")
29 | Text("18:06")
30 | .font(Font.system(size: 12))
31 | .foregroundColor(Color.white.opacity(0.5))
32 | }
33 | .padding(10)
34 | .background(Color.green.opacity(0.8))
35 | .foregroundColor(Color.white)
36 | .cornerRadius(10)
37 | }
38 | }
39 | Spacer()
40 | Divider()
41 | HStack {
42 | TextField("Write a message...", text: $textValue)
43 | Button(action: {}) {
44 | Image(systemName: "paperplane.fill").resizable()
45 | .aspectRatio(contentMode: .fit)
46 | .padding(EdgeInsets(top: 12, leading: 10, bottom: 10, trailing: 12))
47 | .frame(width: 40, height: 40)
48 | .background(Color.blue)
49 | .foregroundColor(Color.white)
50 | .clipShape(Circle())
51 | .shadow(color: Color.blue.opacity(0.1), radius: 3, x: 0, y: 0)
52 | }
53 | }
54 | }
55 | .padding(15)
56 | .navigationTitle("Chat")
57 | .navigationBarItems(trailing: Button(action: {
58 | presentationMode.wrappedValue.dismiss()
59 | }) {
60 | HStack(spacing: 10) {
61 | Image(systemName: "xmark")
62 | }
63 | .padding(10)
64 | .background(Color.black.opacity(0.4))
65 | .clipShape(Circle())
66 | .accentColor(.white)
67 | })
68 | }
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/Shared/Components/UnlockView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct UnlockedView: View {
4 |
5 | @Environment(\.presentationMode) var presentationMode
6 |
7 | var body: some View {
8 | VStack {
9 | HStack {
10 | Image(systemName: "lock.open.fill").resizable()
11 | .aspectRatio(contentMode: .fit)
12 | .padding(10)
13 | .frame(width: 40, height: 40)
14 | .background(Color.green)
15 | .foregroundColor(Color.white)
16 | .clipShape(Circle())
17 | Text("Unlocked")
18 | .font(Font.system(size: 48, weight: .bold, design: .rounded))
19 | .multilineTextAlignment(.leading)
20 | .foregroundColor(.green)
21 | }
22 |
23 | Text("Enjoy your ride")
24 | .font(Font.system(size: 36, weight: .semibold, design: .default))
25 | .multilineTextAlignment(.leading)
26 |
27 | Button(action: {
28 | presentationMode.wrappedValue.dismiss()
29 | }) {
30 | Image(systemName: "stop.fill").resizable()
31 | .aspectRatio(contentMode: .fit)
32 | .padding(35)
33 | .frame(width: 100, height: 100)
34 | .background(Color.red)
35 | .foregroundColor(Color.white)
36 | .clipShape(Circle())
37 | .shadow(color: Color.red.opacity(0.4), radius: 15, x: 0, y: 0)
38 | .offset(y: 50)
39 | }
40 |
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/Shared/Components/UnlockedView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import WidgetKit
3 |
4 | struct UnlockedView: View {
5 |
6 | @Environment(\.presentationMode) var presentationMode
7 |
8 | @EnvironmentObject private var model: AppModel
9 |
10 | var body: some View {
11 | VStack {
12 | HStack {
13 | Image(systemName: "lock.open.fill").resizable()
14 | .aspectRatio(contentMode: .fit)
15 | .padding(10)
16 | .frame(width: 40, height: 40)
17 | .background(Color.green)
18 | .foregroundColor(Color.white)
19 | .clipShape(Circle())
20 | Text("Unlocked")
21 | .font(Font.system(size: 48, weight: .bold, design: .rounded))
22 | .multilineTextAlignment(.leading)
23 | .foregroundColor(.green)
24 | }
25 |
26 | Text("Enjoy your ride")
27 | .font(Font.system(size: 36, weight: .semibold, design: .default))
28 | .multilineTextAlignment(.leading)
29 |
30 | Button(action: {
31 | model.resetRideStartDate()
32 | model.resetSelectedScooter()
33 | WidgetCenter.shared.reloadTimelines(ofKind: "ScooterRideWidget")
34 | presentationMode.wrappedValue.dismiss()
35 | }) {
36 | Image(systemName: "stop.fill").resizable()
37 | .aspectRatio(contentMode: .fit)
38 | .padding(35)
39 | .frame(width: 100, height: 100)
40 | .background(Color.red)
41 | .foregroundColor(Color.white)
42 | .clipShape(Circle())
43 | .shadow(color: Color.red.opacity(0.4), radius: 15, x: 0, y: 0)
44 | .offset(y: 50)
45 | }
46 |
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/Shared/Extensions/AnyTransition+Extensions.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | extension AnyTransition {
4 |
5 | static var moveAndFade: AnyTransition {
6 | let animation = AnyTransition.scale
7 | .combined(with: .move(edge: .bottom))
8 | .combined(with: .opacity)
9 | return .asymmetric(insertion: animation, removal: animation)
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/Shared/Model/AppModel.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MapKit
3 |
4 | class AppModel: ObservableObject {
5 |
6 | @Published var selectedScooter: Scooter?
7 | @Published var isChatPresented: Bool = false
8 |
9 | @Published var scooters: [Scooter] = [
10 | Scooter(
11 | id: 1,
12 | name: "S001",
13 | batteryCharge: 24,
14 | chargeConsumptionPerMinute: 1,
15 | reservationFee: 10,
16 | costPerMinute: 2,
17 | speed: .normal,
18 | location: CLLocationCoordinate2D(latitude: 40.735563, longitude: -73.991496)
19 | ),
20 | Scooter(
21 | id: 2,
22 | name: "S002",
23 | batteryCharge: 70,
24 | chargeConsumptionPerMinute: 1,
25 | reservationFee: 11,
26 | costPerMinute: 3,
27 | speed: .normal,
28 | location: CLLocationCoordinate2D(latitude: 40.737126, longitude: -73.994350)
29 | ),
30 | Scooter(
31 | id: 3,
32 | name: "S003",
33 | batteryCharge: 50,
34 | chargeConsumptionPerMinute: 2,
35 | reservationFee: 11,
36 | costPerMinute: 3,
37 | speed: .fast,
38 | location: CLLocationCoordinate2D(latitude: 40.740239, longitude: -73.989490))
39 | ,
40 | Scooter(
41 | id: 4,
42 | name: "S004",
43 | batteryCharge: 91,
44 | chargeConsumptionPerMinute: 2,
45 | reservationFee: 12,
46 | costPerMinute: 3,
47 | speed: .fast,
48 | location: CLLocationCoordinate2D(latitude: 40.741908, longitude: -73.990445)
49 | ),
50 | Scooter(
51 | id: 5,
52 | name: "S005",
53 | batteryCharge: 76,
54 | chargeConsumptionPerMinute: 2,
55 | reservationFee: 10,
56 | costPerMinute: 2,
57 | speed: .fast,
58 | location: CLLocationCoordinate2D(latitude: 40.750463, longitude: -73.989393)
59 | ),
60 | Scooter(
61 | id: 6,
62 | name: "S006",
63 | batteryCharge: 100,
64 | chargeConsumptionPerMinute: 1,
65 | reservationFee: 12,
66 | costPerMinute: 3,
67 | speed: .normal,
68 | location: CLLocationCoordinate2D(latitude: 40.747839, longitude: -73.996646)
69 | ),
70 | Scooter(
71 | id: 7,
72 | name: "S007",
73 | batteryCharge: 57,
74 | chargeConsumptionPerMinute: 2,
75 | reservationFee: 10,
76 | costPerMinute: 2.5,
77 | speed: .fast,
78 | location: CLLocationCoordinate2D(latitude: 40.744264, longitude: -73.996031)
79 | ),
80 | Scooter(
81 | id: 8,
82 | name: "S008",
83 | batteryCharge: 2,
84 | chargeConsumptionPerMinute: 1,
85 | reservationFee: 10,
86 | costPerMinute: 1.5,
87 | speed: .normal,
88 | location: CLLocationCoordinate2D(latitude: 40.748400, longitude: -73.986518)
89 | )
90 | ]
91 |
92 | func findScooterById(_ id: Int) -> Scooter? {
93 | return scooters.first(where: { $0.id == id })
94 | }
95 |
96 | private static let appGroup = "group.company.ScooterRentalApp"
97 |
98 | func setRideStartDate(_ date: Date) {
99 | UserDefaults(suiteName: AppModel.appGroup)?.setValue(date, forKey: "startDate")
100 | }
101 |
102 | func getRideStartDate() -> Date? {
103 | UserDefaults(suiteName: AppModel.appGroup)?.value(forKey: "startDate") as? Date
104 | }
105 |
106 | func resetRideStartDate() {
107 | UserDefaults(suiteName: AppModel.appGroup)?.setValue(nil, forKey: "startDate")
108 | }
109 |
110 | func setSelectedScooter(_ id: Int) {
111 | UserDefaults(suiteName: AppModel.appGroup)?.setValue(id, forKey: "selectedScooter")
112 | }
113 |
114 | func getSelectedScooter() -> Scooter? {
115 | guard let id = UserDefaults(suiteName: AppModel.appGroup)?.value(forKey: "selectedScooter") as? Int else {
116 | return nil
117 | }
118 | return findScooterById(id)
119 | }
120 |
121 | func resetSelectedScooter() {
122 | UserDefaults(suiteName: AppModel.appGroup)?.setValue(nil, forKey: "selectedScooter")
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/Shared/Model/Scooter.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MapKit
3 |
4 | struct Scooter: Identifiable, Hashable {
5 |
6 | enum Speed: String {
7 |
8 | case normal = "Normal"
9 | case fast = "Fast"
10 |
11 | }
12 |
13 | var id: Int
14 | let name: String
15 | let batteryCharge: Int
16 | let chargeConsumptionPerMinute: Int
17 | let reservationFee: Double
18 | let costPerMinute: Double
19 | let speed: Speed
20 | let location: CLLocationCoordinate2D
21 |
22 | func fullDischargeDate(_ fromDate: Date) -> Date? {
23 | let minutesUntilDischarge = batteryCharge / chargeConsumptionPerMinute
24 | return Calendar.current.date(byAdding: .minute, value: Int(minutesUntilDischarge), to: fromDate)
25 | }
26 |
27 | func calculateRemainingCharge(_ fromDate: Date, toDate: Date) -> Int {
28 | return batteryCharge - (Calendar.current.dateComponents([.minute], from: fromDate, to: toDate).minute ?? 0) * chargeConsumptionPerMinute
29 | }
30 |
31 | func calculateCost(_ fromDate: Date, toDate: Date) -> Double {
32 | return Double(Calendar.current.dateComponents([.minute], from: fromDate, to: toDate).minute ?? 0) * costPerMinute + reservationFee
33 | }
34 |
35 | func hash(into hasher: inout Hasher) {
36 | hasher.combine(id)
37 | }
38 |
39 | static func == (lhs: Scooter, rhs: Scooter) -> Bool {
40 | return lhs.id == rhs.id
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/Shared/Model/ScooterAnnotation.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import MapKit
3 |
4 | class ScooterAnnotation: MKPointAnnotation {
5 |
6 | var scooter: Scooter?
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/Widget Extension/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Widget Extension/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 |
--------------------------------------------------------------------------------
/Widget Extension/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Widget Extension/Assets.xcassets/WidgetBackground.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Widget Extension/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Scooter Rental App Widget Extension
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSExtension
24 |
25 | NSExtensionPointIdentifier
26 | com.apple.widgetkit-extension
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Widget Extension/ScooterRentalAppWidgetExtension.intentdefinition:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | INEnums
6 |
7 | INIntentDefinitionModelVersion
8 | 1.2
9 | INIntentDefinitionNamespace
10 | 88xZPY
11 | INIntentDefinitionSystemVersion
12 | 20C69
13 | INIntentDefinitionToolsBuildVersion
14 | 12B45b
15 | INIntentDefinitionToolsVersion
16 | 12.2
17 | INIntents
18 |
19 |
20 | INIntentCategory
21 | information
22 | INIntentDescriptionID
23 | tVvJ9c
24 | INIntentEligibleForWidgets
25 |
26 | INIntentIneligibleForSuggestions
27 |
28 | INIntentLastParameterTag
29 | 2
30 | INIntentName
31 | Configuration
32 | INIntentParameters
33 |
34 |
35 | INIntentParameterConfigurable
36 |
37 | INIntentParameterDisplayName
38 | Show Timer
39 | INIntentParameterDisplayNameID
40 | IYcyxA
41 | INIntentParameterDisplayPriority
42 | 1
43 | INIntentParameterMetadata
44 |
45 | INIntentParameterMetadataDefaultValue
46 |
47 | INIntentParameterMetadataFalseDisplayName
48 | false
49 | INIntentParameterMetadataFalseDisplayNameID
50 | 3DAboT
51 | INIntentParameterMetadataTrueDisplayName
52 | true
53 | INIntentParameterMetadataTrueDisplayNameID
54 | iauoJx
55 |
56 | INIntentParameterName
57 | showTimer
58 | INIntentParameterPromptDialogs
59 |
60 |
61 | INIntentParameterPromptDialogCustom
62 |
63 | INIntentParameterPromptDialogType
64 | Configuration
65 |
66 |
67 | INIntentParameterPromptDialogCustom
68 |
69 | INIntentParameterPromptDialogFormatString
70 | Ride Started
71 | INIntentParameterPromptDialogFormatStringID
72 | 0adYP7
73 | INIntentParameterPromptDialogType
74 | Primary
75 |
76 |
77 | INIntentParameterSupportsResolution
78 |
79 | INIntentParameterTag
80 | 2
81 | INIntentParameterType
82 | Boolean
83 |
84 |
85 | INIntentResponse
86 |
87 | INIntentResponseCodes
88 |
89 |
90 | INIntentResponseCodeName
91 | success
92 | INIntentResponseCodeSuccess
93 |
94 |
95 |
96 | INIntentResponseCodeName
97 | failure
98 |
99 |
100 |
101 | INIntentTitle
102 | Configuration
103 | INIntentTitleID
104 | gpCwrM
105 | INIntentType
106 | Custom
107 | INIntentVerb
108 | View
109 |
110 |
111 | INTypes
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/Widget Extension/ScooterRentalAppWidgetExtensionBundle.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import WidgetKit
3 |
4 | @main
5 | struct ScooterRentalAppWidgetExtensionBundle: WidgetBundle {
6 |
7 | @WidgetBundleBuilder
8 | var body: some Widget {
9 | RideWidget()
10 | ListWidget()
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/Widget Extension/Timeline Providers/Entries/ScooterEntry.swift:
--------------------------------------------------------------------------------
1 | import WidgetKit
2 |
3 | struct ScooterEntry: TimelineEntry {
4 |
5 | let configuration: ConfigurationIntent
6 | let date: Date
7 |
8 | let startDate: Date?
9 | let scooter: Scooter?
10 | let remainingCharge: Int
11 | let cost: Double
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/Widget Extension/Timeline Providers/Entries/ScooterListEntry.swift:
--------------------------------------------------------------------------------
1 | import WidgetKit
2 |
3 | struct ScooterListEntry: TimelineEntry {
4 |
5 | let date: Date
6 |
7 | let scooters: [Scooter]
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/Widget Extension/Timeline Providers/ListTimelineProvider.swift:
--------------------------------------------------------------------------------
1 | import WidgetKit
2 |
3 | struct ListTimelineProvider: TimelineProvider {
4 |
5 | typealias Entry = ScooterListEntry
6 |
7 | func placeholder(in context: Context) -> Entry {
8 | let scooters = Array(AppModel().scooters[0...1])
9 | return Entry(date: Date(), scooters: scooters)
10 | }
11 |
12 | func getSnapshot(in context: Context, completion: @escaping (Entry) -> ()) {
13 | let scooters = Array(AppModel().scooters[0...1])
14 | completion(Entry(date: Date(), scooters: scooters))
15 | }
16 |
17 | func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) {
18 | let scooters = Array(AppModel().scooters.shuffled()[0...1])
19 | completion(Timeline(entries: [Entry(date: Date(), scooters: scooters)], policy: .never))
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/Widget Extension/Timeline Providers/RideTimelineProvider.swift:
--------------------------------------------------------------------------------
1 | import WidgetKit
2 |
3 | struct RideTimelineProvider: IntentTimelineProvider {
4 |
5 | typealias Entry = ScooterEntry
6 |
7 | func placeholder(in context: Context) -> Entry {
8 | Entry(configuration: ConfigurationIntent(), date: Date(), startDate: nil, scooter: nil, remainingCharge: 0, cost: 0)
9 | }
10 |
11 | func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Entry) -> ()) {
12 | let entry = Entry(configuration: context.isPreview ? ConfigurationIntent() : configuration, date: Date(), startDate: nil, scooter: nil, remainingCharge: 0, cost: 0)
13 | completion(entry)
14 | }
15 |
16 | func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> ()) {
17 | let model = AppModel()
18 | guard let selectedScooter = model.getSelectedScooter(),
19 | let startDate = model.getRideStartDate(),
20 | let endDate = selectedScooter.fullDischargeDate(startDate) else {
21 | completion(Timeline(entries: [
22 | Entry(configuration: configuration, date: Date(), startDate: nil, scooter: nil, remainingCharge: 0, cost: 0)
23 | ], policy: .never))
24 | return
25 | }
26 |
27 | let oneMinute: TimeInterval = 60
28 | var currentDate = Date()
29 | var entries: [Entry] = []
30 |
31 | while currentDate <= endDate {
32 | let remainingCharge = selectedScooter.calculateRemainingCharge(startDate, toDate: currentDate)
33 | let cost = selectedScooter.calculateCost(startDate, toDate: currentDate)
34 | let entry = Entry(configuration: configuration, date: currentDate, startDate: startDate, scooter: selectedScooter, remainingCharge: remainingCharge, cost: cost)
35 |
36 | currentDate += oneMinute
37 | entries.append(entry)
38 | }
39 |
40 | completion(Timeline(entries: entries, policy: .atEnd))
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/Widget Extension/Views/ListWidgetEntryView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ListWidgetEntryView: View {
4 |
5 | var entry: ListTimelineProvider.Entry
6 |
7 | var body: some View {
8 | ZStack {
9 | Color(red: 251 / 255, green: 236 / 255, blue: 197 / 255)
10 | VStack(alignment: .leading, spacing: 15) {
11 | Image("scooter-yellow")
12 | .resizable()
13 | .aspectRatio(contentMode: .fit)
14 | .frame(width: 40, height: 40)
15 | Spacer()
16 | ForEach(entry.scooters, id: \.self) { scooter in
17 | Link(destination: URL(string: "scooter://scooters?id=\(scooter.id)")!) {
18 | HStack {
19 | VStack(alignment: .leading, spacing: 10) {
20 | Text(scooter.name)
21 | .foregroundColor(Color(red: 58 / 255, green: 42 / 255, blue: 3 / 255))
22 | .font(.system(size: 20, weight: .bold, design: .rounded))
23 | HStack(alignment: .center) {
24 | HStack(alignment: .top) {
25 | VStack(alignment: .leading, spacing: 5) {
26 | Text("Starts from ")
27 | .font(Font.system(size: 18, weight: .medium, design: .rounded))
28 | .foregroundColor(Color(red: 115 / 255, green: 85 / 255, blue: 7 / 255))
29 | + Text(String(format: "$%.0f", scooter.reservationFee))
30 | .foregroundColor(Color(red: 78 / 255, green: 57 / 255, blue: 4 / 255))
31 | .font(Font.system(size: 18, weight: .medium, design: .rounded))
32 | Text(String(format: "$%.0f ", scooter.costPerMinute))
33 | .foregroundColor(Color(red: 78 / 255, green: 57 / 255, blue: 4 / 255))
34 | .font(Font.system(size: 18, weight: .medium, design: .rounded))
35 | + Text("each minute")
36 | .font(Font.system(size: 18, weight: .regular, design: .rounded))
37 | .foregroundColor(Color(red: 115 / 255, green: 85 / 255, blue: 7 / 255))
38 | }
39 | }
40 | Spacer()
41 | }
42 | }
43 | Image(systemName: "battery.100.bolt")
44 | .resizable()
45 | .aspectRatio(contentMode: .fit)
46 | .padding(EdgeInsets(top: 8, leading: 9, bottom: 8, trailing: 7))
47 | .frame(width: 40, height: 40)
48 | .foregroundColor(.white)
49 | .background(getBatteryStatusColor(scooter.batteryCharge))
50 | .clipShape(Circle())
51 | }
52 | .padding(15)
53 | .background(Color(red: 249 / 255, green: 223 / 255, blue: 159 / 255))
54 | .cornerRadius(16)
55 | }
56 | }
57 | }
58 | .frame(maxWidth: .infinity, maxHeight: .infinity)
59 | .padding(15)
60 | }
61 |
62 | }
63 |
64 | func getBatteryStatusColor(_ charge: Int) -> Color {
65 | if charge >= 90 {
66 | return Color.green.opacity(0.7)
67 | }
68 | if charge < 90, charge >= 30 {
69 | return Color.yellow
70 | }
71 | return Color.red.opacity(0.7)
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/Widget Extension/Views/RideWidgetEntryView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct RideWidgetEntryView: View {
4 |
5 | @Environment(\.widgetFamily) var family
6 |
7 | var entry: RideTimelineProvider.Entry
8 |
9 | var body: some View {
10 | switch entry.scooter != nil {
11 | case true:
12 | rideStatusView
13 | case false:
14 | emptyRideView
15 | }
16 | }
17 |
18 | var emptyRideView: some View {
19 | HStack {
20 | VStack(alignment: .leading, spacing: 15) {
21 | Image("scooter-yellow")
22 | .resizable()
23 | .aspectRatio(contentMode: .fit)
24 | .frame(width: 40, height: 40)
25 | Spacer()
26 | Text("RIDE")
27 | .foregroundColor(Color(red: 115 / 255, green: 85 / 255, blue: 7 / 255))
28 | .font(Font.system(size: 16, weight: .bold, design: .rounded))
29 | .padding([.top, .bottom], 15)
30 | .frame(maxWidth: .infinity)
31 | .background(Color(red: 248 / 255, green: 220 / 255, blue: 150 / 255))
32 | .cornerRadius(16)
33 | }
34 | }
35 | .frame(maxWidth: .infinity, maxHeight: .infinity)
36 | .padding(15)
37 | .background(Color(red: 251 / 255, green: 236 / 255, blue: 197 / 255))
38 | }
39 |
40 | var rideStatusView: some View {
41 | VStack(alignment: .leading, spacing: 15) {
42 | HStack(alignment: .center, spacing: 10) {
43 | HStack {
44 | Image("scooter-yellow")
45 | .resizable()
46 | .aspectRatio(contentMode: .fit)
47 | .frame(width: 40, height: 40)
48 | }
49 | Text("\(entry.scooter?.name ?? "")")
50 | .foregroundColor(Color(red: 58 / 255, green: 42 / 255, blue: 3 / 255))
51 | .font(.system(size: 20, weight: .bold, design: .rounded))
52 | }
53 | HStack(alignment: .bottom) {
54 | VStack(alignment: .leading, spacing: 5) {
55 | HStack(spacing: 5) {
56 | Image(systemName: "battery.100.bolt")
57 | .resizable()
58 | .aspectRatio(contentMode: .fit)
59 | .padding(EdgeInsets(top: 5, leading: 6, bottom: 5, trailing: 4))
60 | .frame(width: 30, height: 30)
61 | .foregroundColor(.white)
62 | .background(getBatteryStatusColor(entry.remainingCharge))
63 | .clipShape(Circle())
64 | (Text("\(entry.remainingCharge)") + Text("%"))
65 | .foregroundColor(Color(red: 78 / 255, green: 57 / 255, blue: 4 / 255))
66 | .font(Font.system(size: 16, weight: .semibold, design: .rounded))
67 | }
68 | HStack(spacing: 5) {
69 | Image(systemName: "dollarsign.circle.fill")
70 | .resizable()
71 | .aspectRatio(contentMode: .fit)
72 | .frame(width: 30, height: 30)
73 | .foregroundColor(.green)
74 | Text(String(format: "%.0f", entry.cost))
75 | .foregroundColor(Color(red: 78 / 255, green: 57 / 255, blue: 4 / 255))
76 | .font(Font.system(size: 16, weight: .semibold, design: .rounded))
77 | }
78 | }
79 | Spacer()
80 | if family == .systemMedium,
81 | entry.configuration.showTimer?.boolValue ?? true {
82 | Text(entry.startDate ?? Date(), style: .timer)
83 | .font(Font.system(size: 24, weight: .semibold, design: .rounded))
84 | .foregroundColor(Color(red: 115 / 255, green: 85 / 255, blue: 7 / 255))
85 | .multilineTextAlignment(.trailing)
86 | }
87 | }
88 | }
89 | .frame(maxWidth: .infinity, maxHeight: .infinity)
90 | .padding(15)
91 | .background(Color(red: 251 / 255, green: 236 / 255, blue: 197 / 255))
92 | }
93 |
94 | func getBatteryStatusColor(_ charge: Int) -> Color {
95 | if charge >= 90 {
96 | return Color.green.opacity(0.7)
97 | }
98 | if charge < 90, charge >= 30 {
99 | return Color.yellow
100 | }
101 | return Color.red.opacity(0.7)
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/Widget Extension/Widgets/ListWidget.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import WidgetKit
3 |
4 | struct ListWidget: Widget {
5 |
6 | let kind: String = "ScooterListWidget"
7 |
8 | var body: some WidgetConfiguration {
9 | StaticConfiguration(kind: kind, provider: ListTimelineProvider()) { entry in
10 | ListWidgetEntryView(entry: entry)
11 | }
12 | .configurationDisplayName("List Widget")
13 | .description("This is an example widget.")
14 | .supportedFamilies([.systemLarge])
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/Widget Extension/Widgets/RideWidget.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import WidgetKit
3 |
4 | struct RideWidget: Widget {
5 |
6 | let kind: String = "ScooterRideWidget"
7 |
8 | var body: some WidgetConfiguration {
9 | IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: RideTimelineProvider()) { entry in
10 | RideWidgetEntryView(entry: entry)
11 | }
12 | .configurationDisplayName("Ride Widget")
13 | .description("This is an example widget.")
14 | .supportedFamilies([.systemSmall, .systemMedium])
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------