Skip to content

Commit 90a5444

Browse files
[google_maps_flutter] Fix Obj-C type handling (flutter#7010)
Eliminates the remaining google_maps_flutter cases of the once-common-in-this-repo type abuse of getting a value from a method channel arguments dictionary that may either be type `T` or `NSNull`, but assigning directly to a variable of type `T`, and then later comparing that variable to `NSNull` with a cast to silence the (correct) warning from the compiler. Longer term this will be eliminated entirely when the Pigeon conversion is complete, but currently I expect that in the short term we will only do a "shallow" Pigeon conversion (see flutter/packages#6980 for an Android example), meaning this code will likely stay around for a while.
1 parent e50f450 commit 90a5444

11 files changed

+154
-123
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.8.1
2+
3+
* Improves Objective-C type handling.
4+
15
## 2.8.0
26

37
* Adds compatibility with SDK version 9.x for apps targetting iOS 15+.

packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,28 @@ @interface FLTGoogleMapJSONConversionsTests : XCTestCase
1616

1717
@implementation FLTGoogleMapJSONConversionsTests
1818

19+
- (void)testGetValueOrNilWithValue {
20+
NSString *key = @"key";
21+
NSString *value = @"value";
22+
NSDictionary<NSString *, id> *dict = @{key : value};
23+
24+
XCTAssertEqual(FGMGetValueOrNilFromDict(dict, key), value);
25+
}
26+
27+
- (void)testGetValueOrNilWithNoEntry {
28+
NSString *key = @"key";
29+
NSDictionary<NSString *, id> *dict = @{};
30+
31+
XCTAssertNil(FGMGetValueOrNilFromDict(dict, key));
32+
}
33+
34+
- (void)testGetValueOrNilWithNSNull {
35+
NSString *key = @"key";
36+
NSDictionary<NSString *, id> *dict = @{key : [NSNull null]};
37+
38+
XCTAssertNil(FGMGetValueOrNilFromDict(dict, key));
39+
}
40+
1941
- (void)testLocationFromLatLong {
2042
NSArray<NSNumber *> *latlong = @[ @1, @2 ];
2143
CLLocationCoordinate2D location = [FLTGoogleMapJSONConversions locationFromLatLong:latlong];

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
NS_ASSUME_NONNULL_BEGIN
99

10+
/// Returns dict[key], or nil if dict[key] is NSNull.
11+
extern id _Nullable FGMGetValueOrNilFromDict(NSDictionary *dict, NSString *key);
12+
1013
@interface FLTGoogleMapJSONConversions : NSObject
1114

1215
+ (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44

55
#import "FLTGoogleMapJSONConversions.h"
66

7+
/// Returns dict[key], or nil if dict[key] is NSNull.
8+
id FGMGetValueOrNilFromDict(NSDictionary *dict, NSString *key) {
9+
id value = dict[key];
10+
return value == [NSNull null] ? nil : value;
11+
}
12+
713
@implementation FLTGoogleMapJSONConversions
814

915
+ (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong {

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,28 +70,28 @@ - (void)interpretTileOverlayOptions:(NSDictionary *)data {
7070
if (!data) {
7171
return;
7272
}
73-
NSNumber *visible = data[@"visible"];
74-
if (visible != nil && visible != (id)[NSNull null]) {
73+
NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible");
74+
if (visible) {
7575
[self setVisible:visible.boolValue];
7676
}
7777

78-
NSNumber *transparency = data[@"transparency"];
79-
if (transparency != nil && transparency != (id)[NSNull null]) {
78+
NSNumber *transparency = FGMGetValueOrNilFromDict(data, @"transparency");
79+
if (transparency) {
8080
[self setTransparency:transparency.floatValue];
8181
}
8282

83-
NSNumber *zIndex = data[@"zIndex"];
84-
if (zIndex != nil && zIndex != (id)[NSNull null]) {
83+
NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex");
84+
if (zIndex) {
8585
[self setZIndex:zIndex.intValue];
8686
}
8787

88-
NSNumber *fadeIn = data[@"fadeIn"];
89-
if (fadeIn != nil && fadeIn != (id)[NSNull null]) {
88+
NSNumber *fadeIn = FGMGetValueOrNilFromDict(data, @"fadeIn");
89+
if (fadeIn) {
9090
[self setFadeIn:fadeIn.boolValue];
9191
}
9292

93-
NSNumber *tileSize = data[@"tileSize"];
94-
if (tileSize != nil && tileSize != (id)[NSNull null]) {
93+
NSNumber *tileSize = FGMGetValueOrNilFromDict(data, @"tileSize");
94+
if (tileSize) {
9595
[self setTileSize:tileSize.integerValue];
9696
}
9797
}

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,43 +60,43 @@ - (void)setFillColor:(UIColor *)color {
6060
}
6161

6262
- (void)interpretCircleOptions:(NSDictionary *)data {
63-
NSNumber *consumeTapEvents = data[@"consumeTapEvents"];
64-
if (consumeTapEvents && consumeTapEvents != (id)[NSNull null]) {
63+
NSNumber *consumeTapEvents = FGMGetValueOrNilFromDict(data, @"consumeTapEvents");
64+
if (consumeTapEvents) {
6565
[self setConsumeTapEvents:consumeTapEvents.boolValue];
6666
}
6767

68-
NSNumber *visible = data[@"visible"];
69-
if (visible && visible != (id)[NSNull null]) {
68+
NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible");
69+
if (visible) {
7070
[self setVisible:[visible boolValue]];
7171
}
7272

73-
NSNumber *zIndex = data[@"zIndex"];
74-
if (zIndex && zIndex != (id)[NSNull null]) {
73+
NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex");
74+
if (zIndex) {
7575
[self setZIndex:[zIndex intValue]];
7676
}
7777

78-
NSArray *center = data[@"center"];
79-
if (center && center != (id)[NSNull null]) {
78+
NSArray *center = FGMGetValueOrNilFromDict(data, @"center");
79+
if (center) {
8080
[self setCenter:[FLTGoogleMapJSONConversions locationFromLatLong:center]];
8181
}
8282

83-
NSNumber *radius = data[@"radius"];
84-
if (radius && radius != (id)[NSNull null]) {
83+
NSNumber *radius = FGMGetValueOrNilFromDict(data, @"radius");
84+
if (radius) {
8585
[self setRadius:[radius floatValue]];
8686
}
8787

88-
NSNumber *strokeColor = data[@"strokeColor"];
89-
if (strokeColor && strokeColor != (id)[NSNull null]) {
88+
NSNumber *strokeColor = FGMGetValueOrNilFromDict(data, @"strokeColor");
89+
if (strokeColor) {
9090
[self setStrokeColor:[FLTGoogleMapJSONConversions colorFromRGBA:strokeColor]];
9191
}
9292

93-
NSNumber *strokeWidth = data[@"strokeWidth"];
94-
if (strokeWidth && strokeWidth != (id)[NSNull null]) {
93+
NSNumber *strokeWidth = FGMGetValueOrNilFromDict(data, @"strokeWidth");
94+
if (strokeWidth) {
9595
[self setStrokeWidth:[strokeWidth intValue]];
9696
}
9797

98-
NSNumber *fillColor = data[@"fillColor"];
99-
if (fillColor && fillColor != (id)[NSNull null]) {
98+
NSNumber *fillColor = FGMGetValueOrNilFromDict(data, @"fillColor");
99+
if (fillColor) {
100100
[self setFillColor:[FLTGoogleMapJSONConversions colorFromRGBA:fillColor]];
101101
}
102102
}

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

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -581,41 +581,41 @@ - (void)mapView:(GMSMapView *)mapView didLongPressAtCoordinate:(CLLocationCoordi
581581
}
582582

583583
- (void)interpretMapOptions:(NSDictionary *)data {
584-
NSArray *cameraTargetBounds = data[@"cameraTargetBounds"];
585-
if (cameraTargetBounds && cameraTargetBounds != (id)[NSNull null]) {
584+
NSArray *cameraTargetBounds = FGMGetValueOrNilFromDict(data, @"cameraTargetBounds");
585+
if (cameraTargetBounds) {
586586
[self
587587
setCameraTargetBounds:cameraTargetBounds.count > 0 && cameraTargetBounds[0] != [NSNull null]
588588
? [FLTGoogleMapJSONConversions
589589
coordinateBoundsFromLatLongs:cameraTargetBounds.firstObject]
590590
: nil];
591591
}
592-
NSNumber *compassEnabled = data[@"compassEnabled"];
593-
if (compassEnabled && compassEnabled != (id)[NSNull null]) {
592+
NSNumber *compassEnabled = FGMGetValueOrNilFromDict(data, @"compassEnabled");
593+
if (compassEnabled) {
594594
[self setCompassEnabled:[compassEnabled boolValue]];
595595
}
596-
id indoorEnabled = data[@"indoorEnabled"];
597-
if (indoorEnabled && indoorEnabled != [NSNull null]) {
596+
id indoorEnabled = FGMGetValueOrNilFromDict(data, @"indoorEnabled");
597+
if (indoorEnabled) {
598598
[self setIndoorEnabled:[indoorEnabled boolValue]];
599599
}
600-
id trafficEnabled = data[@"trafficEnabled"];
601-
if (trafficEnabled && trafficEnabled != [NSNull null]) {
600+
id trafficEnabled = FGMGetValueOrNilFromDict(data, @"trafficEnabled");
601+
if (trafficEnabled) {
602602
[self setTrafficEnabled:[trafficEnabled boolValue]];
603603
}
604-
id buildingsEnabled = data[@"buildingsEnabled"];
605-
if (buildingsEnabled && buildingsEnabled != [NSNull null]) {
604+
id buildingsEnabled = FGMGetValueOrNilFromDict(data, @"buildingsEnabled");
605+
if (buildingsEnabled) {
606606
[self setBuildingsEnabled:[buildingsEnabled boolValue]];
607607
}
608-
id mapType = data[@"mapType"];
609-
if (mapType && mapType != [NSNull null]) {
608+
id mapType = FGMGetValueOrNilFromDict(data, @"mapType");
609+
if (mapType) {
610610
[self setMapType:[FLTGoogleMapJSONConversions mapViewTypeFromTypeValue:mapType]];
611611
}
612-
NSArray *zoomData = data[@"minMaxZoomPreference"];
613-
if (zoomData && zoomData != (id)[NSNull null]) {
612+
NSArray *zoomData = FGMGetValueOrNilFromDict(data, @"minMaxZoomPreference");
613+
if (zoomData) {
614614
float minZoom = (zoomData[0] == [NSNull null]) ? kGMSMinZoomLevel : [zoomData[0] floatValue];
615615
float maxZoom = (zoomData[1] == [NSNull null]) ? kGMSMaxZoomLevel : [zoomData[1] floatValue];
616616
[self setMinZoom:minZoom maxZoom:maxZoom];
617617
}
618-
NSArray *paddingData = data[@"padding"];
618+
NSArray *paddingData = FGMGetValueOrNilFromDict(data, @"padding");
619619
if (paddingData) {
620620
float top = (paddingData[0] == [NSNull null]) ? 0 : [paddingData[0] floatValue];
621621
float left = (paddingData[1] == [NSNull null]) ? 0 : [paddingData[1] floatValue];
@@ -624,35 +624,35 @@ - (void)interpretMapOptions:(NSDictionary *)data {
624624
[self setPaddingTop:top left:left bottom:bottom right:right];
625625
}
626626

627-
NSNumber *rotateGesturesEnabled = data[@"rotateGesturesEnabled"];
628-
if (rotateGesturesEnabled && rotateGesturesEnabled != (id)[NSNull null]) {
627+
NSNumber *rotateGesturesEnabled = FGMGetValueOrNilFromDict(data, @"rotateGesturesEnabled");
628+
if (rotateGesturesEnabled) {
629629
[self setRotateGesturesEnabled:[rotateGesturesEnabled boolValue]];
630630
}
631-
NSNumber *scrollGesturesEnabled = data[@"scrollGesturesEnabled"];
632-
if (scrollGesturesEnabled && scrollGesturesEnabled != (id)[NSNull null]) {
631+
NSNumber *scrollGesturesEnabled = FGMGetValueOrNilFromDict(data, @"scrollGesturesEnabled");
632+
if (scrollGesturesEnabled) {
633633
[self setScrollGesturesEnabled:[scrollGesturesEnabled boolValue]];
634634
}
635-
NSNumber *tiltGesturesEnabled = data[@"tiltGesturesEnabled"];
636-
if (tiltGesturesEnabled && tiltGesturesEnabled != (id)[NSNull null]) {
635+
NSNumber *tiltGesturesEnabled = FGMGetValueOrNilFromDict(data, @"tiltGesturesEnabled");
636+
if (tiltGesturesEnabled) {
637637
[self setTiltGesturesEnabled:[tiltGesturesEnabled boolValue]];
638638
}
639-
NSNumber *trackCameraPosition = data[@"trackCameraPosition"];
640-
if (trackCameraPosition && trackCameraPosition != (id)[NSNull null]) {
639+
NSNumber *trackCameraPosition = FGMGetValueOrNilFromDict(data, @"trackCameraPosition");
640+
if (trackCameraPosition) {
641641
[self setTrackCameraPosition:[trackCameraPosition boolValue]];
642642
}
643-
NSNumber *zoomGesturesEnabled = data[@"zoomGesturesEnabled"];
644-
if (zoomGesturesEnabled && zoomGesturesEnabled != (id)[NSNull null]) {
643+
NSNumber *zoomGesturesEnabled = FGMGetValueOrNilFromDict(data, @"zoomGesturesEnabled");
644+
if (zoomGesturesEnabled) {
645645
[self setZoomGesturesEnabled:[zoomGesturesEnabled boolValue]];
646646
}
647-
NSNumber *myLocationEnabled = data[@"myLocationEnabled"];
648-
if (myLocationEnabled && myLocationEnabled != (id)[NSNull null]) {
647+
NSNumber *myLocationEnabled = FGMGetValueOrNilFromDict(data, @"myLocationEnabled");
648+
if (myLocationEnabled) {
649649
[self setMyLocationEnabled:[myLocationEnabled boolValue]];
650650
}
651-
NSNumber *myLocationButtonEnabled = data[@"myLocationButtonEnabled"];
652-
if (myLocationButtonEnabled && myLocationButtonEnabled != (id)[NSNull null]) {
651+
NSNumber *myLocationButtonEnabled = FGMGetValueOrNilFromDict(data, @"myLocationButtonEnabled");
652+
if (myLocationButtonEnabled) {
653653
[self setMyLocationButtonEnabled:[myLocationButtonEnabled boolValue]];
654654
}
655-
NSString *style = data[@"style"];
655+
NSString *style = FGMGetValueOrNilFromDict(data, @"style");
656656
if (style) {
657657
self.styleError = [self setMapStyle:style];
658658
}

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

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -93,60 +93,60 @@ - (void)setZIndex:(int)zIndex {
9393
- (void)interpretMarkerOptions:(NSDictionary *)data
9494
registrar:(NSObject<FlutterPluginRegistrar> *)registrar
9595
screenScale:(CGFloat)screenScale {
96-
NSNumber *alpha = data[@"alpha"];
97-
if (alpha && alpha != (id)[NSNull null]) {
96+
NSNumber *alpha = FGMGetValueOrNilFromDict(data, @"alpha");
97+
if (alpha) {
9898
[self setAlpha:[alpha floatValue]];
9999
}
100-
NSArray *anchor = data[@"anchor"];
101-
if (anchor && anchor != (id)[NSNull null]) {
100+
NSArray *anchor = FGMGetValueOrNilFromDict(data, @"anchor");
101+
if (anchor) {
102102
[self setAnchor:[FLTGoogleMapJSONConversions pointFromArray:anchor]];
103103
}
104-
NSNumber *draggable = data[@"draggable"];
105-
if (draggable && draggable != (id)[NSNull null]) {
104+
NSNumber *draggable = FGMGetValueOrNilFromDict(data, @"draggable");
105+
if (draggable) {
106106
[self setDraggable:[draggable boolValue]];
107107
}
108-
NSArray *icon = data[@"icon"];
109-
if (icon && icon != (id)[NSNull null]) {
108+
NSArray *icon = FGMGetValueOrNilFromDict(data, @"icon");
109+
if (icon) {
110110
UIImage *image = [self extractIconFromData:icon registrar:registrar screenScale:screenScale];
111111
[self setIcon:image];
112112
}
113-
NSNumber *flat = data[@"flat"];
114-
if (flat && flat != (id)[NSNull null]) {
113+
NSNumber *flat = FGMGetValueOrNilFromDict(data, @"flat");
114+
if (flat) {
115115
[self setFlat:[flat boolValue]];
116116
}
117-
NSNumber *consumeTapEvents = data[@"consumeTapEvents"];
118-
if (consumeTapEvents && consumeTapEvents != (id)[NSNull null]) {
117+
NSNumber *consumeTapEvents = FGMGetValueOrNilFromDict(data, @"consumeTapEvents");
118+
if (consumeTapEvents) {
119119
[self setConsumeTapEvents:[consumeTapEvents boolValue]];
120120
}
121121
[self interpretInfoWindow:data];
122-
NSArray *position = data[@"position"];
123-
if (position && position != (id)[NSNull null]) {
122+
NSArray *position = FGMGetValueOrNilFromDict(data, @"position");
123+
if (position) {
124124
[self setPosition:[FLTGoogleMapJSONConversions locationFromLatLong:position]];
125125
}
126-
NSNumber *rotation = data[@"rotation"];
127-
if (rotation && rotation != (id)[NSNull null]) {
126+
NSNumber *rotation = FGMGetValueOrNilFromDict(data, @"rotation");
127+
if (rotation) {
128128
[self setRotation:[rotation doubleValue]];
129129
}
130-
NSNumber *visible = data[@"visible"];
131-
if (visible && visible != (id)[NSNull null]) {
130+
NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible");
131+
if (visible) {
132132
[self setVisible:[visible boolValue]];
133133
}
134-
NSNumber *zIndex = data[@"zIndex"];
135-
if (zIndex && zIndex != (id)[NSNull null]) {
134+
NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex");
135+
if (zIndex) {
136136
[self setZIndex:[zIndex intValue]];
137137
}
138138
}
139139

140140
- (void)interpretInfoWindow:(NSDictionary *)data {
141-
NSDictionary *infoWindow = data[@"infoWindow"];
142-
if (infoWindow && infoWindow != (id)[NSNull null]) {
143-
NSString *title = infoWindow[@"title"];
144-
NSString *snippet = infoWindow[@"snippet"];
145-
if (title && title != (id)[NSNull null]) {
141+
NSDictionary *infoWindow = FGMGetValueOrNilFromDict(data, @"infoWindow");
142+
if (infoWindow) {
143+
NSString *title = FGMGetValueOrNilFromDict(infoWindow, @"title");
144+
NSString *snippet = FGMGetValueOrNilFromDict(infoWindow, @"snippet");
145+
if (title) {
146146
[self setInfoWindowTitle:title snippet:snippet];
147147
}
148148
NSArray *infoWindowAnchor = infoWindow[@"infoWindowAnchor"];
149-
if (infoWindowAnchor && infoWindowAnchor != (id)[NSNull null]) {
149+
if (infoWindowAnchor) {
150150
[self setInfoWindowAnchor:[FLTGoogleMapJSONConversions pointFromArray:infoWindowAnchor]];
151151
}
152152
}
@@ -221,15 +221,16 @@ - (UIImage *)extractIconFromData:(NSArray *)iconData
221221
@throw exception;
222222
}
223223

224-
NSString *assetName = assetData[@"assetName"];
225-
NSString *scalingMode = assetData[@"bitmapScaling"];
224+
NSString *assetName = FGMGetValueOrNilFromDict(assetData, @"assetName");
225+
NSString *scalingMode = FGMGetValueOrNilFromDict(assetData, @"bitmapScaling");
226226

227227
image = [UIImage imageNamed:[registrar lookupKeyForAsset:assetName]];
228228

229229
if ([scalingMode isEqualToString:@"auto"]) {
230-
NSNumber *width = assetData[@"width"];
231-
NSNumber *height = assetData[@"height"];
232-
CGFloat imagePixelRatio = [assetData[@"imagePixelRatio"] doubleValue];
230+
NSNumber *width = FGMGetValueOrNilFromDict(assetData, @"width");
231+
NSNumber *height = FGMGetValueOrNilFromDict(assetData, @"height");
232+
CGFloat imagePixelRatio =
233+
[FGMGetValueOrNilFromDict(assetData, @"imagePixelRatio") doubleValue];
233234

234235
if (width || height) {
235236
image = [FLTGoogleMapMarkerController scaledImage:image withScale:screenScale];
@@ -252,15 +253,16 @@ - (UIImage *)extractIconFromData:(NSArray *)iconData
252253
@throw exception;
253254
}
254255

255-
FlutterStandardTypedData *bytes = byteData[@"byteData"];
256-
NSString *scalingMode = byteData[@"bitmapScaling"];
256+
FlutterStandardTypedData *bytes = FGMGetValueOrNilFromDict(byteData, @"byteData");
257+
NSString *scalingMode = FGMGetValueOrNilFromDict(byteData, @"bitmapScaling");
257258

258259
@try {
259260
image = [UIImage imageWithData:[bytes data] scale:screenScale];
260261
if ([scalingMode isEqualToString:@"auto"]) {
261-
NSNumber *width = byteData[@"width"];
262-
NSNumber *height = byteData[@"height"];
263-
CGFloat imagePixelRatio = [byteData[@"imagePixelRatio"] doubleValue];
262+
NSNumber *width = FGMGetValueOrNilFromDict(byteData, @"width");
263+
NSNumber *height = FGMGetValueOrNilFromDict(byteData, @"height");
264+
CGFloat imagePixelRatio =
265+
[FGMGetValueOrNilFromDict(byteData, @"imagePixelRatio") doubleValue];
264266

265267
if (width || height) {
266268
// Before scaling the image, image must be in screenScale

0 commit comments

Comments
 (0)