diff --git a/.gitignore b/.gitignore index 89e97263..b0bb79ae 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,15 @@ build/ DerivedData/ +# Swift Package Manager +.build/ +Package.resolved +.swiftpm/ +.netrc + +# Config files with sensitive data +*.xcconfig + ## Various settings *.pbxuser !default.pbxuser diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI.xcodeproj/project.pbxproj b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI.xcodeproj/project.pbxproj new file mode 100644 index 00000000..b2874f82 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI.xcodeproj/project.pbxproj @@ -0,0 +1,386 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + DAAEE44B2CDC1703004A094F /* GoogleMaps in Frameworks */ = {isa = PBXBuildFile; productRef = DAAEE44A2CDC1703004A094F /* GoogleMaps */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + DA2B59032CDAC2D500520B55 /* GoogleMaps-SwiftUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GoogleMaps-SwiftUI.app"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + DA2B59402CDB076000520B55 /* Exceptions for "GoogleMaps-SwiftUI" folder in "GoogleMaps-SwiftUI" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = DA2B59022CDAC2D500520B55 /* GoogleMaps-SwiftUI */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + DA2B59052CDAC2D500520B55 /* GoogleMaps-SwiftUI */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + DA2B59402CDB076000520B55 /* Exceptions for "GoogleMaps-SwiftUI" folder in "GoogleMaps-SwiftUI" target */, + ); + path = "GoogleMaps-SwiftUI"; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + DA2B59002CDAC2D500520B55 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DAAEE44B2CDC1703004A094F /* GoogleMaps in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + DA2B58FA2CDAC2D500520B55 = { + isa = PBXGroup; + children = ( + DA2B59052CDAC2D500520B55 /* GoogleMaps-SwiftUI */, + DA2B59042CDAC2D500520B55 /* Products */, + ); + sourceTree = ""; + }; + DA2B59042CDAC2D500520B55 /* Products */ = { + isa = PBXGroup; + children = ( + DA2B59032CDAC2D500520B55 /* GoogleMaps-SwiftUI.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + DA2B59022CDAC2D500520B55 /* GoogleMaps-SwiftUI */ = { + isa = PBXNativeTarget; + buildConfigurationList = DA2B59112CDAC2D600520B55 /* Build configuration list for PBXNativeTarget "GoogleMaps-SwiftUI" */; + buildPhases = ( + DA2B58FF2CDAC2D500520B55 /* Sources */, + DA2B59002CDAC2D500520B55 /* Frameworks */, + DA2B59012CDAC2D500520B55 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + DA2B59052CDAC2D500520B55 /* GoogleMaps-SwiftUI */, + ); + name = "GoogleMaps-SwiftUI"; + packageProductDependencies = ( + DAAEE44A2CDC1703004A094F /* GoogleMaps */, + ); + productName = "GoogleMaps-SwiftUI"; + productReference = DA2B59032CDAC2D500520B55 /* GoogleMaps-SwiftUI.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DA2B58FB2CDAC2D500520B55 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1610; + LastUpgradeCheck = 1610; + TargetAttributes = { + DA2B59022CDAC2D500520B55 = { + CreatedOnToolsVersion = 16.1; + }; + }; + }; + buildConfigurationList = DA2B58FE2CDAC2D500520B55 /* Build configuration list for PBXProject "GoogleMaps-SwiftUI" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = DA2B58FA2CDAC2D500520B55; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + DAAEE4492CDC1703004A094F /* XCRemoteSwiftPackageReference "ios-maps-sdk" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = DA2B59042CDAC2D500520B55 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DA2B59022CDAC2D500520B55 /* GoogleMaps-SwiftUI */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + DA2B59012CDAC2D500520B55 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + DA2B58FF2CDAC2D500520B55 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + DA2B590F2CDAC2D600520B55 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = DA2B59052CDAC2D500520B55 /* GoogleMaps-SwiftUI */; + baseConfigurationReferenceRelativePath = "GoogleMaps-SwiftUI.xcconfig"; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LIBRARY_SEARCH_PATHS = ""; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ""; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + DA2B59102CDAC2D600520B55 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = ""; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + DA2B59122CDAC2D600520B55 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"GoogleMaps-SwiftUI/Preview Content\""; + DEVELOPMENT_TEAM = 6586N6K7KU; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "GoogleMaps-SwiftUI/Info.plist"; + INFOPLIST_KEY_LSApplicationCategoryType = ""; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ""; + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = "com.example.GoogleMaps-SwiftUI"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + DA2B59132CDAC2D600520B55 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"GoogleMaps-SwiftUI/Preview Content\""; + DEVELOPMENT_TEAM = 6586N6K7KU; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "GoogleMaps-SwiftUI/Info.plist"; + INFOPLIST_KEY_LSApplicationCategoryType = ""; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = "com.example.GoogleMaps-SwiftUI"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + DA2B58FE2CDAC2D500520B55 /* Build configuration list for PBXProject "GoogleMaps-SwiftUI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DA2B590F2CDAC2D600520B55 /* Debug */, + DA2B59102CDAC2D600520B55 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DA2B59112CDAC2D600520B55 /* Build configuration list for PBXNativeTarget "GoogleMaps-SwiftUI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DA2B59122CDAC2D600520B55 /* Debug */, + DA2B59132CDAC2D600520B55 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + DAAEE4492CDC1703004A094F /* XCRemoteSwiftPackageReference "ios-maps-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/googlemaps/ios-maps-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 9.1.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + DAAEE44A2CDC1703004A094F /* GoogleMaps */ = { + isa = XCSwiftPackageProductDependency; + package = DAAEE4492CDC1703004A094F /* XCRemoteSwiftPackageReference "ios-maps-sdk" */; + productName = GoogleMaps; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = DA2B58FB2CDAC2D500520B55 /* Project object */; +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..23058801 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Assets.xcassets/Contents.json b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Dialog/Dialog.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Dialog/Dialog.swift new file mode 100644 index 00000000..61a31995 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Dialog/Dialog.swift @@ -0,0 +1,53 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + + +struct Dialog: View { + @StateObject private var viewModel = MapExamplesViewModel() + + private let mapOptions: GMSMapViewOptions = { + var options = GMSMapViewOptions() + options.camera = .camera(.googleplex) + return options + }() + + var body: some View { + NavigationStack { + VStack(spacing: 0) { + // Top half - Map View centered on Seattle + GoogleMapView(options: mapOptions) + .frame(maxWidth: .infinity) + .frame(height: UIScreen.main.bounds.height * 0.5) + + // Bottom half - Examples List + List(viewModel.examples) { example in + NavigationLink(destination: example.destination) { + VStack(alignment: .leading, spacing: 4) { + Text(example.title) + .font(.headline) + Text(example.description) + .font(.subheadline) + .foregroundColor(.secondary) + } + .padding(.vertical, 4) + } + } + } + .navigationTitle("Google Maps SwiftUI Samples") + .navigationBarTitleDisplayMode(.inline) + } + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Dialog/Helper/MapExample.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Dialog/Helper/MapExample.swift new file mode 100644 index 00000000..eb2eac7b --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Dialog/Helper/MapExample.swift @@ -0,0 +1,21 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI + +struct MapExample: Identifiable { + let id = UUID() + let title: String + let description: String + let destination: AnyView +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Dialog/Helper/MapExamplesViewModel.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Dialog/Helper/MapExamplesViewModel.swift new file mode 100644 index 00000000..417c6fd1 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Dialog/Helper/MapExamplesViewModel.swift @@ -0,0 +1,62 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + +class MapExamplesViewModel: ObservableObject { + + @Published var examples: [MapExample] = [ + MapExample( + title: "Basic map", + description: "A simple map. Shows how to initalize and update map options.", + destination: AnyView(BasicMap()) + ), + MapExample( + title: "Basic map with MapID", + description: "Shows how to initialize a map with a mapID.", + destination: AnyView(BasicMapWithMapID()) + ), + MapExample( + title: "Map with custom camera", + description: "Map camera position set to street-level 3D perspective.", + destination: AnyView(MapWithCustomCamera()) + ), + MapExample( + title: "Map with marker", + description: "Implements a map marker as a viewModifier. Extends the GMSMarker model.", + destination: AnyView(MapWithMarker()) + ), + MapExample( + title: "Map with markers", + description: "Applies a collection of markers to a map. Extends the GMSMarker model.", + destination: AnyView(MapWithMarkers()) + ), + MapExample( + title: "Map types", + description: "How to set the satellite, terrain, or hybrid map type property.", + destination: AnyView(MapTypes()) + ), + MapExample( + title: "Map with containers", + description: "Shows integration with SwiftUI layouts, allowing for standard modifiers like frame and padding.", + destination: AnyView(MapWithContainer()) + ), + MapExample( + title: "Handle map events", + description: "A GoogleMapView configured with handlers for map and marker tap events.", + destination: AnyView(HandleMapEvents()) + ) + ] + +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/GoogleMapView/GoogleMapView.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/GoogleMapView/GoogleMapView.swift new file mode 100644 index 00000000..cefb610c --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/GoogleMapView/GoogleMapView.swift @@ -0,0 +1,149 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + +/// A SwiftUI wrapper for GMSMapView that displays a map with optional markers and configurable map type +struct GoogleMapView: UIViewRepresentable { + + // Configuration properties - set at initialization + private let options: GMSMapViewOptions + + /// Array of markers to display on the map + private var markers: [GMSMarker] + + /// Type of map to display (normal, satellite, hybrid, terrain) + private let mapType: GMSMapViewType + + // Runtime updatable properties + private var camera: GMSCameraPosition? + private var backgroundColor: UIColor? + + /// Shared delegate instance to handle map interactions across all instances + /// Using static ensures callbacks work together when chaining modifiers + private static let mapDelegate = GoogleMapViewDelegate() + + init(options: GMSMapViewOptions, + markers: [GMSMarker] = [], + mapType: GMSMapViewType = .normal) { + self.options = options + self.markers = markers + self.mapType = mapType + } + + /// Creates the underlying UIKit map view + func makeUIView(context: Context) -> GMSMapView { + // Initialize map with current options + let mapView = GMSMapView(options: options) + mapView.mapType = mapType + + // Set shared delegate to handle interactions + mapView.delegate = Self.mapDelegate + + return mapView + } + + /// Updates the map view when SwiftUI state changes + func updateUIView(_ uiView: GMSMapView, context: Context) { + // Update runtime properties if set + if let camera = camera { + uiView.camera = camera + } + + if let backgroundColor = backgroundColor { + uiView.backgroundColor = backgroundColor + } + + //clears all markers and polylines + uiView.clear() + + // Refresh markers on the map + markers.forEach { marker in + marker.map = uiView + } + + uiView.mapType = mapType // Update map type if changed + } +} + +// MARK: - viewModifiers and Markers + +extension GoogleMapView { + /// Updates the camera position of the map view during runtime + /// - Parameter position: New camera position to apply + /// - Returns: Updated GoogleMapView instance + func camera(_ position: GMSCameraPosition?) -> GoogleMapView { + var view = self + if let position = position { + view.camera = position + } + return view + } + + /// Updates the background color of the map view during runtime + /// - Parameter color: New background color to apply + /// - Returns: Updated GoogleMapView instance + func backgroundColor(_ color: UIColor) -> GoogleMapView { + var view = self + view.backgroundColor = color + return view + } + + /// Changes the map display type + /// - Parameter type: GMSMapViewType to use (.normal, .satellite, etc) + /// - Returns: New GoogleMapView instance with updated map type + func mapType(_ type: GMSMapViewType) -> GoogleMapView { + GoogleMapView(options: options, markers: markers, mapType: type) + } + + /// Adds markers to the map + /// - Parameter markers: Array of GMSMarker objects to display + /// - Returns: New GoogleMapView instance with updated markers + func mapMarkers(_ markers: [GMSMarker]) -> GoogleMapView { + var view = self + view.markers = markers + return view + } + +} + +// MARK: - View Callbacks + +extension GoogleMapView { + /// Adds handler for map tap events + /// - Parameter handler: Closure called when map is tapped, providing tap coordinates + /// - Returns: Same GoogleMapView instance with updated tap handler + func onMapTapped(_ handler: @escaping (CLLocationCoordinate2D) -> Void) -> GoogleMapView { + Self.mapDelegate.tapHandler = handler + return self + } + + /// Adds handler for marker tap events + /// - Parameter handler: Closure called when marker is tapped + /// - Returns: Same GoogleMapView instance with updated marker handler + /// Return true from handler to indicate tap was handled + func onMarkerTapped(_ handler: @escaping (GMSMarker) -> Bool) -> GoogleMapView { + Self.mapDelegate.markerTapHandler = handler + return self + } +} + +extension View { + /// Configures the view to ignore safe areas except for the top + /// Useful for map views that should fill the screen below status bar + /// - Returns: Modified view that extends to screen edges except top + func ignoresSafeAreaExceptTop() -> some View { + ignoresSafeArea(.container, edges: [.bottom, .horizontal]) + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/GoogleMapView/GoogleMapViewDelegate.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/GoogleMapView/GoogleMapViewDelegate.swift new file mode 100644 index 00000000..9c61185a --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/GoogleMapView/GoogleMapViewDelegate.swift @@ -0,0 +1,40 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import Foundation +import GoogleMaps + +/// Delegate class that handles map interaction events from GMSMapView +/// Provides callback support for map taps and marker taps through closure handlers +class GoogleMapViewDelegate: NSObject, GMSMapViewDelegate { + + var tapHandler: ((CLLocationCoordinate2D) -> Void)? + var markerTapHandler: ((GMSMarker) -> Bool)? + + /// Called by GMSMapView when user taps the map at a specific coordinate + /// - Parameters: + /// - mapView: The GMSMapView that detected the tap + /// - coordinate: The geographic coordinate where the tap occurred + func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) { + tapHandler?(coordinate) // Forward tap to handler if one is set + } + + /// Called by GMSMapView when user taps a marker on the map + /// - Parameters: + /// - mapView: The GMSMapView that detected the tap + /// - marker: The GMSMarker that was tapped + /// - Returns: true if tap was handled by the app, false to allow default marker behavior + func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool { + return markerTapHandler?(marker) ?? false // Forward to handler or use default behavior + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/GoogleMaps_SwiftUIApp.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/GoogleMaps_SwiftUIApp.swift new file mode 100644 index 00000000..c857781d --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/GoogleMaps_SwiftUIApp.swift @@ -0,0 +1,46 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + +@main +struct GoogleMaps_SwiftUIApp: App { + + init() { + /* + API Key Setup: + 1. Create a .xcconfig file at the project root level + 2. Add this line: MAPS_API_KEY = your_api_key_here + 3. Get an API key from: https://developers.google.com/maps/documentation/ios-sdk/get-api-key + 4. Replace "your_api_key_here" with the API key obtained in step 3 + + Note: Never commit your actual API key to source control + */ + + guard let infoDictionary: [String: Any] = Bundle.main.infoDictionary else { + fatalError("Info.plist not found") + } + guard let apiKey: String = infoDictionary["MAPS_API_KEY"] as? String else { + fatalError("MAPS_API_KEY not set in Info.plist") + } + let _ = GMSServices.provideAPIKey(apiKey) + + } + var body: some Scene { + WindowGroup { + //present the dialog of map samples + Dialog() + } + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Info.plist b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Info.plist new file mode 100644 index 00000000..b6770fae --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Info.plist @@ -0,0 +1,8 @@ + + + + + MAPS_API_KEY + $(MAPS_API_KEY) + + diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Preview Content/Preview Assets.xcassets/Contents.json b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/BasicMap.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/BasicMap.swift new file mode 100644 index 00000000..ccb89d4f --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/BasicMap.swift @@ -0,0 +1,44 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + +struct BasicMap: View { + + // Initial options - set once at creation + private let mapOptions: GMSMapViewOptions = { + var options = GMSMapViewOptions() + options.camera = .camera(.googleplex) // Initial camera position + return options + }() + + // Runtime updatable property + @State private var newCamera: GMSCameraPosition? + + var body: some View { + VStack { + GoogleMapView(options: mapOptions) + .camera(newCamera) // Runtime camera updates + .ignoresSafeAreaExceptTop() + + VStack(spacing: 12) { + Button("Fly to New York") { + newCamera = .camera(.newYork) + } + + } + .padding() + } + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/BasicMapWithMapID.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/BasicMapWithMapID.swift new file mode 100644 index 00000000..a02c3e1d --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/BasicMapWithMapID.swift @@ -0,0 +1,44 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + +struct BasicMapWithMapID: View { + + private let mapOptions: GMSMapViewOptions = { + var options = GMSMapViewOptions() + options.camera = .camera(.googleplex) // Initial camera position + + /* + 1. See the following url to get a mapID. + https://goo.gle/get-map-id + + 2. Provide your mapID here + let mapID = GMSMapID(identifier: "YOUR_MAP_ID") + */ + + let mapID: GMSMapID = .demoMapID //used for demostration only + options.mapID = mapID + + return options + }() + + var body: some View { + VStack { + GoogleMapView(options: mapOptions) + .ignoresSafeAreaExceptTop() + } + } +} + diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/HandleMapEvents.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/HandleMapEvents.swift new file mode 100644 index 00000000..0fa3495b --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/HandleMapEvents.swift @@ -0,0 +1,69 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + +struct HandleMapEvents: View { + + @State var response: String = "" + + private var mapOptions: GMSMapViewOptions = { + var options = GMSMapViewOptions() + // Initialize map centered on San Francisco + options.camera = .camera(.sanFrancisco) + + // Or with custom zoom level for closer view + // options.camera = .camera(.sanFrancisco, zoom: 15) + return options + }() + + // multiple marker example + let multipleMarkers = [ + GMSMarker(position: .chinatownGate), + GMSMarker(position: .coitTower), + GMSMarker(position: .ferryBuilding), + GMSMarker(position: .fishermansWharf) + ] + + var body: some View { + + VStack(spacing: 16) { + GoogleMapView(options: mapOptions) + .mapMarkers(multipleMarkers) + .onMarkerTapped { marker in + response = "Marker tapped at: \(marker.position)" + return true + } + .onMapTapped { coordinate in + response = "Map tapped at: \(coordinate.latitude), \(coordinate.longitude)" + } + .ignoresSafeAreaExceptTop() + .frame(maxWidth: .infinity, minHeight: 325) + + HStack { + VStack(alignment: .leading, spacing: 8) { + Text("Tap a Map location or Marker") + .font(.headline) + + Text(response) + .font(.body) + .foregroundColor(.secondary) + } + Spacer() + } + .padding(.horizontal) + } + + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/Helper/CLLocationCoordinate2D.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/Helper/CLLocationCoordinate2D.swift new file mode 100644 index 00000000..d09c448d --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/Helper/CLLocationCoordinate2D.swift @@ -0,0 +1,66 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import Foundation +import CoreLocation + +/// Extension to CLLocationCoordinate2D providing convenient preset coordinates +/// for common locations. Simplifies marker placement and location references. +extension CLLocationCoordinate2D { + /// San Francisco, California - Tech hub of the West Coast + /// Centered approximately on the Financial District + static let sanFrancisco = CLLocationCoordinate2D( + latitude: 37.7749, + longitude: -122.4194 + ) + +/// Fisherman's Wharf - Historic waterfront district + /// Famous for seafood restaurants and tourist attractions + static let fishermansWharf = CLLocationCoordinate2D( + latitude: 37.8080, + longitude: -122.4177 + ) + + /// Ferry Building - Historic transportation and food marketplace + /// Iconic landmark on the Embarcadero waterfront + static let ferryBuilding = CLLocationCoordinate2D( + latitude: 37.7955, + longitude: -122.3937 + ) + + /// Chinatown Gate - Entry to largest Chinatown outside of Asia + /// Located at Grant Avenue and Bush Street + static let chinatownGate = CLLocationCoordinate2D( + latitude: 37.7908, + longitude: -122.4058 + ) + + /// Coit Tower - Art Deco tower on Telegraph Hill + /// Offers panoramic views of the city and bay + static let coitTower = CLLocationCoordinate2D( + latitude: 37.8024, + longitude: -122.4058 + ) + + /// New York City, New York - Centered on Lower Manhattan + /// Financial District and nearby landmarks + static let newYork = CLLocationCoordinate2D( + latitude: 40.7128, + longitude: -74.0060 + ) + + // Add more locations as needed. Example format: + /// /// City Name, State/Country - Brief description + /// /// Notable landmarks or area description + /// static let cityName = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0) +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/Helper/GMSCameraPosition.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/Helper/GMSCameraPosition.swift new file mode 100644 index 00000000..a50367b7 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/Helper/GMSCameraPosition.swift @@ -0,0 +1,125 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import Foundation +import GoogleMaps + +extension GMSCameraPosition { + /// Creates a camera position focused on a predefined location with configurable view parameters + /// - Parameters: + /// - location: A predefined MapLocation indicating where to center the map + /// - zoom: The zoom level for the camera position (default: 12) + /// - Values range from 1 (world view) to 20 (building level) + /// - Default value of 12 typically shows a city-level view + /// - bearing: The direction the camera is pointing in degrees (default: 0) + /// - Values range from 0 to 360, where 0° points north + /// - viewingAngle: The angle of the camera from the nadir (directly facing Earth) (default: 0) + /// - Values range from 0 (directly overhead) to 90 degrees (horizontal) + /// - Returns: A configured GMSCameraPosition object ready for use with GMSMapViewOptions + static func camera(_ location: MapLocation, + zoom: Float = 12, + bearing: Double = 0, + viewingAngle: Double = 0) -> GMSCameraPosition { + + switch location { + case .sanFrancisco: + return GMSCameraPosition( + latitude: 37.7749, // Downtown San Francisco + longitude: -122.4194, + zoom: zoom, // Default provides good city overview + bearing: bearing, // Camera orientation + viewingAngle: viewingAngle // Camera tilt + ) + case .newYork: + return GMSCameraPosition( + latitude: 40.7128, // Lower Manhattan + longitude: -74.0060, + zoom: zoom, + bearing: bearing, + viewingAngle: viewingAngle + ) + case .seattle: + return GMSCameraPosition( + latitude: 47.6062, // Downtown Seattle + longitude: -122.3321, + zoom: zoom, + bearing: bearing, + viewingAngle: viewingAngle + ) + case .googleplex: + return GMSCameraPosition( + latitude: 37.4220, // Googleplex HQ + longitude: -122.0841, + zoom: zoom, + bearing: bearing, + viewingAngle: viewingAngle + ) + } + } + + /// Convenience method for creating a street-level 3D perspective + /// - Parameters: + /// - location: A predefined MapLocation indicating where to center the map + /// - bearing: The direction to face in degrees (default: 0 = north) + /// - Returns: A GMSCameraPosition configured for immersive street-level viewing + static func streetLevel(_ location: MapLocation, bearing: Double = 0) -> GMSCameraPosition { + camera(location, + zoom: 18, // Close street-level zoom + bearing: bearing, + viewingAngle: 45) // 45-degree viewing angle for 3D perspective + } +} + +/// Predefined map locations for common use cases. +/// Each case represents a significant location with preset coordinates. +enum MapLocation { + /// San Francisco, California - Tech hub of the West Coast + /// Centered approximately on the Financial District + case sanFrancisco + + /// New York City, New York - Centered on Lower Manhattan + /// Includes major landmarks like the Financial District and nearby boroughs + case newYork + + /// Seattle, Washington - Major Pacific Northwest tech center + /// Centered on downtown, including Pike Place Market and Seattle Center area + case seattle + + /// Mountain View, California - Google's Global Headquarters + /// Centered on the Googleplex campus, including Charleston Park and the Android Lawn Statues + case googleplex + + // Add more locations as needed. Example format: + /// /// City Name, State/Country - Brief description + /// /// Notable landmarks or areas included in this view + /// case cityName +} + +/* Usage Example: + +struct ContentView: View { + @State private var mapOptions: GMSMapViewOptions = { + var options = GMSMapViewOptions() + // Initialize map centered on San Francisco + options.camera = .camera(.sanFrancisco) + + // Or with custom zoom level for closer view + // options.camera = .camera(.sanFrancisco, zoom: 15) + return options + }() + + var body: some View { + GoogleMapView(options: $mapOptions) + } +} +*/ diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapTypes.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapTypes.swift new file mode 100644 index 00000000..55f272f0 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapTypes.swift @@ -0,0 +1,44 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + +struct MapTypes: View { + // Initial options - set once at creation + private let mapOptions: GMSMapViewOptions = { + var options = GMSMapViewOptions() + options.camera = .camera(.googleplex) + return options + }() + + @State private var mapType: GMSMapViewType = .terrain + + var body: some View { + VStack { + GoogleMapView(options: mapOptions) + .mapType(mapType) + .ignoresSafeAreaExceptTop() + + // Available map types: + // .normal - Standard road map with streets, political boundaries, and labels + // .satellite - Satellite imagery without street labels or overlays + // .terrain - Topographic data showing elevation, vegetation, and natural features + // .hybrid - Satellite imagery combined with road overlays and place labels + Button("Switch to Satellite") { + mapType = .satellite + } + .padding() + } + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithContainer.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithContainer.swift new file mode 100644 index 00000000..89343883 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithContainer.swift @@ -0,0 +1,43 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + +struct MapWithContainer: View { + + private let mapOptions: GMSMapViewOptions = { + var options = GMSMapViewOptions() + options.camera = .camera(.seattle) // Initial camera centered on Seattle + return options + }() + + var body: some View { + VStack(spacing: 16) { + + GoogleMapView(options: mapOptions) + .ignoresSafeAreaExceptTop() // Optional property for samples display + .frame(maxWidth: .infinity, minHeight: 325) + + VStack(alignment: .leading) { + Text("Working with Container Views") + .font(.headline) + + Text("The GoogleMapView seamlessly integrates with SwiftUI layouts, allowing for standard modifiers like frame and padding.") + .font(.body) + .foregroundColor(.secondary) + } + .padding(.horizontal) + } + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithCustomCamera.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithCustomCamera.swift new file mode 100644 index 00000000..3ed31934 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithCustomCamera.swift @@ -0,0 +1,42 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + +struct MapWithCustomCamera: View { + + private var mapOptions: GMSMapViewOptions = { + var options = GMSMapViewOptions() + // Initialize map centered on San Francisco + options.camera = .streetLevel(.sanFrancisco, bearing: 45) + + // Or with custom zoom level for closer view + // options.camera = .camera(.sanFrancisco, zoom: 15) + + // For 3D perspective view + // options.camera = .camera(.sanFrancisco, zoom: 18, bearing: 45, viewingAngle: 45) + + // Quick street-level 3D view + // options.camera = .streetLevel(.sanFrancisco, bearing: 45) + return options + }() + + var body: some View { + + //Map camera position set to street-level 3D perspective. + GoogleMapView(options: mapOptions) + .ignoresSafeAreaExceptTop() //optional property for samples display + } +} + diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithMarker.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithMarker.swift new file mode 100644 index 00000000..58e769dd --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithMarker.swift @@ -0,0 +1,39 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + +struct MapWithMarker: View { + + private let mapOptions: GMSMapViewOptions = { + + var options = GMSMapViewOptions() + // Initialize map centered on San Francisco + options.camera = .camera(.sanFrancisco) + + // Or with custom zoom level for closer view + // options.camera = .camera(.sanFrancisco, zoom: 15) + return options + }() + + // Single marker example - no @State needed since markers won't change during runtime + let markers = [ + GMSMarker(position: .sanFrancisco) + ] + + var body: some View { + GoogleMapView(options: mapOptions) + .mapMarkers(markers) + } +} diff --git a/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithMarkers.swift b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithMarkers.swift new file mode 100644 index 00000000..32a2d229 --- /dev/null +++ b/GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/MapWithMarkers.swift @@ -0,0 +1,44 @@ +// Copyright 2024 Google LLC. All rights reserved. +// +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +import SwiftUI +import GoogleMaps + +struct MapWithMarkers: View { + private let mapOptions: GMSMapViewOptions = { + var options = GMSMapViewOptions() + options.camera = .camera(.sanFrancisco) + return options + }() + + // Make markers mutable with @State + @State private var markers: [GMSMarker] = [ + GMSMarker(position: .chinatownGate), + GMSMarker(position: .coitTower), + GMSMarker(position: .ferryBuilding) + ] + + var body: some View { + VStack { + GoogleMapView(options: mapOptions) + .mapMarkers(markers) + .ignoresSafeAreaExceptTop() + + Button("Add Fisherman's Wharf") { + markers.append(GMSMarker(position: .fishermansWharf)) + } + .padding() + } + } +} + diff --git a/README.md b/README.md index 38199005..8a6534aa 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,15 @@ This repository contains sample code for use with the Google Maps SDK for iOS, G The `GoogleMaps` and `GoogleMaps-Swift` sub-directory contains the sample code that is downloaded when you run `pod try GoogleMaps`. To use this project: +For Objective-C samples: + ``` $ cd GoogleMaps $ pod install $ open GoogleMapsDemos.xcworkspace ``` -For Swift samples: +For Swift (UIKit) samples: ``` $ cd GoogleMaps-Swift @@ -28,6 +30,19 @@ You will need to add an API Key to `GoogleMapsDemos/SDKDemoAPIKey.h`. Please see [documentation](https://developers.google.com/maps/documentation/ios-sdk/start#get-key) for details on how to get an API Key. +## GoogleMaps-SwiftUI + +The `GoogleMaps-SwiftUI` sub-directory contains sample code demonstrating how to integrate Google Maps SDK with SwiftUI applications. It provides a modern SwiftUI wrapper around `GMSMapView` with a declarative API for common map configurations and interactions. To use this project: + +``` +$ cd GoogleMaps-SwiftUI +$ open GoogleMaps-SwiftUI.xcodeproj +``` + +You will need to add an API Key to your configuration. Please see the [documentation](https://developers.google.com/maps/documentation/ios-sdk/start#get-key) for details on how to get an API Key. + +This project uses Swift Package Manager and requires the [GoogleMaps package](https://github.com/googlemaps/ios-maps-sdk). The sample code demonstrates best practices for integrating Google Maps into SwiftUI-based iOS applications. + ## GooglePlaces The `GooglePlaces` and `GooglePlaces-Swift` sub-directory contains the sample code that is downloaded