├── SwiftfulMapApp.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── nicksarno.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist └── SwiftfulMapApp ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ ├── Contents.json │ ├── Icon-20.png │ ├── Icon-20@2x-1.png │ ├── Icon-20@2x.png │ ├── Icon-20@3x.png │ ├── Icon-29.png │ ├── Icon-29@2x-1.png │ ├── Icon-29@2x.png │ ├── Icon-29@3x.png │ ├── Icon-40.png │ ├── Icon-40@2x-1.png │ ├── Icon-40@2x.png │ ├── Icon-40@3x.png │ ├── Icon-60@2x.png │ ├── Icon-60@3x.png │ ├── Icon-76.png │ ├── Icon-76@2x.png │ ├── Icon-83_5@2x.png │ └── ios-marketing.png ├── Contents.json ├── Locations │ ├── Contents.json │ ├── Paris │ │ ├── Contents.json │ │ ├── paris-eiffeltower-1.imageset │ │ │ ├── Contents.json │ │ │ └── paris-eiffeltower-1.jpg │ │ ├── paris-eiffeltower-2.imageset │ │ │ ├── Contents.json │ │ │ └── paris-eiffeltower-2.jpg │ │ ├── paris-louvre-1.imageset │ │ │ ├── Contents.json │ │ │ └── paris-louvre-1.jpg │ │ ├── paris-louvre-2.imageset │ │ │ ├── Contents.json │ │ │ └── paris-louvre-2.jpg │ │ └── paris-louvre-3.imageset │ │ │ ├── Contents.json │ │ │ └── paris-louvre-3.jpg │ └── Rome │ │ ├── Contents.json │ │ ├── rome-colosseum-1.imageset │ │ ├── Contents.json │ │ └── rome-colosseum-2.jpg │ │ ├── rome-colosseum-2.imageset │ │ ├── Contents.json │ │ └── rome-colosseum-3.jpg │ │ ├── rome-colosseum-3.imageset │ │ ├── Contents.json │ │ └── rome-colosseum-1.jpg │ │ ├── rome-pantheon-1.imageset │ │ ├── Contents.json │ │ └── rome-pantheon-1.jpg │ │ ├── rome-pantheon-2.imageset │ │ ├── Contents.json │ │ └── rome-pantheon-2.jpg │ │ ├── rome-pantheon-3.imageset │ │ ├── Contents.json │ │ └── rome-pantheon-3.jpg │ │ ├── rome-trevifountain-1.imageset │ │ ├── Contents.json │ │ └── rome-trevifountain-1.jpg │ │ ├── rome-trevifountain-2.imageset │ │ ├── Contents.json │ │ └── rome-trevifountain-2.jpg │ │ ├── rome-trevifountain-3.imageset │ │ ├── Contents.json │ │ └── rome-trevifountain-3.jpg │ │ └── rome-trevifountain-4.imageset │ │ ├── Contents.json │ │ └── rome-trevifountain-4.jpg └── Logos │ ├── Contents.json │ ├── logo-launch.imageset │ ├── Contents.json │ └── logo-launch.png │ └── logo.imageset │ ├── Contents.json │ └── logo.png ├── DataServices └── LocationsDataService.swift ├── Info.plist ├── Models └── Location.swift ├── Preview Content └── Preview Assets.xcassets │ └── Contents.json ├── SwiftfulMapAppApp.swift ├── ViewModels └── LocationsViewModel.swift └── Views ├── LocationDetailView.swift ├── LocationMapAnnotationView.swift ├── LocationPreviewView.swift ├── LocationsListView.swift └── LocationsView.swift /SwiftfulMapApp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 023DC9FB2752B0A9009AC7ED /* SwiftfulMapAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023DC9FA2752B0A9009AC7ED /* SwiftfulMapAppApp.swift */; }; 11 | 023DCA022752B0AC009AC7ED /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 023DCA012752B0AC009AC7ED /* Preview Assets.xcassets */; }; 12 | 023DCA092752B163009AC7ED /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 023DCA082752B163009AC7ED /* Assets.xcassets */; }; 13 | 023DCA122752B396009AC7ED /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023DCA112752B396009AC7ED /* Location.swift */; }; 14 | 023DCA152752B4BD009AC7ED /* LocationsDataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023DCA142752B4BD009AC7ED /* LocationsDataService.swift */; }; 15 | 023DCA172752B6DF009AC7ED /* LocationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023DCA162752B6DF009AC7ED /* LocationsView.swift */; }; 16 | 023DCA192752BAD8009AC7ED /* LocationsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023DCA182752BAD8009AC7ED /* LocationsViewModel.swift */; }; 17 | 023DCA1B2752C3DB009AC7ED /* LocationsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023DCA1A2752C3DB009AC7ED /* LocationsListView.swift */; }; 18 | 023DCA1D2754307E009AC7ED /* LocationPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023DCA1C2754307E009AC7ED /* LocationPreviewView.swift */; }; 19 | 023DCA1F27543D78009AC7ED /* LocationMapAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023DCA1E27543D78009AC7ED /* LocationMapAnnotationView.swift */; }; 20 | 023DCA2127544166009AC7ED /* LocationDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023DCA2027544166009AC7ED /* LocationDetailView.swift */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 023DC9F72752B0A9009AC7ED /* SwiftfulMapApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftfulMapApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 023DC9FA2752B0A9009AC7ED /* SwiftfulMapAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftfulMapAppApp.swift; sourceTree = ""; }; 26 | 023DCA012752B0AC009AC7ED /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 27 | 023DCA082752B163009AC7ED /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | 023DCA112752B396009AC7ED /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = ""; }; 29 | 023DCA142752B4BD009AC7ED /* LocationsDataService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationsDataService.swift; sourceTree = ""; }; 30 | 023DCA162752B6DF009AC7ED /* LocationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsView.swift; sourceTree = ""; }; 31 | 023DCA182752BAD8009AC7ED /* LocationsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsViewModel.swift; sourceTree = ""; }; 32 | 023DCA1A2752C3DB009AC7ED /* LocationsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListView.swift; sourceTree = ""; }; 33 | 023DCA1C2754307E009AC7ED /* LocationPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPreviewView.swift; sourceTree = ""; }; 34 | 023DCA1E27543D78009AC7ED /* LocationMapAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationMapAnnotationView.swift; sourceTree = ""; }; 35 | 023DCA2027544166009AC7ED /* LocationDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationDetailView.swift; sourceTree = ""; }; 36 | 023DCA2227545711009AC7ED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 023DC9F42752B0A9009AC7ED /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | 023DC9EE2752B0A9009AC7ED = { 51 | isa = PBXGroup; 52 | children = ( 53 | 023DC9F92752B0A9009AC7ED /* SwiftfulMapApp */, 54 | 023DC9F82752B0A9009AC7ED /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | 023DC9F82752B0A9009AC7ED /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 023DC9F72752B0A9009AC7ED /* SwiftfulMapApp.app */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | 023DC9F92752B0A9009AC7ED /* SwiftfulMapApp */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 023DCA2227545711009AC7ED /* Info.plist */, 70 | 023DC9FA2752B0A9009AC7ED /* SwiftfulMapAppApp.swift */, 71 | 023DCA132752B49E009AC7ED /* DataServices */, 72 | 023DCA0D2752B2ED009AC7ED /* Views */, 73 | 023DCA0E2752B2F4009AC7ED /* ViewModels */, 74 | 023DCA0C2752B2E8009AC7ED /* Models */, 75 | 023DCA082752B163009AC7ED /* Assets.xcassets */, 76 | 023DCA002752B0AC009AC7ED /* Preview Content */, 77 | ); 78 | path = SwiftfulMapApp; 79 | sourceTree = ""; 80 | }; 81 | 023DCA002752B0AC009AC7ED /* Preview Content */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 023DCA012752B0AC009AC7ED /* Preview Assets.xcassets */, 85 | ); 86 | path = "Preview Content"; 87 | sourceTree = ""; 88 | }; 89 | 023DCA0C2752B2E8009AC7ED /* Models */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 023DCA112752B396009AC7ED /* Location.swift */, 93 | ); 94 | path = Models; 95 | sourceTree = ""; 96 | }; 97 | 023DCA0D2752B2ED009AC7ED /* Views */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 023DCA162752B6DF009AC7ED /* LocationsView.swift */, 101 | 023DCA1A2752C3DB009AC7ED /* LocationsListView.swift */, 102 | 023DCA1C2754307E009AC7ED /* LocationPreviewView.swift */, 103 | 023DCA1E27543D78009AC7ED /* LocationMapAnnotationView.swift */, 104 | 023DCA2027544166009AC7ED /* LocationDetailView.swift */, 105 | ); 106 | path = Views; 107 | sourceTree = ""; 108 | }; 109 | 023DCA0E2752B2F4009AC7ED /* ViewModels */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 023DCA182752BAD8009AC7ED /* LocationsViewModel.swift */, 113 | ); 114 | path = ViewModels; 115 | sourceTree = ""; 116 | }; 117 | 023DCA132752B49E009AC7ED /* DataServices */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 023DCA142752B4BD009AC7ED /* LocationsDataService.swift */, 121 | ); 122 | path = DataServices; 123 | sourceTree = ""; 124 | }; 125 | /* End PBXGroup section */ 126 | 127 | /* Begin PBXNativeTarget section */ 128 | 023DC9F62752B0A9009AC7ED /* SwiftfulMapApp */ = { 129 | isa = PBXNativeTarget; 130 | buildConfigurationList = 023DCA052752B0AC009AC7ED /* Build configuration list for PBXNativeTarget "SwiftfulMapApp" */; 131 | buildPhases = ( 132 | 023DC9F32752B0A9009AC7ED /* Sources */, 133 | 023DC9F42752B0A9009AC7ED /* Frameworks */, 134 | 023DC9F52752B0A9009AC7ED /* Resources */, 135 | ); 136 | buildRules = ( 137 | ); 138 | dependencies = ( 139 | ); 140 | name = SwiftfulMapApp; 141 | productName = SwiftfulMapApp; 142 | productReference = 023DC9F72752B0A9009AC7ED /* SwiftfulMapApp.app */; 143 | productType = "com.apple.product-type.application"; 144 | }; 145 | /* End PBXNativeTarget section */ 146 | 147 | /* Begin PBXProject section */ 148 | 023DC9EF2752B0A9009AC7ED /* Project object */ = { 149 | isa = PBXProject; 150 | attributes = { 151 | BuildIndependentTargetsInParallel = 1; 152 | LastSwiftUpdateCheck = 1310; 153 | LastUpgradeCheck = 1310; 154 | TargetAttributes = { 155 | 023DC9F62752B0A9009AC7ED = { 156 | CreatedOnToolsVersion = 13.1; 157 | }; 158 | }; 159 | }; 160 | buildConfigurationList = 023DC9F22752B0A9009AC7ED /* Build configuration list for PBXProject "SwiftfulMapApp" */; 161 | compatibilityVersion = "Xcode 13.0"; 162 | developmentRegion = en; 163 | hasScannedForEncodings = 0; 164 | knownRegions = ( 165 | en, 166 | Base, 167 | ); 168 | mainGroup = 023DC9EE2752B0A9009AC7ED; 169 | productRefGroup = 023DC9F82752B0A9009AC7ED /* Products */; 170 | projectDirPath = ""; 171 | projectRoot = ""; 172 | targets = ( 173 | 023DC9F62752B0A9009AC7ED /* SwiftfulMapApp */, 174 | ); 175 | }; 176 | /* End PBXProject section */ 177 | 178 | /* Begin PBXResourcesBuildPhase section */ 179 | 023DC9F52752B0A9009AC7ED /* Resources */ = { 180 | isa = PBXResourcesBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | 023DCA092752B163009AC7ED /* Assets.xcassets in Resources */, 184 | 023DCA022752B0AC009AC7ED /* Preview Assets.xcassets in Resources */, 185 | ); 186 | runOnlyForDeploymentPostprocessing = 0; 187 | }; 188 | /* End PBXResourcesBuildPhase section */ 189 | 190 | /* Begin PBXSourcesBuildPhase section */ 191 | 023DC9F32752B0A9009AC7ED /* Sources */ = { 192 | isa = PBXSourcesBuildPhase; 193 | buildActionMask = 2147483647; 194 | files = ( 195 | 023DCA152752B4BD009AC7ED /* LocationsDataService.swift in Sources */, 196 | 023DCA1F27543D78009AC7ED /* LocationMapAnnotationView.swift in Sources */, 197 | 023DCA1B2752C3DB009AC7ED /* LocationsListView.swift in Sources */, 198 | 023DCA1D2754307E009AC7ED /* LocationPreviewView.swift in Sources */, 199 | 023DCA192752BAD8009AC7ED /* LocationsViewModel.swift in Sources */, 200 | 023DCA172752B6DF009AC7ED /* LocationsView.swift in Sources */, 201 | 023DCA122752B396009AC7ED /* Location.swift in Sources */, 202 | 023DCA2127544166009AC7ED /* LocationDetailView.swift in Sources */, 203 | 023DC9FB2752B0A9009AC7ED /* SwiftfulMapAppApp.swift in Sources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXSourcesBuildPhase section */ 208 | 209 | /* Begin XCBuildConfiguration section */ 210 | 023DCA032752B0AC009AC7ED /* Debug */ = { 211 | isa = XCBuildConfiguration; 212 | buildSettings = { 213 | ALWAYS_SEARCH_USER_PATHS = NO; 214 | CLANG_ANALYZER_NONNULL = YES; 215 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 216 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 217 | CLANG_CXX_LIBRARY = "libc++"; 218 | CLANG_ENABLE_MODULES = YES; 219 | CLANG_ENABLE_OBJC_ARC = YES; 220 | CLANG_ENABLE_OBJC_WEAK = YES; 221 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 222 | CLANG_WARN_BOOL_CONVERSION = YES; 223 | CLANG_WARN_COMMA = YES; 224 | CLANG_WARN_CONSTANT_CONVERSION = YES; 225 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 226 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 227 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 228 | CLANG_WARN_EMPTY_BODY = YES; 229 | CLANG_WARN_ENUM_CONVERSION = YES; 230 | CLANG_WARN_INFINITE_RECURSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 233 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 234 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 236 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 237 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 238 | CLANG_WARN_STRICT_PROTOTYPES = YES; 239 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 240 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 241 | CLANG_WARN_UNREACHABLE_CODE = YES; 242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 243 | COPY_PHASE_STRIP = NO; 244 | DEBUG_INFORMATION_FORMAT = dwarf; 245 | ENABLE_STRICT_OBJC_MSGSEND = YES; 246 | ENABLE_TESTABILITY = YES; 247 | GCC_C_LANGUAGE_STANDARD = gnu11; 248 | GCC_DYNAMIC_NO_PIC = NO; 249 | GCC_NO_COMMON_BLOCKS = YES; 250 | GCC_OPTIMIZATION_LEVEL = 0; 251 | GCC_PREPROCESSOR_DEFINITIONS = ( 252 | "DEBUG=1", 253 | "$(inherited)", 254 | ); 255 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 256 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 257 | GCC_WARN_UNDECLARED_SELECTOR = YES; 258 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 259 | GCC_WARN_UNUSED_FUNCTION = YES; 260 | GCC_WARN_UNUSED_VARIABLE = YES; 261 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 262 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 263 | MTL_FAST_MATH = YES; 264 | ONLY_ACTIVE_ARCH = YES; 265 | SDKROOT = iphoneos; 266 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 267 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 268 | }; 269 | name = Debug; 270 | }; 271 | 023DCA042752B0AC009AC7ED /* Release */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | ALWAYS_SEARCH_USER_PATHS = NO; 275 | CLANG_ANALYZER_NONNULL = YES; 276 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 277 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 278 | CLANG_CXX_LIBRARY = "libc++"; 279 | CLANG_ENABLE_MODULES = YES; 280 | CLANG_ENABLE_OBJC_ARC = YES; 281 | CLANG_ENABLE_OBJC_WEAK = YES; 282 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 283 | CLANG_WARN_BOOL_CONVERSION = YES; 284 | CLANG_WARN_COMMA = YES; 285 | CLANG_WARN_CONSTANT_CONVERSION = YES; 286 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 287 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 288 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 289 | CLANG_WARN_EMPTY_BODY = YES; 290 | CLANG_WARN_ENUM_CONVERSION = YES; 291 | CLANG_WARN_INFINITE_RECURSION = YES; 292 | CLANG_WARN_INT_CONVERSION = YES; 293 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 294 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 295 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 296 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 297 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 298 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 299 | CLANG_WARN_STRICT_PROTOTYPES = YES; 300 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 301 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 302 | CLANG_WARN_UNREACHABLE_CODE = YES; 303 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 304 | COPY_PHASE_STRIP = NO; 305 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 306 | ENABLE_NS_ASSERTIONS = NO; 307 | ENABLE_STRICT_OBJC_MSGSEND = YES; 308 | GCC_C_LANGUAGE_STANDARD = gnu11; 309 | GCC_NO_COMMON_BLOCKS = YES; 310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 312 | GCC_WARN_UNDECLARED_SELECTOR = YES; 313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 314 | GCC_WARN_UNUSED_FUNCTION = YES; 315 | GCC_WARN_UNUSED_VARIABLE = YES; 316 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 317 | MTL_ENABLE_DEBUG_INFO = NO; 318 | MTL_FAST_MATH = YES; 319 | SDKROOT = iphoneos; 320 | SWIFT_COMPILATION_MODE = wholemodule; 321 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 322 | VALIDATE_PRODUCT = YES; 323 | }; 324 | name = Release; 325 | }; 326 | 023DCA062752B0AC009AC7ED /* Debug */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 330 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 331 | CODE_SIGN_STYLE = Automatic; 332 | CURRENT_PROJECT_VERSION = 1; 333 | DEVELOPMENT_ASSET_PATHS = "\"SwiftfulMapApp/Preview Content\""; 334 | DEVELOPMENT_TEAM = Z735PM5XVD; 335 | ENABLE_PREVIEWS = YES; 336 | GENERATE_INFOPLIST_FILE = YES; 337 | INFOPLIST_FILE = SwiftfulMapApp/Info.plist; 338 | INFOPLIST_KEY_CFBundleDisplayName = "My Locations"; 339 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 340 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 341 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 342 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 343 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; 344 | LD_RUNPATH_SEARCH_PATHS = ( 345 | "$(inherited)", 346 | "@executable_path/Frameworks", 347 | ); 348 | MARKETING_VERSION = 1.0; 349 | PRODUCT_BUNDLE_IDENTIFIER = com.nicksarno.SwiftfulMapApp; 350 | PRODUCT_NAME = "$(TARGET_NAME)"; 351 | SWIFT_EMIT_LOC_STRINGS = YES; 352 | SWIFT_VERSION = 5.0; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | }; 355 | name = Debug; 356 | }; 357 | 023DCA072752B0AC009AC7ED /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 361 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 362 | CODE_SIGN_STYLE = Automatic; 363 | CURRENT_PROJECT_VERSION = 1; 364 | DEVELOPMENT_ASSET_PATHS = "\"SwiftfulMapApp/Preview Content\""; 365 | DEVELOPMENT_TEAM = Z735PM5XVD; 366 | ENABLE_PREVIEWS = YES; 367 | GENERATE_INFOPLIST_FILE = YES; 368 | INFOPLIST_FILE = SwiftfulMapApp/Info.plist; 369 | INFOPLIST_KEY_CFBundleDisplayName = "My Locations"; 370 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 371 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 372 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 373 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 374 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; 375 | LD_RUNPATH_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "@executable_path/Frameworks", 378 | ); 379 | MARKETING_VERSION = 1.0; 380 | PRODUCT_BUNDLE_IDENTIFIER = com.nicksarno.SwiftfulMapApp; 381 | PRODUCT_NAME = "$(TARGET_NAME)"; 382 | SWIFT_EMIT_LOC_STRINGS = YES; 383 | SWIFT_VERSION = 5.0; 384 | TARGETED_DEVICE_FAMILY = "1,2"; 385 | }; 386 | name = Release; 387 | }; 388 | /* End XCBuildConfiguration section */ 389 | 390 | /* Begin XCConfigurationList section */ 391 | 023DC9F22752B0A9009AC7ED /* Build configuration list for PBXProject "SwiftfulMapApp" */ = { 392 | isa = XCConfigurationList; 393 | buildConfigurations = ( 394 | 023DCA032752B0AC009AC7ED /* Debug */, 395 | 023DCA042752B0AC009AC7ED /* Release */, 396 | ); 397 | defaultConfigurationIsVisible = 0; 398 | defaultConfigurationName = Release; 399 | }; 400 | 023DCA052752B0AC009AC7ED /* Build configuration list for PBXNativeTarget "SwiftfulMapApp" */ = { 401 | isa = XCConfigurationList; 402 | buildConfigurations = ( 403 | 023DCA062752B0AC009AC7ED /* Debug */, 404 | 023DCA072752B0AC009AC7ED /* Release */, 405 | ); 406 | defaultConfigurationIsVisible = 0; 407 | defaultConfigurationName = Release; 408 | }; 409 | /* End XCConfigurationList section */ 410 | }; 411 | rootObject = 023DC9EF2752B0A9009AC7ED /* Project object */; 412 | } 413 | -------------------------------------------------------------------------------- /SwiftfulMapApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftfulMapApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftfulMapApp.xcodeproj/xcuserdata/nicksarno.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SwiftfulMapApp.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.000", 9 | "green" : "0.149", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Icon-20@2x.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "Icon-20@3x.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "Icon-29@2x.png", 17 | "idiom" : "iphone", 18 | "scale" : "2x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "Icon-29@3x.png", 23 | "idiom" : "iphone", 24 | "scale" : "3x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "Icon-40@2x.png", 29 | "idiom" : "iphone", 30 | "scale" : "2x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "Icon-40@3x.png", 35 | "idiom" : "iphone", 36 | "scale" : "3x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "Icon-60@2x.png", 41 | "idiom" : "iphone", 42 | "scale" : "2x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "filename" : "Icon-60@3x.png", 47 | "idiom" : "iphone", 48 | "scale" : "3x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "Icon-20.png", 53 | "idiom" : "ipad", 54 | "scale" : "1x", 55 | "size" : "20x20" 56 | }, 57 | { 58 | "filename" : "Icon-20@2x-1.png", 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "Icon-29.png", 65 | "idiom" : "ipad", 66 | "scale" : "1x", 67 | "size" : "29x29" 68 | }, 69 | { 70 | "filename" : "Icon-29@2x-1.png", 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "Icon-40.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "40x40" 80 | }, 81 | { 82 | "filename" : "Icon-40@2x-1.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "Icon-76.png", 89 | "idiom" : "ipad", 90 | "scale" : "1x", 91 | "size" : "76x76" 92 | }, 93 | { 94 | "filename" : "Icon-76@2x.png", 95 | "idiom" : "ipad", 96 | "scale" : "2x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "Icon-83_5@2x.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "83.5x83.5" 104 | }, 105 | { 106 | "filename" : "ios-marketing.png", 107 | "idiom" : "ios-marketing", 108 | "scale" : "1x", 109 | "size" : "1024x1024" 110 | } 111 | ], 112 | "info" : { 113 | "author" : "xcode", 114 | "version" : 1 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-20.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-20@2x-1.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-20@2x.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-20@3x.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-29.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-29@2x-1.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-83_5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/Icon-83_5@2x.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/AppIcon.appiconset/ios-marketing.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Paris/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-eiffeltower-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "paris-eiffeltower-1.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-eiffeltower-1.imageset/paris-eiffeltower-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-eiffeltower-1.imageset/paris-eiffeltower-1.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-eiffeltower-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "paris-eiffeltower-2.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-eiffeltower-2.imageset/paris-eiffeltower-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-eiffeltower-2.imageset/paris-eiffeltower-2.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-louvre-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "paris-louvre-1.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-louvre-1.imageset/paris-louvre-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-louvre-1.imageset/paris-louvre-1.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-louvre-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "paris-louvre-2.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-louvre-2.imageset/paris-louvre-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-louvre-2.imageset/paris-louvre-2.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-louvre-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "paris-louvre-3.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-louvre-3.imageset/paris-louvre-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Paris/paris-louvre-3.imageset/paris-louvre-3.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-colosseum-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rome-colosseum-2.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-colosseum-1.imageset/rome-colosseum-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-colosseum-1.imageset/rome-colosseum-2.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-colosseum-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rome-colosseum-3.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-colosseum-2.imageset/rome-colosseum-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-colosseum-2.imageset/rome-colosseum-3.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-colosseum-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rome-colosseum-1.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-colosseum-3.imageset/rome-colosseum-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-colosseum-3.imageset/rome-colosseum-1.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-pantheon-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rome-pantheon-1.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-pantheon-1.imageset/rome-pantheon-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-pantheon-1.imageset/rome-pantheon-1.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-pantheon-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rome-pantheon-2.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-pantheon-2.imageset/rome-pantheon-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-pantheon-2.imageset/rome-pantheon-2.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-pantheon-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rome-pantheon-3.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-pantheon-3.imageset/rome-pantheon-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-pantheon-3.imageset/rome-pantheon-3.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rome-trevifountain-1.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-1.imageset/rome-trevifountain-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-1.imageset/rome-trevifountain-1.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rome-trevifountain-2.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-2.imageset/rome-trevifountain-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-2.imageset/rome-trevifountain-2.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rome-trevifountain-3.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-3.imageset/rome-trevifountain-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-3.imageset/rome-trevifountain-3.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rome-trevifountain-4.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-4.imageset/rome-trevifountain-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Locations/Rome/rome-trevifountain-4.imageset/rome-trevifountain-4.jpg -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Logos/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Logos/logo-launch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo-launch.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Logos/logo-launch.imageset/logo-launch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Logos/logo-launch.imageset/logo-launch.png -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Logos/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Assets.xcassets/Logos/logo.imageset/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftfulThinking/SwiftfUI-Map-App-MVVM/90e1888cb0ba0c12df31c42ef4eb4bd3b9b69595/SwiftfulMapApp/Assets.xcassets/Logos/logo.imageset/logo.png -------------------------------------------------------------------------------- /SwiftfulMapApp/DataServices/LocationsDataService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationsDataService.swift 3 | // MapTest 4 | // 5 | // Created by Nick Sarno on 11/26/21. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | class LocationsDataService { 12 | 13 | static let locations: [Location] = [ 14 | Location( 15 | name: "Colosseum", 16 | cityName: "Rome", 17 | coordinates: CLLocationCoordinate2D(latitude: 41.8902, longitude: 12.4922), 18 | description: "The Colosseum is an oval amphitheatre in the centre of the city of Rome, Italy, just east of the Roman Forum. It is the largest ancient amphitheatre ever built, and is still the largest standing amphitheatre in the world today, despite its age.", 19 | imageNames: [ 20 | "rome-colosseum-1", 21 | "rome-colosseum-2", 22 | "rome-colosseum-3", 23 | ], 24 | link: "https://en.wikipedia.org/wiki/Colosseum"), 25 | Location( 26 | name: "Pantheon", 27 | cityName: "Rome", 28 | coordinates: CLLocationCoordinate2D(latitude: 41.8986, longitude: 12.4769), 29 | description: "The Pantheon is a former Roman temple and since the year 609 a Catholic church, in Rome, Italy, on the site of an earlier temple commissioned by Marcus Agrippa during the reign of Augustus.", 30 | imageNames: [ 31 | "rome-pantheon-1", 32 | "rome-pantheon-2", 33 | "rome-pantheon-3", 34 | ], 35 | link: "https://en.wikipedia.org/wiki/Pantheon,_Rome"), 36 | Location( 37 | name: "Trevi Fountain", 38 | cityName: "Rome", 39 | coordinates: CLLocationCoordinate2D(latitude: 41.9009, longitude: 12.4833), 40 | description: "The Trevi Fountain is a fountain in the Trevi district in Rome, Italy, designed by Italian architect Nicola Salvi and completed by Giuseppe Pannini and several others. Standing 26.3 metres high and 49.15 metres wide, it is the largest Baroque fountain in the city and one of the most famous fountains in the world.", 41 | imageNames: [ 42 | "rome-trevifountain-1", 43 | "rome-trevifountain-2", 44 | "rome-trevifountain-3", 45 | ], 46 | link: "https://en.wikipedia.org/wiki/Trevi_Fountain"), 47 | Location( 48 | name: "Eiffel Tower", 49 | cityName: "Paris", 50 | coordinates: CLLocationCoordinate2D(latitude: 48.8584, longitude: 2.2945), 51 | description: "The Eiffel Tower is a wrought-iron lattice tower on the Champ de Mars in Paris, France. It is named after the engineer Gustave Eiffel, whose company designed and built the tower. Locally nicknamed 'La dame de fer', it was constructed from 1887 to 1889 as the centerpiece of the 1889 World's Fair and was initially criticized by some of France's leading artists and intellectuals for its design, but it has become a global cultural icon of France and one of the most recognizable structures in the world.", 52 | imageNames: [ 53 | "paris-eiffeltower-1", 54 | "paris-eiffeltower-2", 55 | ], 56 | link: "https://en.wikipedia.org/wiki/Eiffel_Tower"), 57 | Location( 58 | name: "Louvre Museum", 59 | cityName: "Paris", 60 | coordinates: CLLocationCoordinate2D(latitude: 48.8606, longitude: 2.3376), 61 | description: "The Louvre, or the Louvre Museum, is the world's most-visited museum and a historic monument in Paris, France. It is the home of some of the best-known works of art, including the Mona Lisa and the Venus de Milo. A central landmark of the city, it is located on the Right Bank of the Seine in the city's 1st arrondissement.", 62 | imageNames: [ 63 | "paris-louvre-1", 64 | "paris-louvre-2", 65 | "paris-louvre-3", 66 | ], 67 | link: "https://en.wikipedia.org/wiki/Louvre"), 68 | ] 69 | 70 | } 71 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UILaunchScreen 6 | 7 | UIColorName 8 | AccentColor 9 | UIImageName 10 | logo-launch 11 | UIImageRespectsSafeAreaInsets 12 | 13 | UILaunchScreen 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Models/Location.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Location.swift 3 | // SwiftfulMapApp 4 | // 5 | // Created by Nick Sarno on 11/27/21. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | struct Location: Identifiable, Equatable { 12 | 13 | let name: String 14 | let cityName: String 15 | let coordinates: CLLocationCoordinate2D 16 | let description: String 17 | let imageNames: [String] 18 | let link: String 19 | 20 | // Identifiable 21 | var id: String { 22 | // name = "Colosseum" 23 | // cityName = "Rome" 24 | // id = "ColosseumRome" 25 | name + cityName 26 | } 27 | 28 | // Equatable 29 | static func == (lhs: Location, rhs: Location) -> Bool { 30 | lhs.id == rhs.id 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftfulMapApp/SwiftfulMapAppApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftfulMapAppApp.swift 3 | // SwiftfulMapApp 4 | // 5 | // Created by Nick Sarno on 11/27/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct SwiftfulMapAppApp: App { 12 | 13 | @StateObject private var vm = LocationsViewModel() 14 | 15 | var body: some Scene { 16 | WindowGroup { 17 | LocationsView() 18 | .environmentObject(vm) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftfulMapApp/ViewModels/LocationsViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationsViewModel.swift 3 | // SwiftfulMapApp 4 | // 5 | // Created by Nick Sarno on 11/27/21. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | import SwiftUI 11 | 12 | class LocationsViewModel: ObservableObject { 13 | 14 | // All loaded locations 15 | @Published var locations: [Location] 16 | 17 | // Current location on map 18 | @Published var mapLocation: Location { 19 | didSet { 20 | updateMapRegion(location: mapLocation) 21 | } 22 | } 23 | 24 | // Current region on map 25 | @Published var mapRegion: MKCoordinateRegion = MKCoordinateRegion() 26 | let mapSpan = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1) 27 | 28 | // Show list of locations 29 | @Published var showLocationsList: Bool = false 30 | 31 | // Show location detail via sheet 32 | @Published var sheetLocation: Location? = nil 33 | 34 | init() { 35 | let locations = LocationsDataService.locations 36 | self.locations = locations 37 | self.mapLocation = locations.first! 38 | 39 | self.updateMapRegion(location: locations.first!) 40 | } 41 | 42 | private func updateMapRegion(location: Location) { 43 | withAnimation(.easeInOut) { 44 | mapRegion = MKCoordinateRegion( 45 | center: location.coordinates, 46 | span: mapSpan) 47 | } 48 | } 49 | 50 | func toggleLocationsList() { 51 | withAnimation(.easeInOut) { 52 | // showLocationsList = !showLocationsList 53 | showLocationsList.toggle() 54 | } 55 | } 56 | 57 | func showNextLocation(location: Location) { 58 | withAnimation(.easeInOut) { 59 | mapLocation = location 60 | showLocationsList = false 61 | } 62 | } 63 | 64 | func nextButtonPressed() { 65 | // Get the current index 66 | guard let currentIndex = locations.firstIndex(where: { $0 == mapLocation }) else { 67 | print("Could not find current index in locations array! Should never happen.") 68 | return 69 | } 70 | 71 | // Check if the currentIndex is valid 72 | let nextIndex = currentIndex + 1 73 | guard locations.indices.contains(nextIndex) else { 74 | // Next index is NOT valid 75 | // Restart from 0 76 | guard let firstLocation = locations.first else { return } 77 | showNextLocation(location: firstLocation) 78 | return 79 | } 80 | 81 | // Next index IS valid 82 | let nextLocation = locations[nextIndex] 83 | showNextLocation(location: nextLocation) 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Views/LocationDetailView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationDetailView.swift 3 | // SwiftfulMapApp 4 | // 5 | // Created by Nick Sarno on 11/28/21. 6 | // 7 | 8 | import SwiftUI 9 | import MapKit 10 | 11 | struct LocationDetailView: View { 12 | 13 | @EnvironmentObject private var vm: LocationsViewModel 14 | let location: Location 15 | 16 | var body: some View { 17 | ScrollView { 18 | VStack { 19 | imageSection 20 | .shadow(color: Color.black.opacity(0.3), radius: 20, x: 0, y: 10) 21 | 22 | VStack(alignment: .leading, spacing: 16) { 23 | titleSection 24 | Divider() 25 | descriptionSection 26 | Divider() 27 | mapLayer 28 | } 29 | .frame(maxWidth: .infinity, alignment: .leading) 30 | .padding() 31 | } 32 | } 33 | .ignoresSafeArea() 34 | .background(.ultraThinMaterial) 35 | .overlay(backButton, alignment: .topLeading) 36 | } 37 | } 38 | 39 | struct LocationDetailView_Previews: PreviewProvider { 40 | static var previews: some View { 41 | LocationDetailView(location: LocationsDataService.locations.first!) 42 | .environmentObject(LocationsViewModel()) 43 | } 44 | } 45 | 46 | extension LocationDetailView { 47 | 48 | private var imageSection: some View { 49 | TabView { 50 | ForEach(location.imageNames, id: \.self) { 51 | Image($0) 52 | .resizable() 53 | .scaledToFill() 54 | .frame(width: UIDevice.current.userInterfaceIdiom == .pad ? nil : UIScreen.main.bounds.width) 55 | .clipped() 56 | } 57 | } 58 | .frame(height: 500) 59 | .tabViewStyle(PageTabViewStyle()) 60 | } 61 | 62 | private var titleSection: some View { 63 | VStack(alignment: .leading, spacing: 8) { 64 | Text(location.name) 65 | .font(.largeTitle) 66 | .fontWeight(.semibold) 67 | 68 | Text(location.cityName) 69 | .font(.title3) 70 | .foregroundColor(.secondary) 71 | } 72 | } 73 | 74 | private var descriptionSection: some View { 75 | VStack(alignment: .leading, spacing: 16) { 76 | Text(location.description) 77 | .font(.subheadline) 78 | .foregroundColor(.secondary) 79 | 80 | if let url = URL(string: location.link) { 81 | Link("Read more on Wikipedia", destination: url) 82 | .font(.headline) 83 | .tint(.blue) 84 | } 85 | } 86 | } 87 | 88 | private var mapLayer: some View { 89 | Map(coordinateRegion: .constant(MKCoordinateRegion( 90 | center: location.coordinates, 91 | span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))), 92 | annotationItems: [location]) { location in 93 | MapAnnotation(coordinate: location.coordinates) { 94 | LocationMapAnnotationView() 95 | .shadow(radius: 10) 96 | } 97 | } 98 | .allowsHitTesting(false) 99 | .aspectRatio(1, contentMode: .fit) 100 | .cornerRadius(30) 101 | } 102 | 103 | private var backButton: some View { 104 | Button { 105 | vm.sheetLocation = nil 106 | } label: { 107 | Image(systemName: "xmark") 108 | .font(.headline) 109 | .padding(16) 110 | .foregroundColor(.primary) 111 | .background(.thickMaterial) 112 | .cornerRadius(10) 113 | .shadow(radius: 4) 114 | .padding() 115 | } 116 | 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Views/LocationMapAnnotationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationMapAnnotationView.swift 3 | // SwiftfulMapApp 4 | // 5 | // Created by Nick Sarno on 11/28/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LocationMapAnnotationView: View { 11 | 12 | let accentColor = Color("AccentColor") 13 | 14 | var body: some View { 15 | VStack(spacing: 0) { 16 | Image(systemName: "map.circle.fill") 17 | .resizable() 18 | .scaledToFit() 19 | .frame(width: 30, height: 30) 20 | .font(.headline) 21 | .foregroundColor(.white) 22 | .padding(6) 23 | .background(accentColor) 24 | .clipShape(Circle()) 25 | 26 | Image(systemName: "triangle.fill") 27 | .resizable() 28 | .scaledToFit() 29 | .foregroundColor(accentColor) 30 | .frame(width: 10, height: 10) 31 | .rotationEffect(Angle(degrees: 180)) 32 | .offset(y: -3) 33 | .padding(.bottom, 40) 34 | } 35 | } 36 | } 37 | 38 | struct LocationMapAnnotationView_Previews: PreviewProvider { 39 | static var previews: some View { 40 | ZStack { 41 | Color.black.ignoresSafeArea() 42 | 43 | LocationMapAnnotationView() 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Views/LocationPreviewView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationPreviewView.swift 3 | // SwiftfulMapApp 4 | // 5 | // Created by Nick Sarno on 11/28/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LocationPreviewView: View { 11 | 12 | @EnvironmentObject private var vm: LocationsViewModel 13 | let location: Location 14 | 15 | var body: some View { 16 | HStack(alignment: .bottom, spacing: 0) { 17 | VStack(alignment: .leading, spacing: 16) { 18 | imageSection 19 | titleSection 20 | } 21 | 22 | VStack(spacing: 8) { 23 | learnMoreButton 24 | nextButton 25 | } 26 | } 27 | .padding(20) 28 | .background( 29 | RoundedRectangle(cornerRadius: 10) 30 | .fill(.ultraThinMaterial) 31 | .offset(y: 65) 32 | ) 33 | .cornerRadius(10) 34 | } 35 | } 36 | 37 | struct LocationPreviewView_Previews: PreviewProvider { 38 | static var previews: some View { 39 | ZStack { 40 | Color.green.ignoresSafeArea() 41 | 42 | LocationPreviewView(location: LocationsDataService.locations.first!) 43 | .padding() 44 | } 45 | .environmentObject(LocationsViewModel()) 46 | } 47 | } 48 | 49 | extension LocationPreviewView { 50 | 51 | private var imageSection: some View { 52 | ZStack { 53 | if let imageName = location.imageNames.first { 54 | Image(imageName) 55 | .resizable() 56 | .scaledToFill() 57 | .frame(width: 100, height: 100) 58 | .cornerRadius(10) 59 | } 60 | } 61 | .padding(6) 62 | .background(Color.white) 63 | .cornerRadius(10) 64 | } 65 | 66 | private var titleSection: some View { 67 | VStack(alignment: .leading, spacing: 4) { 68 | Text(location.name) 69 | .font(.title2) 70 | .fontWeight(.bold) 71 | 72 | Text(location.cityName) 73 | .font(.subheadline) 74 | } 75 | .frame(maxWidth: .infinity, alignment: .leading) 76 | } 77 | 78 | private var learnMoreButton: some View { 79 | Button { 80 | vm.sheetLocation = location 81 | } label: { 82 | Text("Learn more") 83 | .font(.headline) 84 | .frame(width: 125, height: 35) 85 | } 86 | .buttonStyle(.borderedProminent) 87 | } 88 | 89 | private var nextButton: some View { 90 | Button { 91 | vm.nextButtonPressed() 92 | } label: { 93 | Text("Next") 94 | .font(.headline) 95 | .frame(width: 125, height: 35) 96 | } 97 | .buttonStyle(.bordered) 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Views/LocationsListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationsListView.swift 3 | // SwiftfulMapApp 4 | // 5 | // Created by Nick Sarno on 11/27/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LocationsListView: View { 11 | 12 | @EnvironmentObject private var vm: LocationsViewModel 13 | 14 | var body: some View { 15 | List { 16 | ForEach(vm.locations) { location in 17 | Button { 18 | vm.showNextLocation(location: location) 19 | } label: { 20 | listRowView(location: location) 21 | } 22 | .padding(.vertical, 4) 23 | .listRowBackground(Color.clear) 24 | } 25 | } 26 | .listStyle(PlainListStyle()) 27 | } 28 | } 29 | 30 | struct LocationsListView_Previews: PreviewProvider { 31 | static var previews: some View { 32 | LocationsListView() 33 | .environmentObject(LocationsViewModel()) 34 | } 35 | } 36 | 37 | extension LocationsListView { 38 | 39 | private func listRowView(location: Location) -> some View { 40 | HStack { 41 | if let imageName = location.imageNames.first { 42 | Image(imageName) 43 | .resizable() 44 | .scaledToFill() 45 | .frame(width: 45, height: 45) 46 | .cornerRadius(10) 47 | } 48 | 49 | VStack(alignment: .leading) { 50 | Text(location.name) 51 | .font(.headline) 52 | Text(location.cityName) 53 | .font(.subheadline) 54 | } 55 | .frame(maxWidth: .infinity, alignment: .leading) 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /SwiftfulMapApp/Views/LocationsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationsView.swift 3 | // SwiftfulMapApp 4 | // 5 | // Created by Nick Sarno on 11/27/21. 6 | // 7 | 8 | import SwiftUI 9 | import MapKit 10 | 11 | struct LocationsView: View { 12 | 13 | @EnvironmentObject private var vm: LocationsViewModel 14 | let maxWidthForIpad: CGFloat = 700 15 | 16 | var body: some View { 17 | ZStack { 18 | mapLayer 19 | .ignoresSafeArea() 20 | 21 | VStack(spacing: 0) { 22 | header 23 | .padding() 24 | .frame(maxWidth: maxWidthForIpad) 25 | 26 | Spacer() 27 | locationsPreviewStack 28 | } 29 | } 30 | .sheet(item: $vm.sheetLocation, onDismiss: nil) { location in 31 | LocationDetailView(location: location) 32 | } 33 | } 34 | } 35 | 36 | struct LocationsView_Previews: PreviewProvider { 37 | static var previews: some View { 38 | LocationsView() 39 | .environmentObject(LocationsViewModel()) 40 | } 41 | } 42 | 43 | extension LocationsView { 44 | 45 | private var header: some View { 46 | VStack { 47 | Button(action: vm.toggleLocationsList) { 48 | Text(vm.mapLocation.name + ", " + vm.mapLocation.cityName) 49 | .font(.title2) 50 | .fontWeight(.black) 51 | .foregroundColor(.primary) 52 | .frame(height: 55) 53 | .frame(maxWidth: .infinity) 54 | .animation(.none, value: vm.mapLocation) 55 | .overlay(alignment: .leading) { 56 | Image(systemName: "arrow.down") 57 | .font(.headline) 58 | .foregroundColor(.primary) 59 | .padding() 60 | .rotationEffect(Angle(degrees: vm.showLocationsList ? 180 : 0)) 61 | } 62 | } 63 | 64 | if vm.showLocationsList { 65 | LocationsListView() 66 | } 67 | } 68 | .background(.thickMaterial) 69 | .cornerRadius(10) 70 | .shadow(color: Color.black.opacity(0.3), radius: 20, x: 0, y: 15) 71 | } 72 | 73 | private var mapLayer: some View { 74 | Map(coordinateRegion: $vm.mapRegion, 75 | annotationItems: vm.locations, 76 | annotationContent: { location in 77 | MapAnnotation(coordinate: location.coordinates) { 78 | LocationMapAnnotationView() 79 | .scaleEffect(vm.mapLocation == location ? 1 : 0.7) 80 | .shadow(radius: 10) 81 | .onTapGesture { 82 | vm.showNextLocation(location: location) 83 | } 84 | } 85 | }) 86 | } 87 | 88 | private var locationsPreviewStack: some View { 89 | ZStack { 90 | ForEach(vm.locations) { location in 91 | if vm.mapLocation == location { 92 | LocationPreviewView(location: location) 93 | .shadow(color: Color.black.opacity(0.3), radius: 20) 94 | .padding() 95 | .frame(maxWidth: maxWidthForIpad) 96 | .frame(maxWidth: .infinity) 97 | .transition(.asymmetric( 98 | insertion: .move(edge: .trailing), 99 | removal: .move(edge: .leading))) 100 | } 101 | } 102 | } 103 | } 104 | 105 | } 106 | --------------------------------------------------------------------------------