Skip to content

Commit eaf475c

Browse files
[google_maps_flutter] Fix iOS info window regression (#8939)
Fixes a regression introduced during the migration to structured Pigeon data (#8142), where the info window data was not passed through to the underlying SDK marker object. This wasn't caught because there was apparently no native unit testing of marker properties, and this level of info window detail isn't inspected in integration tests. Adds missing tests, and backfills tests for other marker properties as well. Fixes flutter/flutter#159471 ## Pre-Review Checklist [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 8f5844a commit eaf475c

File tree

6 files changed

+252
-36
lines changed

6 files changed

+252
-36
lines changed

packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.15.1
2+
3+
* Fixes regression in displaying info windows.
4+
15
## 2.15.0
26

37
* Adds support for animating the camera with a duration.

packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
1212
2A6906C72D263DF4001F8426 /* GoogleMapsGroundOverlayControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A6906C62D263DE7001F8426 /* GoogleMapsGroundOverlayControllerTests.m */; };
1313
2BDE99378062AE3E60B40021 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3ACE0AFE8D82CD5962486AFD /* Pods_RunnerTests.framework */; };
14+
330909FF2D99B7A60077A751 /* GoogleMapsMarkerControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 330909FE2D99B79B0077A751 /* GoogleMapsMarkerControllerTests.m */; };
1415
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
1516
478116522BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */; };
1617
528F16832C62941000148160 /* FGMClusterManagersControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 528F16822C62941000148160 /* FGMClusterManagersControllerTests.m */; };
@@ -64,6 +65,7 @@
6465
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
6566
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
6667
2A6906C62D263DE7001F8426 /* GoogleMapsGroundOverlayControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsGroundOverlayControllerTests.m; sourceTree = "<group>"; };
68+
330909FE2D99B79B0077A751 /* GoogleMapsMarkerControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsMarkerControllerTests.m; sourceTree = "<group>"; };
6769
3ACE0AFE8D82CD5962486AFD /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6870
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
6971
478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsPolylinesControllerTests.m; sourceTree = "<group>"; };
@@ -212,6 +214,7 @@
212214
6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */,
213215
0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */,
214216
F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */,
217+
330909FE2D99B79B0077A751 /* GoogleMapsMarkerControllerTests.m */,
215218
478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */,
216219
2A6906C62D263DE7001F8426 /* GoogleMapsGroundOverlayControllerTests.m */,
217220
982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */,
@@ -522,6 +525,7 @@
522525
F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */,
523526
6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */,
524527
982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */,
528+
330909FF2D99B7A60077A751 /* GoogleMapsMarkerControllerTests.m in Sources */,
525529
478116522BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m in Sources */,
526530
2A6906C72D263DF4001F8426 /* GoogleMapsGroundOverlayControllerTests.m in Sources */,
527531
0DD7B6C32B744EEF00E857FD /* FLTTileProviderControllerTests.m in Sources */,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
@import google_maps_flutter_ios;
6+
@import google_maps_flutter_ios.Test;
7+
@import XCTest;
8+
@import GoogleMaps;
9+
10+
#import <OCMock/OCMock.h>
11+
#import <google_maps_flutter_ios/messages.g.h>
12+
#import "PartiallyMockedMapView.h"
13+
14+
@interface GoogleMapsMarkerControllerTests : XCTestCase
15+
@end
16+
17+
@implementation GoogleMapsMarkerControllerTests
18+
19+
/// Returns a mocked map view for use with marker controllers.
20+
- (GMSMapView *)mockedMapView {
21+
GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init];
22+
mapViewOptions.frame = CGRectMake(0, 0, 100, 100);
23+
mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0];
24+
return [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions];
25+
}
26+
27+
/// Returns a FLTMarkersController instance instantiated with the given map view.
28+
///
29+
/// The mapView should outlive the controller, as the controller keeps a weak reference to it.
30+
- (FLTMarkersController *)markersControllerWithMapView:(GMSMapView *)mapView {
31+
NSObject<FlutterPluginRegistrar> *mockRegistrar =
32+
OCMStrictProtocolMock(@protocol(FlutterPluginRegistrar));
33+
return [[FLTMarkersController alloc] initWithMapView:mapView
34+
callbackHandler:[[FGMMapsCallbackApi alloc] init]
35+
clusterManagersController:nil
36+
registrar:mockRegistrar];
37+
}
38+
39+
- (FGMPlatformBitmap *)placeholderBitmap {
40+
return [FGMPlatformBitmap makeWithBitmap:[FGMPlatformBitmapDefaultMarker makeWithHue:@0]];
41+
}
42+
43+
- (void)testSetsMarkerNumericProperties {
44+
GMSMapView *mapView = [self mockedMapView];
45+
FLTMarkersController *controller = [self markersControllerWithMapView:mapView];
46+
47+
NSString *markerIdentifier = @"marker";
48+
double anchorX = 3.14;
49+
double anchorY = 2.718;
50+
double alpha = 0.4;
51+
double rotation = 90.0;
52+
double zIndex = 3.0;
53+
double latitutde = 10.0;
54+
double longitude = 20.0;
55+
[controller addMarkers:@[ [FGMPlatformMarker
56+
makeWithAlpha:alpha
57+
anchor:[FGMPlatformPoint makeWithX:anchorX y:anchorY]
58+
consumeTapEvents:YES
59+
draggable:YES
60+
flat:YES
61+
icon:[self placeholderBitmap]
62+
infoWindow:[FGMPlatformInfoWindow
63+
makeWithTitle:@"info title"
64+
snippet:@"info snippet"
65+
anchor:[FGMPlatformPoint makeWithX:0 y:0]]
66+
position:[FGMPlatformLatLng makeWithLatitude:latitutde
67+
longitude:longitude]
68+
rotation:rotation
69+
visible:YES
70+
zIndex:zIndex
71+
markerId:markerIdentifier
72+
clusterManagerId:nil] ]];
73+
74+
FLTGoogleMapMarkerController *markerController =
75+
controller.markerIdentifierToController[markerIdentifier];
76+
GMSMarker *marker = markerController.marker;
77+
78+
const double delta = 0.0001;
79+
XCTAssertEqualWithAccuracy(marker.opacity, alpha, delta);
80+
XCTAssertEqualWithAccuracy(marker.rotation, rotation, delta);
81+
XCTAssertEqualWithAccuracy(marker.zIndex, zIndex, delta);
82+
XCTAssertEqualWithAccuracy(marker.groundAnchor.x, anchorX, delta);
83+
XCTAssertEqualWithAccuracy(marker.groundAnchor.y, anchorY, delta);
84+
XCTAssertEqualWithAccuracy(marker.position.latitude, latitutde, delta);
85+
XCTAssertEqualWithAccuracy(marker.position.longitude, longitude, delta);
86+
}
87+
88+
// Boolean properties are tested individually to ensure they aren't accidentally cross-assigned from
89+
// another property.
90+
- (void)testSetsDraggable {
91+
GMSMapView *mapView = [self mockedMapView];
92+
FLTMarkersController *controller = [self markersControllerWithMapView:mapView];
93+
94+
NSString *markerIdentifier = @"marker";
95+
[controller addMarkers:@[ [FGMPlatformMarker
96+
makeWithAlpha:1.0
97+
anchor:[FGMPlatformPoint makeWithX:0 y:0]
98+
consumeTapEvents:NO
99+
draggable:YES
100+
flat:NO
101+
icon:[self placeholderBitmap]
102+
infoWindow:[FGMPlatformInfoWindow
103+
makeWithTitle:@"info title"
104+
snippet:@"info snippet"
105+
anchor:[FGMPlatformPoint makeWithX:0 y:0]]
106+
position:[FGMPlatformLatLng makeWithLatitude:0.0 longitude:0.0]
107+
rotation:0
108+
visible:NO
109+
zIndex:0
110+
markerId:markerIdentifier
111+
clusterManagerId:nil] ]];
112+
113+
FLTGoogleMapMarkerController *markerController =
114+
controller.markerIdentifierToController[markerIdentifier];
115+
GMSMarker *marker = markerController.marker;
116+
117+
XCTAssertTrue(marker.draggable);
118+
}
119+
120+
// Boolean properties are tested individually to ensure they aren't accidentally cross-assigned from
121+
// another property.
122+
- (void)testSetsFlat {
123+
GMSMapView *mapView = [self mockedMapView];
124+
FLTMarkersController *controller = [self markersControllerWithMapView:mapView];
125+
126+
NSString *markerIdentifier = @"marker";
127+
[controller addMarkers:@[ [FGMPlatformMarker
128+
makeWithAlpha:1.0
129+
anchor:[FGMPlatformPoint makeWithX:0 y:0]
130+
consumeTapEvents:NO
131+
draggable:NO
132+
flat:YES
133+
icon:[self placeholderBitmap]
134+
infoWindow:[FGMPlatformInfoWindow
135+
makeWithTitle:@"info title"
136+
snippet:@"info snippet"
137+
anchor:[FGMPlatformPoint makeWithX:0 y:0]]
138+
position:[FGMPlatformLatLng makeWithLatitude:0.0 longitude:0.0]
139+
rotation:0
140+
visible:NO
141+
zIndex:0
142+
markerId:markerIdentifier
143+
clusterManagerId:nil] ]];
144+
145+
FLTGoogleMapMarkerController *markerController =
146+
controller.markerIdentifierToController[markerIdentifier];
147+
GMSMarker *marker = markerController.marker;
148+
149+
XCTAssertTrue(marker.flat);
150+
}
151+
152+
// Boolean properties are tested individually to ensure they aren't accidentally cross-assigned from
153+
// another property.
154+
- (void)testSetsVisible {
155+
GMSMapView *mapView = [self mockedMapView];
156+
FLTMarkersController *controller = [self markersControllerWithMapView:mapView];
157+
158+
NSString *markerIdentifier = @"marker";
159+
[controller addMarkers:@[ [FGMPlatformMarker
160+
makeWithAlpha:1.0
161+
anchor:[FGMPlatformPoint makeWithX:0 y:0]
162+
consumeTapEvents:NO
163+
draggable:NO
164+
flat:NO
165+
icon:[self placeholderBitmap]
166+
infoWindow:[FGMPlatformInfoWindow
167+
makeWithTitle:@"info title"
168+
snippet:@"info snippet"
169+
anchor:[FGMPlatformPoint makeWithX:0 y:0]]
170+
position:[FGMPlatformLatLng makeWithLatitude:0.0 longitude:0.0]
171+
rotation:0
172+
visible:YES
173+
zIndex:0
174+
markerId:markerIdentifier
175+
clusterManagerId:nil] ]];
176+
177+
FLTGoogleMapMarkerController *markerController =
178+
controller.markerIdentifierToController[markerIdentifier];
179+
GMSMarker *marker = markerController.marker;
180+
181+
// Visibility is controlled by being set to a map.
182+
XCTAssertNotNil(marker.map);
183+
}
184+
185+
- (void)testSetsMarkerInfoWindowProperties {
186+
GMSMapView *mapView = [self mockedMapView];
187+
FLTMarkersController *controller = [self markersControllerWithMapView:mapView];
188+
189+
NSString *markerIdentifier = @"marker";
190+
NSString *title = @"info title";
191+
NSString *snippet = @"info snippet";
192+
double anchorX = 3.14;
193+
double anchorY = 2.718;
194+
[controller
195+
addMarkers:@[ [FGMPlatformMarker
196+
makeWithAlpha:1.0
197+
anchor:[FGMPlatformPoint makeWithX:0 y:0]
198+
consumeTapEvents:YES
199+
draggable:YES
200+
flat:YES
201+
icon:[self placeholderBitmap]
202+
infoWindow:[FGMPlatformInfoWindow
203+
makeWithTitle:title
204+
snippet:snippet
205+
anchor:[FGMPlatformPoint makeWithX:anchorX
206+
y:anchorY]]
207+
position:[FGMPlatformLatLng makeWithLatitude:0 longitude:0]
208+
rotation:0
209+
visible:YES
210+
zIndex:0
211+
markerId:markerIdentifier
212+
clusterManagerId:nil] ]];
213+
214+
FLTGoogleMapMarkerController *markerController =
215+
controller.markerIdentifierToController[markerIdentifier];
216+
GMSMarker *marker = markerController.marker;
217+
218+
const double delta = 0.0001;
219+
XCTAssertEqualWithAccuracy(marker.infoWindowAnchor.x, anchorX, delta);
220+
XCTAssertEqualWithAccuracy(marker.infoWindowAnchor.y, anchorY, delta);
221+
XCTAssertEqual(marker.title, title);
222+
XCTAssertEqual(marker.snippet, snippet);
223+
}
224+
225+
@end

packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
@interface FLTGoogleMapMarkerController ()
1212

13-
@property(strong, nonatomic) GMSMarker *marker;
13+
@property(strong, nonatomic, readwrite) GMSMarker *marker;
1414
@property(weak, nonatomic) GMSMapView *mapView;
1515
@property(assign, nonatomic, readwrite) BOOL consumeTapEvents;
1616
/// The unique identifier for the cluster manager.
@@ -119,30 +119,21 @@ - (void)updateFromPlatformMarker:(FGMPlatformMarker *)platformMarker
119119
[self setConsumeTapEvents:platformMarker.consumeTapEvents];
120120
[self setPosition:FGMGetCoordinateForPigeonLatLng(platformMarker.position)];
121121
[self setRotation:platformMarker.rotation];
122-
[self setVisible:platformMarker.visible];
123122
[self setZIndex:platformMarker.zIndex];
124-
}
125-
126-
- (void)interpretInfoWindow:(NSDictionary *)data {
127-
NSDictionary *infoWindow = FGMGetValueOrNilFromDict(data, @"infoWindow");
128-
if (infoWindow) {
129-
NSString *title = FGMGetValueOrNilFromDict(infoWindow, @"title");
130-
NSString *snippet = FGMGetValueOrNilFromDict(infoWindow, @"snippet");
131-
if (title) {
132-
[self setInfoWindowTitle:title snippet:snippet];
133-
}
134-
NSArray *infoWindowAnchor = infoWindow[@"infoWindowAnchor"];
135-
if (infoWindowAnchor) {
136-
[self setInfoWindowAnchor:[FLTGoogleMapJSONConversions pointFromArray:infoWindowAnchor]];
137-
}
123+
FGMPlatformInfoWindow *infoWindow = platformMarker.infoWindow;
124+
[self setInfoWindowAnchor:FGMGetCGPointForPigeonPoint(infoWindow.anchor)];
125+
if (infoWindow.title) {
126+
[self setInfoWindowTitle:infoWindow.title snippet:infoWindow.snippet];
138127
}
128+
129+
[self setVisible:platformMarker.visible];
139130
}
140131

141132
@end
142133

143134
@interface FLTMarkersController ()
144135

145-
@property(strong, nonatomic) NSMutableDictionary *markerIdentifierToController;
136+
@property(strong, nonatomic, readwrite) NSMutableDictionary *markerIdentifierToController;
146137
@property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler;
147138
/// Controller for adding/removing/fetching cluster managers
148139
@property(weak, nonatomic, nullable) FGMClusterManagersController *clusterManagersController;

packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController_Test.h

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,18 @@
44

55
#import "GoogleMapMarkerController.h"
66

7+
/// Methods exposed for unit testing.
78
@interface FLTGoogleMapMarkerController (Test)
89

9-
/// Extracts an icon image from the iconData array.
10-
///
11-
/// @param platformBitmap The Pigeon representation of the icon image.
12-
/// @param registrar A Flutter plugin registrar.
13-
/// @param screenScale Screen scale factor for scaling bitmaps. Must be greater than 0.
14-
/// @return A UIImage object created from the icon data.
15-
/// @note Assert unless screenScale is greater than 0.
16-
- (UIImage *)iconFromBitmap:(FGMPlatformBitmap *)platformBitmap
17-
registrar:(NSObject<FlutterPluginRegistrar> *)registrar
18-
screenScale:(CGFloat)screenScale;
10+
/// The underlying controlled GMSMarker.
11+
@property(strong, nonatomic, readonly) GMSMarker *marker;
1912

20-
/// Checks if an image can be scaled from an original size to a target size using a scale factor
21-
/// while maintaining the aspect ratio.
22-
///
23-
/// @param originalSize The original size of the image.
24-
/// @param targetSize The desired target size to scale the image to.
25-
/// @return A BOOL indicating whether the image can be scaled to the target size with scale
26-
/// factor.
27-
+ (BOOL)isScalableWithScaleFactorFromSize:(CGSize)originalSize toSize:(CGSize)targetSize;
13+
@end
14+
15+
/// Methods exposed for unit testing.
16+
@interface FLTMarkersController (Test)
17+
18+
/// A mapping from marker identifiers to corresponding marker controllers.
19+
@property(strong, nonatomic, readonly) NSMutableDictionary *markerIdentifierToController;
2820

2921
@end

packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: google_maps_flutter_ios
22
description: iOS implementation of the google_maps_flutter plugin.
33
repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22
5-
version: 2.15.0
5+
version: 2.15.1
66

77
environment:
88
sdk: ^3.4.0

0 commit comments

Comments
 (0)