From ba7bdc3a3741f50b97a979aebb10fc003c58a01f Mon Sep 17 00:00:00 2001 From: Ivan Persidsky Date: Tue, 16 Jan 2024 16:20:07 +0200 Subject: [PATCH] Fix attribution dialog presentation (#1982) MAPSIOS-515: Fix attribution dialog presentation --- .../Examples.xcodeproj/project.pbxproj | 56 +++++++++++-------- .../SwiftUI Examples/SwiftUIRoot.swift | 7 ++- .../AnnotationsOrderTestExample.swift | 0 .../AttributionDialogueExamples.swift} | 20 ++++++- .../MapSettingsExample.swift | 0 .../PuckPlayground.swift | 0 .../SimpleMapExample.swift | 0 .../ViewportPlayground.swift | 0 CHANGELOG.md | 1 + .../UIViewController+Extensions.swift | 11 ++++ .../Foundation/MapView+Attribution.swift | 4 +- .../Style/AttributionDialogManager.swift | 5 +- .../UIViewController+ExtensionsTests.swift | 28 ++++++++++ .../InfoButton/InfoButtonOrnamentTests.swift | 2 +- ...MockAttributionDialogManagerDelegate.swift | 2 +- 15 files changed, 102 insertions(+), 34 deletions(-) rename Apps/Examples/Examples/SwiftUI Examples/{ => Testing Examples}/AnnotationsOrderTestExample.swift (100%) rename Apps/Examples/Examples/SwiftUI Examples/{URLOpenExamples.swift => Testing Examples/AttributionDialogueExamples.swift} (50%) rename Apps/Examples/Examples/SwiftUI Examples/{ => Testing Examples}/MapSettingsExample.swift (100%) rename Apps/Examples/Examples/SwiftUI Examples/{ => Testing Examples}/PuckPlayground.swift (100%) rename Apps/Examples/Examples/SwiftUI Examples/{ => Testing Examples}/SimpleMapExample.swift (100%) rename Apps/Examples/Examples/SwiftUI Examples/{ => Testing Examples}/ViewportPlayground.swift (100%) create mode 100644 Sources/MapboxMaps/Foundation/Extensions/UIViewController+Extensions.swift create mode 100644 Tests/MapboxMapsTests/Foundation/Extensions/UIViewController+ExtensionsTests.swift diff --git a/Apps/Examples/Examples.xcodeproj/project.pbxproj b/Apps/Examples/Examples.xcodeproj/project.pbxproj index 5bbd8c15e50d..2394d14e4159 100644 --- a/Apps/Examples/Examples.xcodeproj/project.pbxproj +++ b/Apps/Examples/Examples.xcodeproj/project.pbxproj @@ -27,12 +27,10 @@ 1DAE02D73D16E543777C2025 /* ClusteringExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46CE3D9C2873C0767DD76D85 /* ClusteringExample.swift */; }; 1F860D5B445E75772C4C3B6C /* SkyLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93B8372871DB4BC991737A06 /* SkyLayerExample.swift */; }; 215230836B6AD1040D3DA547 /* CombineLocationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4EE8F38428A64B5B9D4DBBE /* CombineLocationExample.swift */; }; - 22092068DDBDE726A75668F2 /* MapSettingsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = EED24561F58ADAC99A7A0398 /* MapSettingsExample.swift */; }; 2997D21A7DB20098C6D03D3B /* StandardStyleImportExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640198169EEDFC7CBEFCFCCF /* StandardStyleImportExample.swift */; }; 2B44F3E8EF3A50D9AE6B825F /* route.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 450C8D5E4B84428FE51BCA97 /* route.geojson */; }; 2C03342240D5487880316518 /* AddOneMarkerSymbolExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DCBE814694CF08A9C2E4A42 /* AddOneMarkerSymbolExample.swift */; }; 30589E5AB307FC934E466332 /* radar2.gif in Resources */ = {isa = PBXBuildFile; fileRef = D8730F8FB259A4F889609108 /* radar2.gif */; }; - 3068148DE07779E2FB6B6B97 /* URLOpenExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D16FD65C8132C9379935D76 /* URLOpenExamples.swift */; }; 32FA2A4133B0464494212B34 /* Array+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BB15B17EDE597D37CFF3FCA /* Array+Split.swift */; }; 373BD1EE35B76E43534E23F6 /* Fingertips in Frameworks */ = {isa = PBXBuildFile; productRef = FD9311FF1C736B80A26F4258 /* Fingertips */; }; 38AD95B6DD9BE858F4E59C31 /* WeatherAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91D21963A6FF5DA26A210DA5 /* WeatherAnnotationExample.swift */; }; @@ -49,6 +47,7 @@ 49F6209402BF34C06C90107A /* HeatmapLayerGlobeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70922E748D003176C4A3C60A /* HeatmapLayerGlobeExample.swift */; }; 4ACB99FAFBF38A425EBD0285 /* ModelLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D98F58F756D035C98B1F39 /* ModelLayerExample.swift */; }; 4EF3E4C342C3F8ED5BF6C332 /* ViewAnnotationMarkerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1317C28ACDAC187017096A99 /* ViewAnnotationMarkerExample.swift */; }; + 50641F1F3A58B85873E2E5B8 /* AttributionDialogueExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A77AEDBF679F223D4412FEE /* AttributionDialogueExamples.swift */; }; 556C8423BA408C7FF54BB5DA /* AnimateLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A7965C46F2F371AA940A99 /* AnimateLayerExample.swift */; }; 560D4A0D2C704ECC346D8B5F /* fragment-realestate-NY.json in Resources */ = {isa = PBXBuildFile; fileRef = 50618B3CF42CCF735CCAE9B4 /* fragment-realestate-NY.json */; }; 56446E388868862E2BB80F4D /* ViewAnnotationWithPointAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB726A94B069761528B57EC8 /* ViewAnnotationWithPointAnnotationExample.swift */; }; @@ -56,6 +55,8 @@ 5A68CBA756780F7DE8F7BDCC /* radar3.gif in Resources */ = {isa = PBXBuildFile; fileRef = 24FD45C982E4403DA3BD241A /* radar3.gif */; }; 5A6D7B2A302A6555FE23FF80 /* blueprint_style.json in Resources */ = {isa = PBXBuildFile; fileRef = 989F5AB9D5D8AD39D21327A1 /* blueprint_style.json */; }; 5B2CE02503AF44EBC86FE884 /* MapboxMaps in Frameworks */ = {isa = PBXBuildFile; productRef = 0AF5F744C6369BF1FB233FB6 /* MapboxMaps */; }; + 5E508E47388A646B4F74DD0B /* AnnotationsOrderTestExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3595A6E8FB1FD9F41DEB5C6F /* AnnotationsOrderTestExample.swift */; }; + 5F537B052041931CB507E12B /* ViewportPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE863B179BF4F740C36D185E /* ViewportPlayground.swift */; }; 5FF3E34B523C39A404154BF7 /* OfflineRegionManagerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1229327C13654C370B5641FC /* OfflineRegionManagerExample.swift */; }; 60A1572CCF5763FA3C946B89 /* Custom2DPuckExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262AD875661BAB5564084A9E /* Custom2DPuckExample.swift */; }; 61B79A9069DCE6865E43E261 /* radar4.gif in Resources */ = {isa = PBXBuildFile; fileRef = 876CE24F4E565ED342DDDCD6 /* radar4.gif */; }; @@ -80,9 +81,8 @@ 85AA0D942D4C0E218D87F7D8 /* GradientLine.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 2DD8B1D25297B7433F4AAF35 /* GradientLine.geojson */; }; 85E0F727CBB3374D3EF499C3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 55FDF5B3329BFD6E6C346D80 /* LaunchScreen.storyboard */; }; 86AED5DD9F8C8BB2C9736483 /* ResizeMapViewExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E54D3F5943238258AA0A9BE /* ResizeMapViewExample.swift */; }; - 872C30A67CEE69B8501337F6 /* PuckPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7D53EC2D5B982E6FF123C5 /* PuckPlayground.swift */; }; + 8913C410B72BE0C40EE02BB9 /* MapSettingsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6E875420B5C674C8CCAB9B1 /* MapSettingsExample.swift */; }; 8B4085733CCABE3BE3D16F7E /* LayerPositionExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99610E884967673F7F28D2B2 /* LayerPositionExample.swift */; }; - 8ECBC2495FB60A9F1631F60F /* SimpleMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F8EC4B22A80D0BB29BCF4B /* SimpleMapExample.swift */; }; 8F0BEA796867B64E48A1B328 /* StandardStyleLocationsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F1212BB2453DBFECE12F2 /* StandardStyleLocationsExample.swift */; }; 902FD51EC410A1E8BD88941D /* NavigationSimulatorExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A090EE21C9A65BE2697868F /* NavigationSimulatorExample.swift */; }; 918F4BDCC25819DD68BC9518 /* LayerSlotExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D058C94675BB58AE74392829 /* LayerSlotExample.swift */; }; @@ -95,10 +95,10 @@ A972D3306BC53DEC9798C60D /* ExternalVectorSourceExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133E4EABC7540ED460F08B8F /* ExternalVectorSourceExample.swift */; }; AE51E276DCD8CF89AB339224 /* LongTapAnimationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DB76F486D80FED88678B04D /* LongTapAnimationExample.swift */; }; AE6E90DB7B6DA4580C2DAB59 /* FrameViewAnnotationsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC0F09FF1EF1A88BC1C6545 /* FrameViewAnnotationsExample.swift */; }; + B304BACFCD08802A740E8919 /* PuckPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0CC67084BA1191D0B179A94 /* PuckPlayground.swift */; }; B53EA441C54E2B680A7E99F0 /* BuildingExtrusionsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFF80A86D7DEF54E0A7256DF /* BuildingExtrusionsExample.swift */; }; B9B1EE72E6203358F2785916 /* IconSizeChangeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F479FB7FFD90FA95F400E /* IconSizeChangeExample.swift */; }; B9D4B9C3042383738AB5B667 /* DebugMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59EF8798357CF55109A4E56 /* DebugMapExample.swift */; }; - BA3AA1B8C7C642832149040B /* ViewportPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12DFA30FD813791A63DAB3C2 /* ViewportPlayground.swift */; }; BD99E89F050E7D93846147FF /* ViewAnnotationBasicExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C6CF222C8EE9AE69563E39 /* ViewAnnotationBasicExample.swift */; }; BDABAAC8727AF67A0DEE2020 /* AnimateGeoJSONLineExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7274E152F7FBB7894447F822 /* AnimateGeoJSONLineExample.swift */; }; C04160BF66055F7DE9315395 /* Lights3DExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60A8CD8A69D3429FCF8ACBDD /* Lights3DExample.swift */; }; @@ -106,7 +106,6 @@ C327DBA17D79D5DFBBE84BE0 /* ButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858990E6795D3162A941E82C /* ButtonStyle.swift */; }; C664365A373267B564EC84EE /* CombineExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DBF737C9C7FBF46BB5F7B78 /* CombineExample.swift */; }; C6E1E615C75960D1BD1755A9 /* annotations.json in Resources */ = {isa = PBXBuildFile; fileRef = B05B410135D0B466B73C0765 /* annotations.json */; }; - C8C9C25DE2DCA18682F01197 /* AnnotationsOrderTestExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB32EFC8C3E7EB7813AA5F6B /* AnnotationsOrderTestExample.swift */; }; C940835B030A20F0C5BC31AD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B31A932A62B6142FE20C39DF /* Assets.xcassets */; }; C953F022C91FCA59CFF06BE9 /* SymbolClusteringExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FD38B8D20C0695A03AE4E68 /* SymbolClusteringExample.swift */; }; CA2209956E93ECB18C4C9DEC /* CircleAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1658938A5F0908D151A9B9 /* CircleAnnotationExample.swift */; }; @@ -133,6 +132,7 @@ F48BF087BB56B0A44D8B16F3 /* PointAnnotationClusteringExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8359ECB5024BEF3722C3CA1 /* PointAnnotationClusteringExample.swift */; }; F5311222553DA118AC571D82 /* MultipleGeometriesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7A7586D05B960928AB17A0D /* MultipleGeometriesExample.swift */; }; F5E96E5798947CA56FD77CF9 /* Fire_Hydrants.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 02DA2CC04980F807255D646B /* Fire_Hydrants.geojson */; }; + F613749DCDDDDC6F041032A0 /* SimpleMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78811E5A3185D2D32495870A /* SimpleMapExample.swift */; }; F6E3EF9BE4F1D2F58DE1BED2 /* radar1.gif in Resources */ = {isa = PBXBuildFile; fileRef = 8BD8BADE1108B0D380D9BEF8 /* radar1.gif */; }; FDA4B57BE32D92BB57A5B7E6 /* FeaturesAtPointExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6239A5CDA61892902765B843 /* FeaturesAtPointExample.swift */; }; /* End PBXBuildFile section */ @@ -164,7 +164,6 @@ 10C7CEE3F343DF482D428211 /* ExampleProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleProtocol.swift; sourceTree = ""; }; 1229327C13654C370B5641FC /* OfflineRegionManagerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineRegionManagerExample.swift; sourceTree = ""; }; 12A0818B5BC601707E3235A9 /* ResizableImageExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizableImageExample.swift; sourceTree = ""; }; - 12DFA30FD813791A63DAB3C2 /* ViewportPlayground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewportPlayground.swift; sourceTree = ""; }; 1317C28ACDAC187017096A99 /* ViewAnnotationMarkerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewAnnotationMarkerExample.swift; sourceTree = ""; }; 133E4EABC7540ED460F08B8F /* ExternalVectorSourceExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalVectorSourceExample.swift; sourceTree = ""; }; 1A182413E535260335459F26 /* SpinningGlobeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpinningGlobeExample.swift; sourceTree = ""; }; @@ -184,11 +183,11 @@ 2F6B4718F05FB1E6736EA1FF /* CustomRasterSourceExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRasterSourceExample.swift; sourceTree = ""; }; 3333EF3E0F1C789809F385AF /* DynamicViewAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicViewAnnotationExample.swift; sourceTree = ""; }; 33E5647315A2357320BCF575 /* ViewAnnotationAnimationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewAnnotationAnimationExample.swift; sourceTree = ""; }; + 3595A6E8FB1FD9F41DEB5C6F /* AnnotationsOrderTestExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnotationsOrderTestExample.swift; sourceTree = ""; }; 370DFCA52EB6C7F119BF81DA /* MapEventsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapEventsExample.swift; sourceTree = ""; }; 384FD8FC97B9F5011AF4BD61 /* GlobeFlyToExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobeFlyToExample.swift; sourceTree = ""; }; 3BCB1CC4577300FEF4DE017B /* InsetMapExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsetMapExample.swift; sourceTree = ""; }; 3BDE7738CA55957F3FAC3ECE /* LineAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineAnnotationExample.swift; sourceTree = ""; }; - 3D16FD65C8132C9379935D76 /* URLOpenExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLOpenExamples.swift; sourceTree = ""; }; 3E2F68B22AFF73A71F86CABC /* ExamplesUITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = ExamplesUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3E3A0CCD53B02FF2BAD830A2 /* CameraAnimationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAnimationExample.swift; sourceTree = ""; }; 3F6F479FB7FFD90FA95F400E /* IconSizeChangeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconSizeChangeExample.swift; sourceTree = ""; }; @@ -197,12 +196,10 @@ 450C8D5E4B84428FE51BCA97 /* route.geojson */ = {isa = PBXFileReference; path = route.geojson; sourceTree = ""; }; 455C0B9F01316D0FF38ED62B /* CLLocationCoordinate2D+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CLLocationCoordinate2D+Random.swift"; sourceTree = ""; }; 46CE3D9C2873C0767DD76D85 /* ClusteringExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClusteringExample.swift; sourceTree = ""; }; - 4B7D53EC2D5B982E6FF123C5 /* PuckPlayground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PuckPlayground.swift; sourceTree = ""; }; 4D5DB9BD5E97D3C0080EC5D3 /* CustomPointAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPointAnnotationExample.swift; sourceTree = ""; }; 4DCBE814694CF08A9C2E4A42 /* AddOneMarkerSymbolExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOneMarkerSymbolExample.swift; sourceTree = ""; }; 4EE19F00E87B31FDE5481D56 /* ExamplesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesTests.swift; sourceTree = ""; }; 50618B3CF42CCF735CCAE9B4 /* fragment-realestate-NY.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "fragment-realestate-NY.json"; sourceTree = ""; }; - 51F8EC4B22A80D0BB29BCF4B /* SimpleMapExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleMapExample.swift; sourceTree = ""; }; 5554DBFA5DE4F0AAEA67785D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 588FD640D91E9DD366703F7B /* SnapshotterExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotterExample.swift; sourceTree = ""; }; 590D83805AA598D2B0A7638A /* ExamplesTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = ExamplesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -223,6 +220,8 @@ 70922E748D003176C4A3C60A /* HeatmapLayerGlobeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeatmapLayerGlobeExample.swift; sourceTree = ""; }; 7274E152F7FBB7894447F822 /* AnimateGeoJSONLineExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateGeoJSONLineExample.swift; sourceTree = ""; }; 75D03F5A3A0E879717BFE421 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 78811E5A3185D2D32495870A /* SimpleMapExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleMapExample.swift; sourceTree = ""; }; + 7A77AEDBF679F223D4412FEE /* AttributionDialogueExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributionDialogueExamples.swift; sourceTree = ""; }; 7DB76F486D80FED88678B04D /* LongTapAnimationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongTapAnimationExample.swift; sourceTree = ""; }; 7DCBE4524A8793B4DE950533 /* PolygonAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PolygonAnnotationExample.swift; sourceTree = ""; }; 7F5E598A16FA446F583344CB /* TrackingModeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingModeExample.swift; sourceTree = ""; }; @@ -260,6 +259,7 @@ B33F64CDBA98B91EE819B2C4 /* AddMarkersSymbolExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMarkersSymbolExample.swift; sourceTree = ""; }; B7A7586D05B960928AB17A0D /* MultipleGeometriesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipleGeometriesExample.swift; sourceTree = ""; }; BE18E37A8652B4807D2459F1 /* ExamplesUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesUITests.swift; sourceTree = ""; }; + C0CC67084BA1191D0B179A94 /* PuckPlayground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PuckPlayground.swift; sourceTree = ""; }; C47942F80A50166AC823012B /* sportcar.glb */ = {isa = PBXFileReference; path = sportcar.glb; sourceTree = ""; }; CB726A94B069761528B57EC8 /* ViewAnnotationWithPointAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewAnnotationWithPointAnnotationExample.swift; sourceTree = ""; }; CDABBED8502001D17EF30F35 /* CameraAnimatorsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAnimatorsExample.swift; sourceTree = ""; }; @@ -276,18 +276,18 @@ DC98E9169E8E7DFE8DC1CB27 /* BasicLocationPulsingExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicLocationPulsingExample.swift; sourceTree = ""; }; DD6F1212BB2453DBFECE12F2 /* StandardStyleLocationsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardStyleLocationsExample.swift; sourceTree = ""; }; DE6CEA9899CC0EC4F4381E19 /* CarPlayMapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarPlayMapViewController.swift; sourceTree = ""; }; + DE863B179BF4F740C36D185E /* ViewportPlayground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewportPlayground.swift; sourceTree = ""; }; E09DABCB8139643F1BD3B972 /* Examples_CarPlay.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Examples_CarPlay.entitlements; sourceTree = ""; }; E0C19E67A2E87A3D18B7B511 /* ViewportExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewportExample.swift; sourceTree = ""; }; E612275E3042D0D0AF8B583E /* MapRecorderExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRecorderExample.swift; sourceTree = ""; }; + E6E875420B5C674C8CCAB9B1 /* MapSettingsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapSettingsExample.swift; sourceTree = ""; }; EC1155178B21E2E8075454A8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - EED24561F58ADAC99A7A0398 /* MapSettingsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapSettingsExample.swift; sourceTree = ""; }; F000C4D3B6FC70FA9607E3A3 /* UIColor+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Random.swift"; sourceTree = ""; }; F033C8EFB89A90D6705B047D /* GeoJSONSourceExample.geojson */ = {isa = PBXFileReference; path = GeoJSONSourceExample.geojson; sourceTree = ""; }; F0A6063E57EC170F558A3F74 /* ExampleTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleTableViewController.swift; sourceTree = ""; }; F0CE51977FA6E83B6F11BE5C /* Examples.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = Examples.app; sourceTree = BUILT_PRODUCTS_DIR; }; F393DF039D7AD2F35C8DE4CE /* ViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtensions.swift; sourceTree = ""; }; F890746B56E20150A053B41B /* AnnotationsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnotationsExample.swift; sourceTree = ""; }; - FB32EFC8C3E7EB7813AA5F6B /* AnnotationsOrderTestExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnotationsOrderTestExample.swift; sourceTree = ""; }; FE2A263DD2E9DC52CEE356FA /* sf_airport_route.geojson */ = {isa = PBXFileReference; path = sf_airport_route.geojson; sourceTree = ""; }; /* End PBXFileReference section */ @@ -304,6 +304,19 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 2CDB0066C8683D83F2532B62 /* Testing Examples */ = { + isa = PBXGroup; + children = ( + 3595A6E8FB1FD9F41DEB5C6F /* AnnotationsOrderTestExample.swift */, + 7A77AEDBF679F223D4412FEE /* AttributionDialogueExamples.swift */, + E6E875420B5C674C8CCAB9B1 /* MapSettingsExample.swift */, + C0CC67084BA1191D0B179A94 /* PuckPlayground.swift */, + 78811E5A3185D2D32495870A /* SimpleMapExample.swift */, + DE863B179BF4F740C36D185E /* ViewportPlayground.swift */, + ); + path = "Testing Examples"; + sourceTree = ""; + }; 3FDBCEEB7C4C53F0545FC427 /* Products */ = { isa = PBXGroup; children = ( @@ -344,20 +357,15 @@ isa = PBXGroup; children = ( F890746B56E20150A053B41B /* AnnotationsExample.swift */, - FB32EFC8C3E7EB7813AA5F6B /* AnnotationsOrderTestExample.swift */, 46CE3D9C2873C0767DD76D85 /* ClusteringExample.swift */, A6B06A1D70F479D8DC5C375A /* FeaturesQueryExample.swift */, 62DA0608D44DEF6C4A82777C /* LocateMeExample.swift */, - EED24561F58ADAC99A7A0398 /* MapSettingsExample.swift */, - 4B7D53EC2D5B982E6FF123C5 /* PuckPlayground.swift */, - 51F8EC4B22A80D0BB29BCF4B /* SimpleMapExample.swift */, 640198169EEDFC7CBEFCFCCF /* StandardStyleImportExample.swift */, DD6F1212BB2453DBFECE12F2 /* StandardStyleLocationsExample.swift */, 90CEF3209781923E53974F20 /* SwiftUIRoot.swift */, - 3D16FD65C8132C9379935D76 /* URLOpenExamples.swift */, 59AACE9E33102AE90526569F /* ViewAnnotationsExample.swift */, - 12DFA30FD813791A63DAB3C2 /* ViewportPlayground.swift */, 91D21963A6FF5DA26A210DA5 /* WeatherAnnotationExample.swift */, + 2CDB0066C8683D83F2532B62 /* Testing Examples */, 708CF4C3DE36627B66561595 /* Util */, ); path = "SwiftUI Examples"; @@ -790,10 +798,11 @@ 392857DBD1231B0438144335 /* AnimatedMarkerExample.swift in Sources */, D27F0573360A7234BCF7AB6C /* AnnotationView.swift in Sources */, 7E84D4D6459049E452808C91 /* AnnotationsExample.swift in Sources */, - C8C9C25DE2DCA18682F01197 /* AnnotationsOrderTestExample.swift in Sources */, + 5E508E47388A646B4F74DD0B /* AnnotationsOrderTestExample.swift in Sources */, E5A3B926DD7E451F1E660547 /* AppDelegate.swift in Sources */, 79B889CF23A3C0A5EA7F6ADD /* ApplicationCarPlaySceneDelegage.swift in Sources */, 32FA2A4133B0464494212B34 /* Array+Split.swift in Sources */, + 50641F1F3A58B85873E2E5B8 /* AttributionDialogueExamples.swift in Sources */, A3D7C0836BFE6FEB40C3C15A /* BasicLocationPulsingExample.swift in Sources */, D4FFFAE49D4B805BDA014AAD /* BasicMapExample.swift in Sources */, B53EA441C54E2B680A7E99F0 /* BuildingExtrusionsExample.swift in Sources */, @@ -844,7 +853,7 @@ AE51E276DCD8CF89AB339224 /* LongTapAnimationExample.swift in Sources */, 03EEF25ABD58ADD9631AB509 /* MapEventsExample.swift in Sources */, 3FD83483E0AE57790504CB0C /* MapRecorderExample.swift in Sources */, - 22092068DDBDE726A75668F2 /* MapSettingsExample.swift in Sources */, + 8913C410B72BE0C40EE02BB9 /* MapSettingsExample.swift in Sources */, 4ACB99FAFBF38A425EBD0285 /* ModelLayerExample.swift in Sources */, F5311222553DA118AC571D82 /* MultipleGeometriesExample.swift in Sources */, D94672F30272E31087AB5DDD /* NavigationSimulator.swift in Sources */, @@ -855,13 +864,13 @@ F48BF087BB56B0A44D8B16F3 /* PointAnnotationClusteringExample.swift in Sources */, 4417BB8A356335BC8421A19B /* PointClusteringExample.swift in Sources */, 6661DB69D4980E24BCA18AB2 /* PolygonAnnotationExample.swift in Sources */, - 872C30A67CEE69B8501337F6 /* PuckPlayground.swift in Sources */, + B304BACFCD08802A740E8919 /* PuckPlayground.swift in Sources */, 0E191B29AE31584DCFDC3821 /* RasterColorExample.swift in Sources */, E8CEBC697D805204F129C4FB /* RasterTileSourceExample.swift in Sources */, 191391C51FC69A6D36EB67F0 /* ResizableImageExample.swift in Sources */, 86AED5DD9F8C8BB2C9736483 /* ResizeMapViewExample.swift in Sources */, 4791CACAC0846107E4B0955B /* SceneKitExample.swift in Sources */, - 8ECBC2495FB60A9F1631F60F /* SimpleMapExample.swift in Sources */, + F613749DCDDDDC6F041032A0 /* SimpleMapExample.swift in Sources */, 1F860D5B445E75772C4C3B6C /* SkyLayerExample.swift in Sources */, F2B385831A78B3EE16BFEA69 /* SnapshotterCoreGraphicsExample.swift in Sources */, 68FD9E1F4606B2729BA1E6DC /* SnapshotterExample.swift in Sources */, @@ -875,7 +884,6 @@ 7036A19FCD2CCE85BDDF4E00 /* TrackingModeExample.swift in Sources */, A6A68B4ED674A924ACBD8FA2 /* UIColor+Random.swift in Sources */, D9297596469F9B31C2350B43 /* UIViewController+Extensions.swift in Sources */, - 3068148DE07779E2FB6B6B97 /* URLOpenExamples.swift in Sources */, CF5C5513D659D4981706DDEC /* ViewAnnotationAnimationExample.swift in Sources */, BD99E89F050E7D93846147FF /* ViewAnnotationBasicExample.swift in Sources */, 4EF3E4C342C3F8ED5BF6C332 /* ViewAnnotationMarkerExample.swift in Sources */, @@ -883,7 +891,7 @@ E2617ACF1E2367C012A87CD1 /* ViewAnnotationsExample.swift in Sources */, 8418F1775D49F5A66489B988 /* ViewExtensions.swift in Sources */, 14799547EFD5C4757FBAD6E4 /* ViewportExample.swift in Sources */, - BA3AA1B8C7C642832149040B /* ViewportPlayground.swift in Sources */, + 5F537B052041931CB507E12B /* ViewportPlayground.swift in Sources */, C315E1C61D222296FE0244FC /* VoiceOverAccessibilityExample.swift in Sources */, 38AD95B6DD9BE858F4E59C31 /* WeatherAnnotationExample.swift in Sources */, ); diff --git a/Apps/Examples/Examples/SwiftUI Examples/SwiftUIRoot.swift b/Apps/Examples/Examples/SwiftUI Examples/SwiftUIRoot.swift index 033572862e2f..5ba4d53f9492 100644 --- a/Apps/Examples/Examples/SwiftUI Examples/SwiftUIRoot.swift +++ b/Apps/Examples/Examples/SwiftUI Examples/SwiftUIRoot.swift @@ -33,9 +33,12 @@ struct SwiftUIRoot: View { ExampleLink("Puck playground", note: "Display user location using puck.", destination: PuckPlayground()) ExampleLink("Annotation Order", destination: AnnotationsOrderTestExample()) ExampleLink("Simple Map", note: "Camera observing, automatic dark mode support.", destination: SimpleMapExample()) - ExampleLink("AttributionURL open (iOS 13+)", note: "Override attribution url opener.", destination: URLOpenIOS13()) + ExampleLink("Attribution url via callback", note: "Works on iOS 13+", destination: AttributionManualURLOpen()) if #available(iOS 15.0, *) { - ExampleLink("AttributionURL open (iOS 15+)", note: "Override attribution url opener.", destination: URLOpenIOS15()) + ExampleLink("Attribution url open via environment", note: "Works on iOS 15+", destination: AttributionEnvironmentURLOpen()) + } + if #available(iOS 16.5, *) { + ExampleLink("Attribution dialog with presented sheet", destination: AttributionDialogueWithSheet()) } } header: { Text("Testing Examples") } diff --git a/Apps/Examples/Examples/SwiftUI Examples/AnnotationsOrderTestExample.swift b/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/AnnotationsOrderTestExample.swift similarity index 100% rename from Apps/Examples/Examples/SwiftUI Examples/AnnotationsOrderTestExample.swift rename to Apps/Examples/Examples/SwiftUI Examples/Testing Examples/AnnotationsOrderTestExample.swift diff --git a/Apps/Examples/Examples/SwiftUI Examples/URLOpenExamples.swift b/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/AttributionDialogueExamples.swift similarity index 50% rename from Apps/Examples/Examples/SwiftUI Examples/URLOpenExamples.swift rename to Apps/Examples/Examples/SwiftUI Examples/Testing Examples/AttributionDialogueExamples.swift index 7d2e6e8863bc..3d94195df03a 100644 --- a/Apps/Examples/Examples/SwiftUI Examples/URLOpenExamples.swift +++ b/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/AttributionDialogueExamples.swift @@ -3,7 +3,7 @@ import SwiftUI @available(iOS 15, *) -struct URLOpenIOS15: View { +struct AttributionEnvironmentURLOpen: View { @State private var alert: String? var body: some View { Map() @@ -16,8 +16,24 @@ struct URLOpenIOS15: View { } } +@available(iOS 16.4, *) +struct AttributionDialogueWithSheet: View { + @State var sheet = true + var body: some View { + Map() + .additionalSafeAreaInsets(.bottom, 70) + .ignoresSafeArea() + .sheet(isPresented: $sheet) { + Text("Tap attribution info button") + .interactiveDismissDisabled() + .presentationBackgroundInteraction(.enabled(upThrough: .large)) + .presentationDetents([.height(80), .medium, .large]) + } + } +} + @available(iOS 13, *) -struct URLOpenIOS13: View { +struct AttributionManualURLOpen: View { @State private var alert: String? var body: some View { Map(urlOpener: { alert = $0.absoluteString}) {} diff --git a/Apps/Examples/Examples/SwiftUI Examples/MapSettingsExample.swift b/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/MapSettingsExample.swift similarity index 100% rename from Apps/Examples/Examples/SwiftUI Examples/MapSettingsExample.swift rename to Apps/Examples/Examples/SwiftUI Examples/Testing Examples/MapSettingsExample.swift diff --git a/Apps/Examples/Examples/SwiftUI Examples/PuckPlayground.swift b/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/PuckPlayground.swift similarity index 100% rename from Apps/Examples/Examples/SwiftUI Examples/PuckPlayground.swift rename to Apps/Examples/Examples/SwiftUI Examples/Testing Examples/PuckPlayground.swift diff --git a/Apps/Examples/Examples/SwiftUI Examples/SimpleMapExample.swift b/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/SimpleMapExample.swift similarity index 100% rename from Apps/Examples/Examples/SwiftUI Examples/SimpleMapExample.swift rename to Apps/Examples/Examples/SwiftUI Examples/Testing Examples/SimpleMapExample.swift diff --git a/Apps/Examples/Examples/SwiftUI Examples/ViewportPlayground.swift b/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/ViewportPlayground.swift similarity index 100% rename from Apps/Examples/Examples/SwiftUI Examples/ViewportPlayground.swift rename to Apps/Examples/Examples/SwiftUI Examples/Testing Examples/ViewportPlayground.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index bc77ffa8ceaa..a7ec3766c2fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Mapbox welcomes participation and contributions from everyone. * Fix glitch in chained camera animations. * Make padding optional in `MapboxMap.camera(for:padding:bearing:pitch:maxZoom:offset:)` and `MapboxMap.camera(for:padding:bearing:pitch:)`. * Build XCFramework with `SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO` flag to avoid serialized search paths in Swift modules. +* Fixed a bug where the attribution dialog does not appear when there is a presented view controller. ## 11.1.0-rc.1 - 04 January, 2024 diff --git a/Sources/MapboxMaps/Foundation/Extensions/UIViewController+Extensions.swift b/Sources/MapboxMaps/Foundation/Extensions/UIViewController+Extensions.swift new file mode 100644 index 000000000000..c04f584c9316 --- /dev/null +++ b/Sources/MapboxMaps/Foundation/Extensions/UIViewController+Extensions.swift @@ -0,0 +1,11 @@ +import UIKit + +extension UIViewController { + var topmostPresentedViewController: UIViewController { + var result = self + while let presented = result.presentedViewController { + result = presented + } + return result + } +} diff --git a/Sources/MapboxMaps/Foundation/MapView+Attribution.swift b/Sources/MapboxMaps/Foundation/MapView+Attribution.swift index 3f0a1e5d8f3c..3ab80a43e159 100644 --- a/Sources/MapboxMaps/Foundation/MapView+Attribution.swift +++ b/Sources/MapboxMaps/Foundation/MapView+Attribution.swift @@ -2,8 +2,8 @@ import UIKit extension MapView: AttributionDialogManagerDelegate { - func viewControllerForPresenting(_ attributionDialogManager: AttributionDialogManager) -> UIViewController { - return parentViewController! + func viewControllerForPresenting(_ attributionDialogManager: AttributionDialogManager) -> UIViewController? { + parentViewController?.topmostPresentedViewController } func attributionDialogManager(_ attributionDialogManager: AttributionDialogManager, didTriggerActionFor attribution: Attribution) { diff --git a/Sources/MapboxMaps/Style/AttributionDialogManager.swift b/Sources/MapboxMaps/Style/AttributionDialogManager.swift index 66b8bb5b1ff6..65b4d2141c33 100644 --- a/Sources/MapboxMaps/Style/AttributionDialogManager.swift +++ b/Sources/MapboxMaps/Style/AttributionDialogManager.swift @@ -4,7 +4,7 @@ internal protocol AttributionDataSource: AnyObject { } internal protocol AttributionDialogManagerDelegate: AnyObject { - func viewControllerForPresenting(_ attributionDialogManager: AttributionDialogManager) -> UIViewController + func viewControllerForPresenting(_ attributionDialogManager: AttributionDialogManager) -> UIViewController? func attributionDialogManager(_ attributionDialogManager: AttributionDialogManager, didTriggerActionFor attribution: Attribution) } @@ -121,7 +121,8 @@ extension AttributionDialogManager: InfoButtonOrnamentDelegate { private func showAttributionDialog(for attributions: [Attribution]) { guard let viewController = delegate?.viewControllerForPresenting(self) else { - fatalError("No view controller found") + Log.error(forMessage: "Failed to present an attribution dialogue: no presenting view controller found.") + return } let title = NSLocalizedString("SDK_NAME", diff --git a/Tests/MapboxMapsTests/Foundation/Extensions/UIViewController+ExtensionsTests.swift b/Tests/MapboxMapsTests/Foundation/Extensions/UIViewController+ExtensionsTests.swift new file mode 100644 index 000000000000..381c258bebc0 --- /dev/null +++ b/Tests/MapboxMapsTests/Foundation/Extensions/UIViewController+ExtensionsTests.swift @@ -0,0 +1,28 @@ +@testable import MapboxMaps +import XCTest + +final class UIViewControllerExtensionsTests: XCTestCase { + func testTopmostPresentedViewController() { + let vc1 = MockViewController() + let vc2 = MockViewController() + let vc3 = MockViewController() + + XCTAssertIdentical(vc1.topmostPresentedViewController, vc1) + + vc1.simulatePresent(vc2) + XCTAssertIdentical(vc1.topmostPresentedViewController, vc2) + + vc2.simulatePresent(vc3) + XCTAssertIdentical(vc1.topmostPresentedViewController, vc3) + } +} + +private class MockViewController: UIViewController { + private var _presentedViewControllerOverride: UIViewController? + override var presentedViewController: UIViewController? { + return _presentedViewControllerOverride + } + func simulatePresent(_ vc: UIViewController) { + _presentedViewControllerOverride = vc + } +} diff --git a/Tests/MapboxMapsTests/Ornaments/InfoButton/InfoButtonOrnamentTests.swift b/Tests/MapboxMapsTests/Ornaments/InfoButton/InfoButtonOrnamentTests.swift index a5408f88e5da..911fbc57f5d8 100644 --- a/Tests/MapboxMapsTests/Ornaments/InfoButton/InfoButtonOrnamentTests.swift +++ b/Tests/MapboxMapsTests/Ornaments/InfoButton/InfoButtonOrnamentTests.swift @@ -116,7 +116,7 @@ extension InfoButtonOrnamentTests: AttributionDataSource { } extension InfoButtonOrnamentTests: AttributionDialogManagerDelegate { - func viewControllerForPresenting(_ attributionDialogManager: AttributionDialogManager) -> UIViewController { + func viewControllerForPresenting(_ attributionDialogManager: AttributionDialogManager) -> UIViewController? { return parentViewController } diff --git a/Tests/MapboxMapsTests/Style/Mocks/MockAttributionDialogManagerDelegate.swift b/Tests/MapboxMapsTests/Style/Mocks/MockAttributionDialogManagerDelegate.swift index 99303b221a7b..2dd8a898dda5 100644 --- a/Tests/MapboxMapsTests/Style/Mocks/MockAttributionDialogManagerDelegate.swift +++ b/Tests/MapboxMapsTests/Style/Mocks/MockAttributionDialogManagerDelegate.swift @@ -5,7 +5,7 @@ import Foundation final class MockAttributionDialogManagerDelegate: AttributionDialogManagerDelegate { let viewControllerForPresentingStub = Stub(defaultReturnValue: UIViewController()) - func viewControllerForPresenting(_ attributionDialogManager: AttributionDialogManager) -> UIViewController { + func viewControllerForPresenting(_ attributionDialogManager: AttributionDialogManager) -> UIViewController? { viewControllerForPresentingStub.call(with: attributionDialogManager) }