From 7040cd41c15abb9434d538d42cd8242bf90ab659 Mon Sep 17 00:00:00 2001 From: Flescio Date: Thu, 15 Jun 2023 16:40:42 +0200 Subject: [PATCH] Add reverse geocoding service (#1085) * add Geocoding service * move url session extension --- ElementX.xcodeproj/project.pbxproj | 40 +++++++---- .../Sources/Application/AppSettings.swift | 3 + .../{MapStyleBuilder.swift => MapTiler.swift} | 6 ++ .../Sources/Other/Extensions/URLSession.swift | 28 ++++++++ .../MapLibre/MapTilerAuthorization.swift | 2 +- .../MapTilerGeoCodingServiceProtocol.swift | 27 +++++++ .../Other/MapLibre/MapTilerGeocoding.swift | 72 +++++++++++++++++++ .../Other/MapLibre/MapTilerStyleBuilder.swift | 6 +- .../MapTilerStyleBuilderProtocol.swift | 21 ++++++ .../Services/BugReport/BugReportService.swift | 11 --- ElementX/SupportingFiles/Info.plist | 2 + changelog.d/1085.feature | 1 + 12 files changed, 190 insertions(+), 29 deletions(-) rename ElementX/Sources/Other/Extensions/{MapStyleBuilder.swift => MapTiler.swift} (77%) create mode 100644 ElementX/Sources/Other/Extensions/URLSession.swift create mode 100644 ElementX/Sources/Other/MapLibre/MapTilerGeoCodingServiceProtocol.swift create mode 100644 ElementX/Sources/Other/MapLibre/MapTilerGeocoding.swift create mode 100644 ElementX/Sources/Other/MapLibre/MapTilerStyleBuilderProtocol.swift create mode 100644 changelog.d/1085.feature diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index b2cdcbb21..68b6a8900 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -49,6 +49,7 @@ 12CCA59536EDD99A3272CF77 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC3F82523D6F48B926D6AF68 /* AppSettings.swift */; }; 13853973A5E24374FCEDE8A3 /* RedactedRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */; }; 13C77FDF17C4C6627CFFC205 /* RoomTimelineItemFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D25A35764C7B3DB78954AB5 /* RoomTimelineItemFactoryProtocol.swift */; }; + 14343C2F9AD2BFEA92CA28FF /* MapTilerStyleBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7AE92E7BFF71797BDE1D261 /* MapTilerStyleBuilder.swift */; }; 149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */; }; 14E99D27628B1A6F0CB46FEA /* SeparatorRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9F49B3EE59147AF2F70BB /* SeparatorRoomTimelineItem.swift */; }; 152AE2B8650FB23AFD2E28B9 /* MockAuthenticationServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65C2B80DD0BF6F10BB5FA922 /* MockAuthenticationServiceProxy.swift */; }; @@ -183,6 +184,7 @@ 4A618590DEB72C4F186BFED4 /* UserSessionFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C99FDEEB71173C4C6FA2734C /* UserSessionFlowCoordinator.swift */; }; 4A85928E27D4C1A548A06EE9 /* StartChatScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 052B2F924572AFD70B5F500E /* StartChatScreenViewModel.swift */; }; 4AAA8606FBA290E23D15422E /* AvatarHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC743C7A85E3171BCBF0A653 /* AvatarHeaderView.swift */; }; + 4B16ABA1CCC957C7E919BDCE /* MapTiler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08ADEE79933CF1F3E6626920 /* MapTiler.swift */; }; 4B978C09567387EF4366BD7A /* MediaLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EF1AC723C2609C7705569CA /* MediaLoaderTests.swift */; }; 4BB282209EA82015D0DF8F89 /* NavigationStackCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C698E30698EC59302A8EEBD /* NavigationStackCoordinatorTests.swift */; }; 4C5A638DAA8AF64565BA4866 /* EncryptedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5351EBD7A0B9610548E4B7B2 /* EncryptedRoomTimelineItem.swift */; }; @@ -550,12 +552,13 @@ CD0088B763CD970CF1CBF8CB /* DateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5E97E9615A158C76B2AB77 /* DateTests.swift */; }; CD6A72B65D3B6076F4045C30 /* PHGPostHogConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */; }; CE1694C7BB93C3311524EF28 /* Untranslated.strings in Resources */ = {isa = PBXBuildFile; fileRef = D2F7194F440375338F8E2487 /* Untranslated.strings */; }; - CE6383512A38A4BE00F82EBA /* MapTilerAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6383502A38A4BE00F82EBA /* MapTilerAuthorization.swift */; }; - CE6383532A38A4DA00F82EBA /* MapTilerStyleBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6383522A38A4DA00F82EBA /* MapTilerStyleBuilder.swift */; }; - CE6383552A38A52000F82EBA /* MapStyleBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6383542A38A52000F82EBA /* MapStyleBuilder.swift */; }; CE7148E80F09B7305E026AC6 /* OnboardingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1198B925F4A88DA74083662 /* OnboardingViewModel.swift */; }; + CE9152222A3B4B7000EDC949 /* MapTilerGeoCodingServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9152212A3B4B7000EDC949 /* MapTilerGeoCodingServiceProtocol.swift */; }; + CE9152242A3B4B9200EDC949 /* MapTilerStyleBuilderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9152232A3B4B9200EDC949 /* MapTilerStyleBuilderProtocol.swift */; }; CE9530A4CA661E090635C2F2 /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */; }; + CEB59FDC2A39D1A000E6B2DF /* MapTilerGeocoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB59FDB2A39D1A000E6B2DF /* MapTilerGeocoding.swift */; }; CEB8FB1269DE20536608B957 /* LoginMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B41FABA2B0AEF4389986495 /* LoginMode.swift */; }; + CED722AD2A3B0E7B000F10C8 /* URLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED722AC2A3B0E7B000F10C8 /* URLSession.swift */; }; CF4044A8EED5C41BC0ED6ABE /* SoftLogoutScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D316BB02636AF2174F2580E6 /* SoftLogoutScreenViewModelProtocol.swift */; }; CF82143AA4A4F7BD11D22946 /* RoomTimelineViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB6C5E4950B6C9842F35A38 /* RoomTimelineViewProvider.swift */; }; D02AA6208C7ACB9BE6332394 /* UNNotificationContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE148A4FFEE853C5A281500C /* UNNotificationContent.swift */; }; @@ -670,6 +673,7 @@ FBF09B6C900415800DDF2A21 /* EmojiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C113E0CB7E15E9765B1817A /* EmojiProvider.swift */; }; FC10228E73323BDC09526F97 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 9573B94B1C86C6DF751AF3FD /* SwiftState */; }; FCD3F2B82CAB29A07887A127 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; }; + FCDA202B246F75BA28E10C5F /* MapTilerAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = E062C1750EFC8627DE4CAB8E /* MapTilerAuthorization.swift */; }; FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */; }; FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF1593DD87F974F8509BB619 /* ElementAnimations.swift */; }; FF149F0A3550A54C50ECBE7A /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C843CF833BF6485B64AC87E1 /* AppRouter.swift */; }; @@ -747,6 +751,7 @@ 07E65E613F057697A1A0BC03 /* NotificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewController.swift; sourceTree = ""; }; 086B997409328F091EBA43CE /* RoomScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenUITests.swift; sourceTree = ""; }; 086C19086DD16E9B38E25954 /* ReportContentViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentViewModelTests.swift; sourceTree = ""; }; + 08ADEE79933CF1F3E6626920 /* MapTiler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTiler.swift; sourceTree = ""; }; 095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderProtocol.swift; sourceTree = ""; }; 0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementToggleStyle.swift; sourceTree = ""; }; 099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiProviderTests.swift; sourceTree = ""; }; @@ -1156,6 +1161,7 @@ B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = ConfettiScene.scn; sourceTree = ""; }; B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineView.swift; sourceTree = ""; }; B6E89E530A8E92EC44301CA1 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; + B7AE92E7BFF71797BDE1D261 /* MapTilerStyleBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStyleBuilder.swift; sourceTree = ""; }; B7F0192CE2F891141A25B49F /* UITestsSignalling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsSignalling.swift; sourceTree = ""; }; B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenCoordinator.swift; sourceTree = ""; }; B81B6170DB690013CEB646F4 /* MapLibreModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLibreModels.swift; sourceTree = ""; }; @@ -1220,9 +1226,10 @@ CD469F7513574341181F7EAA /* ServerSelectionScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreen.swift; sourceTree = ""; }; CD6B0C4639E066915B5E6463 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; CDB3227C7A74B734924942E9 /* RoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProvider.swift; sourceTree = ""; }; - CE6383502A38A4BE00F82EBA /* MapTilerAuthorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerAuthorization.swift; sourceTree = ""; }; - CE6383522A38A4DA00F82EBA /* MapTilerStyleBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStyleBuilder.swift; sourceTree = ""; }; - CE6383542A38A52000F82EBA /* MapStyleBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapStyleBuilder.swift; sourceTree = ""; }; + CE9152212A3B4B7000EDC949 /* MapTilerGeoCodingServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerGeoCodingServiceProtocol.swift; sourceTree = ""; }; + CE9152232A3B4B9200EDC949 /* MapTilerStyleBuilderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStyleBuilderProtocol.swift; sourceTree = ""; }; + CEB59FDB2A39D1A000E6B2DF /* MapTilerGeocoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerGeocoding.swift; sourceTree = ""; }; + CED722AC2A3B0E7B000F10C8 /* URLSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSession.swift; sourceTree = ""; }; CEE0E6043EFCF6FD2A341861 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = ""; }; CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = ""; }; CF48AF076424DBC1615C74AD /* AuthenticationServiceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceProxy.swift; sourceTree = ""; }; @@ -1257,6 +1264,7 @@ DF05DA24F71B455E8EFEBC3B /* SessionVerificationViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationViewModelTests.swift; sourceTree = ""; }; DF38B69D2C331A499276F400 /* FilePreviewViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewViewModelTests.swift; sourceTree = ""; }; DF3D25B3EDB283B5807EADCF /* ReadMarkerRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineItem.swift; sourceTree = ""; }; + E062C1750EFC8627DE4CAB8E /* MapTilerAuthorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerAuthorization.swift; sourceTree = ""; }; E0FCA0957FAA0E15A9F5579D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Untranslated.stringsdict; sourceTree = ""; }; E1253D3E9395A0493DB944B9 /* AnalyticsPromptScreenCheckmarkItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenCheckmarkItem.swift; sourceTree = ""; }; E18CF12478983A5EB390FB26 /* MessageComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposer.swift; sourceTree = ""; }; @@ -1906,7 +1914,7 @@ 4E2245243369B99216C7D84E /* ImageCache.swift */, C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */, 2AFEF3AC64B1358083F76B8B /* List.swift */, - CE6383542A38A52000F82EBA /* MapStyleBuilder.swift */, + 08ADEE79933CF1F3E6626920 /* MapTiler.swift */, F72EFC8C634469F9262659C7 /* NSItemProvider.swift */, 95BAC0F6C9644336E9567EE6 /* NSRegularExpresion.swift */, 7310D8DFE01AF45F0689C3AA /* Publisher.swift */, @@ -1917,6 +1925,7 @@ 287FC98AF2664EAD79C0D902 /* UIDevice.swift */, BE148A4FFEE853C5A281500C /* UNNotificationContent.swift */, 227AC5D71A4CE43512062243 /* URL.swift */, + CED722AC2A3B0E7B000F10C8 /* URLSession.swift */, 897DF5E9A70CE05A632FC8AF /* UTType.swift */, E992D7B8BE54B2AB454613AF /* XCUIElement.swift */, ); @@ -2965,8 +2974,11 @@ AAD8234D0E9C9B12BF9F240B /* LocationAnnotation.swift */, 622D09D4ECE759189009AEAF /* MapLibreMapView.swift */, B81B6170DB690013CEB646F4 /* MapLibreModels.swift */, - CE6383502A38A4BE00F82EBA /* MapTilerAuthorization.swift */, - CE6383522A38A4DA00F82EBA /* MapTilerStyleBuilder.swift */, + E062C1750EFC8627DE4CAB8E /* MapTilerAuthorization.swift */, + B7AE92E7BFF71797BDE1D261 /* MapTilerStyleBuilder.swift */, + CE9152232A3B4B9200EDC949 /* MapTilerStyleBuilderProtocol.swift */, + CEB59FDB2A39D1A000E6B2DF /* MapTilerGeocoding.swift */, + CE9152212A3B4B7000EDC949 /* MapTilerGeoCodingServiceProtocol.swift */, ); path = MapLibre; sourceTree = ""; @@ -3864,6 +3876,7 @@ 3ED2725734568F6B8CC87544 /* AttributedStringBuilder.swift in Sources */, A6DEC1ADEC8FEEC206A0FA37 /* AttributedStringBuilderProtocol.swift in Sources */, F8E725D42023ECA091349245 /* AudioRoomTimelineItem.swift in Sources */, + CE9152242A3B4B9200EDC949 /* MapTilerStyleBuilderProtocol.swift in Sources */, 88F348E2CB14FF71CBBB665D /* AudioRoomTimelineItemContent.swift in Sources */, E62EC30B39354A391E32A126 /* AudioRoomTimelineView.swift in Sources */, EA65360A0EC026DD83AC0CF5 /* AuthenticationCoordinator.swift in Sources */, @@ -3900,14 +3913,12 @@ C32765D740C81AD4C42E8F50 /* CreateRoomFlowParameters.swift in Sources */, FB53CD9B74A15B3B94F9F788 /* CreateRoomModels.swift in Sources */, 292827744227DF61C930BDDB /* CreateRoomScreen.swift in Sources */, - CE6383512A38A4BE00F82EBA /* MapTilerAuthorization.swift in Sources */, 0BE4D5CBF86956410F071F91 /* CreateRoomViewModel.swift in Sources */, 4799A852132F1744E2825994 /* CreateRoomViewModelProtocol.swift in Sources */, 565868808A1DA565707394ED /* CurrentValuePublisher.swift in Sources */, 12C867E85E6D12EEDFD0B127 /* CustomStringConvertible.swift in Sources */, C4F69156C31A447FEFF2A47C /* DTHTMLElement+AttributedStringBuilder.swift in Sources */, C6C06DDA8881260303FBA3A0 /* Date.swift in Sources */, - CE6383532A38A4DA00F82EBA /* MapTilerStyleBuilder.swift in Sources */, EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */, 5780E444F405AA1304E1C23E /* DeveloperOptionsScreen.swift in Sources */, 5DD85A0FE3D85AEC3C7EFE36 /* DeveloperOptionsScreenCoordinator.swift in Sources */, @@ -3946,6 +3957,7 @@ 02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */, 63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */, 5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */, + CED722AD2A3B0E7B000F10C8 /* URLSession.swift in Sources */, 661A664C6EDF856B05519206 /* FilePreviewScreen.swift in Sources */, B5BD05558DC2C3091905E14A /* FilePreviewScreenCoordinator.swift in Sources */, A6F713461DB62AC06293E7B7 /* FilePreviewScreenModels.swift in Sources */, @@ -4014,6 +4026,9 @@ B66757D0254843162595B25D /* MXLogger.swift in Sources */, C1D0AB8222D7BAFC9AF9C8C0 /* MapLibreMapView.swift in Sources */, C9BE065FA7D4E77E4C61CB69 /* MapLibreModels.swift in Sources */, + 4B16ABA1CCC957C7E919BDCE /* MapTiler.swift in Sources */, + FCDA202B246F75BA28E10C5F /* MapTilerAuthorization.swift in Sources */, + 14343C2F9AD2BFEA92CA28FF /* MapTilerStyleBuilder.swift in Sources */, 67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */, 8658F5034EAD7357CE7F9AC7 /* MatrixUserShareLink.swift in Sources */, BCC864190651B3A3CF51E4DF /* MediaFileHandleProxy.swift in Sources */, @@ -4045,6 +4060,7 @@ EA01A06EEDFEF4AE7652E5F3 /* NSRegularExpresion.swift in Sources */, FA2BBAE9FC5E2E9F960C0980 /* NavigationCoordinators.swift in Sources */, 71C1347F23868324A4F43940 /* NavigationModule.swift in Sources */, + CEB59FDC2A39D1A000E6B2DF /* MapTilerGeocoding.swift in Sources */, B5E455C9689EA600EDB3E9E0 /* NavigationRootCoordinator.swift in Sources */, C6136E848E55D2C86BF760F5 /* NetworkMonitor.swift in Sources */, 0C58A846F61949B1D545D661 /* NoticeRoomTimelineItem.swift in Sources */, @@ -4223,7 +4239,6 @@ A680F54935A6ADEA4ED6C38F /* TimelineItemStatusView.swift in Sources */, 9B582B3EEFEA615D4A6FBF1A /* TimelineReactionsView.swift in Sources */, 49814A48470F347426513B07 /* TimelineReadReceiptsView.swift in Sources */, - CE6383552A38A52000F82EBA /* MapStyleBuilder.swift in Sources */, 2A90DD14DE5C891BFA433950 /* TimelineReplyView.swift in Sources */, ABF3FAB234AD3565B214309B /* TimelineSenderAvatarView.swift in Sources */, C4FE0E11A907C8999F92D5A8 /* TimelineStartRoomTimelineItem.swift in Sources */, @@ -4259,6 +4274,7 @@ F7BC744FFA7FE248FAE7F570 /* UserIndicatorToastView.swift in Sources */, E3291AD16D7A5CB14781819C /* UserNotificationCenterProtocol.swift in Sources */, 40B79D20A873620F7F128A2C /* UserPreference.swift in Sources */, + CE9152222A3B4B7000EDC949 /* MapTilerGeoCodingServiceProtocol.swift in Sources */, 80DEA2A4B20F9E279EAE6B2B /* UserProfile+Mock.swift in Sources */, F94000E3D91B11C527DA8807 /* UserProfileCell.swift in Sources */, E21FE4C5B614F311C0955859 /* UserProfileProxy.swift in Sources */, diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index ee82e0202..5df090eea 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -184,6 +184,9 @@ final class AppSettings { let lightTileStaticMapStyleURL = "https://api.maptiler.com/maps/9bc819c8-e627-474a-a348-ec144fe3d810/{z}/{x}/{y}.png" let darkTileStaticMapStyleURL = "https://api.maptiler.com/maps/dea61faf-292b-4774-9660-58fcef89a7f3/{z}/{x}/{y}.png" + // maptiler geocoding url + let geocodingURLFormatString = "https://api.maptiler.com/geocoding/%f,%f.json" + // MARK: - Feature Flags // MARK: Start Chat diff --git a/ElementX/Sources/Other/Extensions/MapStyleBuilder.swift b/ElementX/Sources/Other/Extensions/MapTiler.swift similarity index 77% rename from ElementX/Sources/Other/Extensions/MapStyleBuilder.swift rename to ElementX/Sources/Other/Extensions/MapTiler.swift index c6b72c0fb..c49a9b930 100644 --- a/ElementX/Sources/Other/Extensions/MapStyleBuilder.swift +++ b/ElementX/Sources/Other/Extensions/MapTiler.swift @@ -21,3 +21,9 @@ extension MapTilerStyleBuilder { self.init(lightURL: appSettings.lightTileMapStyleURL, darkURL: appSettings.darkTileMapStyleURL, key: appSettings.mapTilerApiKey) } } + +extension MapTilerGeoCodingService { + init(session: URLSession = .shared, appSettings: AppSettings) { + self.init(session: session, key: appSettings.mapTilerApiKey, geocodingURL: appSettings.geocodingURLFormatString) + } +} diff --git a/ElementX/Sources/Other/Extensions/URLSession.swift b/ElementX/Sources/Other/Extensions/URLSession.swift new file mode 100644 index 000000000..f9464cd31 --- /dev/null +++ b/ElementX/Sources/Other/Extensions/URLSession.swift @@ -0,0 +1,28 @@ +// +// Copyright 2023 New Vector Ltd +// +// 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 + +extension URLSession { + /// The same as `data(for:delegate:)` but with an additional immediate retry if the first attempt fails. + func dataWithRetry(for request: URLRequest, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { + if let firstTryResult = try? await data(for: request, delegate: delegate) { + return firstTryResult + } + + return try await data(for: request, delegate: delegate) + } +} diff --git a/ElementX/Sources/Other/MapLibre/MapTilerAuthorization.swift b/ElementX/Sources/Other/MapLibre/MapTilerAuthorization.swift index 889dc737e..bd5cd71e4 100644 --- a/ElementX/Sources/Other/MapLibre/MapTilerAuthorization.swift +++ b/ElementX/Sources/Other/MapLibre/MapTilerAuthorization.swift @@ -23,7 +23,7 @@ struct MapTilerAuthorization { self.key = key } - func authorizateURL(_ url: URL) -> URL { + func authorizeURL(_ url: URL) -> URL { url.appending(queryItems: [URLQueryItem(name: "key", value: key)]) } } diff --git a/ElementX/Sources/Other/MapLibre/MapTilerGeoCodingServiceProtocol.swift b/ElementX/Sources/Other/MapLibre/MapTilerGeoCodingServiceProtocol.swift new file mode 100644 index 000000000..61b0d4499 --- /dev/null +++ b/ElementX/Sources/Other/MapLibre/MapTilerGeoCodingServiceProtocol.swift @@ -0,0 +1,27 @@ +// +// Copyright 2023 New Vector Ltd +// +// 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 CoreLocation +import Foundation + +enum MapTilerGeocodingError: Error { + case wrongGeocodingURL + case geocodingFailed +} + +protocol MapTilerGeoCodingServiceProtocol { + func reverseGeoCoding(for coordinate: CLLocationCoordinate2D) async -> Result +} diff --git a/ElementX/Sources/Other/MapLibre/MapTilerGeocoding.swift b/ElementX/Sources/Other/MapLibre/MapTilerGeocoding.swift new file mode 100644 index 000000000..4ba2c6fba --- /dev/null +++ b/ElementX/Sources/Other/MapLibre/MapTilerGeocoding.swift @@ -0,0 +1,72 @@ +// +// Copyright 2023 New Vector Ltd +// +// 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 CoreLocation +import Foundation + +struct MapTilerGeoCodingService: MapTilerGeoCodingServiceProtocol { + private let key: String + private let session: URLSession + private let geocodingURL: String + + init(session: URLSession = .shared, key: String, geocodingURL: String) { + self.session = session + self.key = key + self.geocodingURL = geocodingURL + } + + private struct GeocodedPlace: Decodable { + struct FeatureCollection: Decodable { + let placeName: String + } + + let features: [FeatureCollection] + } + + func reverseGeoCoding(for coordinate: CLLocationCoordinate2D) async -> Result { + let latitude = coordinate.latitude + let longitude = coordinate.longitude + + let path = String(format: geocodingURL, longitude, latitude) + guard var url = URL(string: path) else { return .failure(.wrongGeocodingURL) } + let authorization = MapTilerAuthorization(key: key) + url = authorization.authorizeURL(url) + url.append(queryItems: [.init(name: "limit", value: "1")]) + + var request = URLRequest(url: url) + request.httpMethod = "GET" + + do { + let (data, response) = try await session.dataWithRetry(for: request) + + guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { + let errorDescription = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "Unknown" + MXLog.error("Failed to get reverse geocoding: \(errorDescription)") + MXLog.error("Response: \(response)") + return .failure(.geocodingFailed) + } + + // Parse the JSON data + let decoder = JSONDecoder() + decoder.keyDecodingStrategy = .convertFromSnakeCase + let decodedResponse = try decoder.decode(GeocodedPlace.self, from: data) + guard let placeName = decodedResponse.features.first?.placeName else { return .failure(.geocodingFailed) } + return .success(placeName) + } catch { + return .failure(.geocodingFailed) + } + } +} diff --git a/ElementX/Sources/Other/MapLibre/MapTilerStyleBuilder.swift b/ElementX/Sources/Other/MapLibre/MapTilerStyleBuilder.swift index 9174e3565..760437794 100644 --- a/ElementX/Sources/Other/MapLibre/MapTilerStyleBuilder.swift +++ b/ElementX/Sources/Other/MapLibre/MapTilerStyleBuilder.swift @@ -16,10 +16,6 @@ import Foundation -protocol MapTilerStyleBuilderProtocol { - func dynamicMapURL(for style: MapTilerStyle) -> URL? -} - struct MapTilerStyleBuilder: MapTilerStyleBuilderProtocol { let lightURL: String let darkURL: String @@ -36,6 +32,6 @@ struct MapTilerStyleBuilder: MapTilerStyleBuilderProtocol { guard let url = URL(string: path) else { return nil } let authorization = MapTilerAuthorization(key: key) - return authorization.authorizateURL(url) + return authorization.authorizeURL(url) } } diff --git a/ElementX/Sources/Other/MapLibre/MapTilerStyleBuilderProtocol.swift b/ElementX/Sources/Other/MapLibre/MapTilerStyleBuilderProtocol.swift new file mode 100644 index 000000000..62f0f0f68 --- /dev/null +++ b/ElementX/Sources/Other/MapLibre/MapTilerStyleBuilderProtocol.swift @@ -0,0 +1,21 @@ +// +// Copyright 2023 New Vector Ltd +// +// 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 + +protocol MapTilerStyleBuilderProtocol { + func dynamicMapURL(for style: MapTilerStyle) -> URL? +} diff --git a/ElementX/Sources/Services/BugReport/BugReportService.swift b/ElementX/Sources/Services/BugReport/BugReportService.swift index 1aea713ea..07d553d95 100644 --- a/ElementX/Sources/Services/BugReport/BugReportService.swift +++ b/ElementX/Sources/Services/BugReport/BugReportService.swift @@ -342,14 +342,3 @@ extension BugReportService: URLSessionTaskDelegate { .store(in: &cancellables) } } - -private extension URLSession { - /// The same as `data(for:delegate:)` but with an additional immediate retry if the first attempt fails. - func dataWithRetry(for request: URLRequest, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { - if let firstTryResult = try? await data(for: request, delegate: delegate) { - return firstTryResult - } - - return try await data(for: request, delegate: delegate) - } -} diff --git a/ElementX/SupportingFiles/Info.plist b/ElementX/SupportingFiles/Info.plist index 2c3970356..b634c4bdf 100644 --- a/ElementX/SupportingFiles/Info.plist +++ b/ElementX/SupportingFiles/Info.plist @@ -28,6 +28,8 @@ NSCameraUsageDescription To take pictures or videos and send them as a message $(APP_DISPLAY_NAME) needs access to the camera. + NSLocationWhenInUseUsageDescription + When you share your location to people, $(APP_DISPLAY_NAME) needs access to show them a map. NSMicrophoneUsageDescription To take videos with audio and send them as a message $(APP_DISPLAY_NAME) needs access to the microphone. NSPhotoLibraryAddUsageDescription diff --git a/changelog.d/1085.feature b/changelog.d/1085.feature new file mode 100644 index 000000000..9ca753914 --- /dev/null +++ b/changelog.d/1085.feature @@ -0,0 +1 @@ +Add reverse geocoding request, that for a given coordinate will return the place name. \ No newline at end of file