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