From 5a32d1a78815cdb049402ca468e71a947a94fc92 Mon Sep 17 00:00:00 2001 From: Aleksey Belouosv Date: Fri, 7 Dec 2018 13:03:38 +0300 Subject: [PATCH] [iOS] geo tracking --- iphone/Maps/Bridging-Header.h | 1 + .../Maps/Core/Location/GeoTracker/Geo.swift | 7 ++ .../Location/GeoTracker/IGeoTracker.swift | 5 ++ .../Location/GeoTracker/IMWMGeoTrackerCore.h | 15 ++++ .../Location/GeoTracker/IMWMGeoTrackerZone.h | 11 +++ .../Location/GeoTracker/Impl/GeoTracker.swift | 66 +++++++++++++++++ .../GeoTracker/Impl/GeoZoneTracker.swift | 38 ++++++++++ .../GeoTracker/Impl/MWMGeoTrackerCore.h | 9 +++ .../GeoTracker/Impl/MWMGeoTrackerCore.mm | 74 +++++++++++++++++++ .../Maps/Core/Location/MWMLocationManager.mm | 13 +++- .../en.lproj/InfoPlist.strings | 3 + iphone/Maps/Maps.xcodeproj/project.pbxproj | 42 +++++++++++ 12 files changed, 281 insertions(+), 3 deletions(-) create mode 100644 iphone/Maps/Core/Location/GeoTracker/Geo.swift create mode 100644 iphone/Maps/Core/Location/GeoTracker/IGeoTracker.swift create mode 100644 iphone/Maps/Core/Location/GeoTracker/IMWMGeoTrackerCore.h create mode 100644 iphone/Maps/Core/Location/GeoTracker/IMWMGeoTrackerZone.h create mode 100644 iphone/Maps/Core/Location/GeoTracker/Impl/GeoTracker.swift create mode 100644 iphone/Maps/Core/Location/GeoTracker/Impl/GeoZoneTracker.swift create mode 100644 iphone/Maps/Core/Location/GeoTracker/Impl/MWMGeoTrackerCore.h create mode 100644 iphone/Maps/Core/Location/GeoTracker/Impl/MWMGeoTrackerCore.mm diff --git a/iphone/Maps/Bridging-Header.h b/iphone/Maps/Bridging-Header.h index 2b1551db79..0e3e738bba 100644 --- a/iphone/Maps/Bridging-Header.h +++ b/iphone/Maps/Bridging-Header.h @@ -37,6 +37,7 @@ #import "MWMController.h" #import "MWMEditorHelper.h" #import "MWMFrameworkHelper.h" +#import "MWMGeoTrackerCore.h" #import "MWMKeyboard.h" #import "MWMLocationManager.h" #import "MWMMapWidgetsHelper.h" diff --git a/iphone/Maps/Core/Location/GeoTracker/Geo.swift b/iphone/Maps/Core/Location/GeoTracker/Geo.swift new file mode 100644 index 0000000000..91f1624963 --- /dev/null +++ b/iphone/Maps/Core/Location/GeoTracker/Geo.swift @@ -0,0 +1,7 @@ +final class Geo: NSObject { + @objc + static func geoTracker() -> IGeoTracker { + let trackerCore = MWMGeoTrackerCore() + return GeoTracker(trackerCore: trackerCore) + } +} diff --git a/iphone/Maps/Core/Location/GeoTracker/IGeoTracker.swift b/iphone/Maps/Core/Location/GeoTracker/IGeoTracker.swift new file mode 100644 index 0000000000..61c27d81ad --- /dev/null +++ b/iphone/Maps/Core/Location/GeoTracker/IGeoTracker.swift @@ -0,0 +1,5 @@ +@objc +protocol IGeoTracker: AnyObject { + func startTracking() + func endTracking() +} diff --git a/iphone/Maps/Core/Location/GeoTracker/IMWMGeoTrackerCore.h b/iphone/Maps/Core/Location/GeoTracker/IMWMGeoTrackerCore.h new file mode 100644 index 0000000000..50c2c9a512 --- /dev/null +++ b/iphone/Maps/Core/Location/GeoTracker/IMWMGeoTrackerCore.h @@ -0,0 +1,15 @@ +#import "IMWMGeoTrackerZone.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol IMWMGeoTrackerCore + +- (NSArray> *)geoZonesForLat:(CLLocationDegrees)lat + lon:(CLLocationDegrees)lon + accuracy:(CLLocationAccuracy) accuracy; + +- (void)logEnterZone:(id)zone location:(CLLocation *)location; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/Maps/Core/Location/GeoTracker/IMWMGeoTrackerZone.h b/iphone/Maps/Core/Location/GeoTracker/IMWMGeoTrackerZone.h new file mode 100644 index 0000000000..c5934263fb --- /dev/null +++ b/iphone/Maps/Core/Location/GeoTracker/IMWMGeoTrackerZone.h @@ -0,0 +1,11 @@ +NS_ASSUME_NONNULL_BEGIN + +@protocol IMWMGeoTrackerZone + +@property (nonatomic, readonly) CLLocationDegrees latitude; +@property (nonatomic, readonly) CLLocationDegrees longitude; +@property (nonatomic, readonly) NSString *identifier; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/Maps/Core/Location/GeoTracker/Impl/GeoTracker.swift b/iphone/Maps/Core/Location/GeoTracker/Impl/GeoTracker.swift new file mode 100644 index 0000000000..99421f35e8 --- /dev/null +++ b/iphone/Maps/Core/Location/GeoTracker/Impl/GeoTracker.swift @@ -0,0 +1,66 @@ +class GeoTracker: NSObject, IGeoTracker { + private let trackerCore: IMWMGeoTrackerCore + private let locationManager = CLLocationManager() + private var trackingZones: [String : IMWMGeoTrackerZone]? + private var zoneTrackers: [String : GeoZoneTracker] = [:] + + init(trackerCore: IMWMGeoTrackerCore) { + self.trackerCore = trackerCore + super.init() + locationManager.delegate = self + } + + deinit { + locationManager.delegate = nil + } + + @objc + func startTracking() { + if CLLocationManager.significantLocationChangeMonitoringAvailable() { + locationManager.startMonitoringSignificantLocationChanges() + } + } + + @objc + func endTracking() { + locationManager.stopMonitoringSignificantLocationChanges() + } +} + +extension GeoTracker: CLLocationManagerDelegate { + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + if let location = locations.last { + locationManager.monitoredRegions.forEach { + locationManager.stopMonitoring(for: $0) + } + + if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) { + let zones = trackerCore.geoZones(forLat: location.coordinate.latitude, + lon: location.coordinate.longitude, + accuracy: location.horizontalAccuracy) + zones.forEach { + locationManager.startMonitoring( + for: CLCircularRegion(center: CLLocationCoordinate2DMake($0.latitude, $0.longitude), + radius: 20, + identifier: $0.identifier)) + } + trackingZones = zones.reduce(into: [:]) { $0[$1.identifier] = $1 } + } + } + } + + func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) { + if let zone = trackingZones?[region.identifier] { + let zoneTracker = GeoZoneTracker(zone, trackerCore: trackerCore) + zoneTracker.startTracking() + zoneTrackers[region.identifier] = zoneTracker + } + } + + func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) { + if let zoneTracker = zoneTrackers[region.identifier] { + zoneTracker.stopTracking() + zoneTrackers[region.identifier] = nil + } + } +} diff --git a/iphone/Maps/Core/Location/GeoTracker/Impl/GeoZoneTracker.swift b/iphone/Maps/Core/Location/GeoTracker/Impl/GeoZoneTracker.swift new file mode 100644 index 0000000000..9816a7033a --- /dev/null +++ b/iphone/Maps/Core/Location/GeoTracker/Impl/GeoZoneTracker.swift @@ -0,0 +1,38 @@ +class GeoZoneTracker: NSObject { + let geoZone: IMWMGeoTrackerZone + let trackerCore: IMWMGeoTrackerCore + let locationManager = CLLocationManager() + + init(_ zone: IMWMGeoTrackerZone, trackerCore: IMWMGeoTrackerCore) { + self.geoZone = zone + self.trackerCore = trackerCore + locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters + locationManager.distanceFilter = 10 + super.init() + locationManager.delegate = self + } + + deinit { + locationManager.delegate = nil + } + + func startTracking() { + locationManager.startUpdatingLocation() + } + + func stopTracking() { + locationManager.stopUpdatingLocation() + } +} + +extension GeoZoneTracker: CLLocationManagerDelegate { + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + if let visitedLocation = locations.filter({ + $0.horizontalAccuracy <= 20 && + $0.distance(from: CLLocation(latitude: geoZone.latitude, longitude: geoZone.longitude)) <= 20 + }).first { + trackerCore.logEnter(geoZone, location: visitedLocation) + stopTracking() + } + } +} diff --git a/iphone/Maps/Core/Location/GeoTracker/Impl/MWMGeoTrackerCore.h b/iphone/Maps/Core/Location/GeoTracker/Impl/MWMGeoTrackerCore.h new file mode 100644 index 0000000000..19b2fe7ca7 --- /dev/null +++ b/iphone/Maps/Core/Location/GeoTracker/Impl/MWMGeoTrackerCore.h @@ -0,0 +1,9 @@ +#import "IMWMGeoTrackerCore.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MWMGeoTrackerCore : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/Maps/Core/Location/GeoTracker/Impl/MWMGeoTrackerCore.mm b/iphone/Maps/Core/Location/GeoTracker/Impl/MWMGeoTrackerCore.mm new file mode 100644 index 0000000000..b5dcd91c38 --- /dev/null +++ b/iphone/Maps/Core/Location/GeoTracker/Impl/MWMGeoTrackerCore.mm @@ -0,0 +1,74 @@ +#import "MWMGeoTrackerCore.h" + +#include "map/framework_light.hpp" + +@interface MWMGeoTrackerZone: NSObject + +@property (nonatomic, readwrite) NSString *identifier; + +- (instancetype)initWithCampaignFeature:(CampaignFeature const &)feature; + +- (CampaignFeature const &)feature; + +@end + +@implementation MWMGeoTrackerZone { + CampaignFeature _feature; +} + +- (instancetype)initWithCampaignFeature:(CampaignFeature const &)feature { + self = [super init]; + if (self) { + _feature = feature; + _identifier = [NSUUID UUID].UUIDString; + } + + return self; +} + +- (CLLocationDegrees)latitude { + return _feature.m_lat; +} + +- (CLLocationDegrees)longitude { + return _feature.m_lon; +} + +- (CampaignFeature const &)feature { + return _feature; +} + +@end + +@implementation MWMGeoTrackerCore + +- (NSArray> *)geoZonesForLat:(CLLocationDegrees)lat + lon:(CLLocationDegrees)lon + accuracy:(CLLocationAccuracy)accuracy { + lightweight::Framework f(lightweight::REQUEST_TYPE_LOCAL_ADS_FEATURES); + auto campainFeatures = f.GetLocalAdsFeatures(lat, lon, accuracy, 20); + NSMutableArray * result = [NSMutableArray array]; + for (auto const & cf : campainFeatures) { + [result addObject:[[MWMGeoTrackerZone alloc] initWithCampaignFeature:cf]]; + } + return [result copy]; +} + +- (void)logEnterZone:(id)zone location:(CLLocation *)location { + NSAssert([zone isKindOfClass:MWMGeoTrackerZone.class], @"zone must be of MWMGeoTrackerZone type"); + MWMGeoTrackerZone * geoZone = (MWMGeoTrackerZone *)zone; + auto feature = geoZone.feature; + lightweight::Framework f(lightweight::REQUEST_TYPE_LOCAL_ADS_STATISTICS); + local_ads::Event event(local_ads::EventType::Visit, + feature.m_mwmVersion, + feature.m_countryId, + feature.m_featureIndex, + 0, + local_ads::Clock::now(), + location.coordinate.latitude, + location.coordinate.longitude, + location.horizontalAccuracy); + f.GetLocalAdsStatistics()->RegisterEvent(std::move(event)); +} + +@end diff --git a/iphone/Maps/Core/Location/MWMLocationManager.mm b/iphone/Maps/Core/Location/MWMLocationManager.mm index 1aa223fc6d..c538a0620d 100644 --- a/iphone/Maps/Core/Location/MWMLocationManager.mm +++ b/iphone/Maps/Core/Location/MWMLocationManager.mm @@ -1,11 +1,13 @@ #import "MWMLocationManager.h" #import #import "MWMAlertViewController.h" +#import "MWMGeoTrackerCore.h" #import "MWMLocationObserver.h" #import "MWMLocationPredictor.h" #import "MWMRouter.h" #import "MapsAppDelegate.h" #import "Statistics.h" +#import "SwiftBridge.h" #import "3party/Alohalytics/src/alohalytics_objc.h" #include "Framework.h" @@ -156,6 +158,7 @@ void setPermissionRequested() @property(nonatomic) Observers * observers; @property(nonatomic) MWMLocationFrameworkUpdate frameworkUpdateMode; @property(nonatomic) location::TLocationSource locationSource; +@property(nonatomic) id geoTracker; @end @@ -177,7 +180,10 @@ void setPermissionRequested() { self = [super init]; if (self) + { _observers = [Observers weakObjectsHashTable]; + _geoTracker = [Geo geoTracker]; + } return self; } @@ -489,12 +495,13 @@ void setPermissionRequested() - (BOOL)start { - auto const doStart = ^{ + MWMVoidBlock doStart = ^{ LOG(LINFO, ("startUpdatingLocation")); CLLocationManager * locationManager = self.locationManager; - if ([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) - [locationManager requestWhenInUseAuthorization]; + if ([locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) + [locationManager requestAlwaysAuthorization]; [locationManager startUpdatingLocation]; + [self.geoTracker startTracking]; setPermissionRequested(); if ([CLLocationManager headingAvailable]) [locationManager startUpdatingHeading]; diff --git a/iphone/Maps/LocalizedStrings/en.lproj/InfoPlist.strings b/iphone/Maps/LocalizedStrings/en.lproj/InfoPlist.strings index 566aebd2eb..6b30b290f5 100644 --- a/iphone/Maps/LocalizedStrings/en.lproj/InfoPlist.strings +++ b/iphone/Maps/LocalizedStrings/en.lproj/InfoPlist.strings @@ -21,6 +21,9 @@ /********** gps strings **********/ +/* Needed to explain why we always require access to GPS coordinates, and not only when the app is active. */ +"NSLocationAlwaysAndWhenInUseUsageDescription" = "Defining current location in the background is necessary to fully enjoy the functionality of the app. It is mainly used in the options of following the route and saving your recent travelled path."; + /* Needed to explain why we always require access to GPS coordinates, and not only when the app is active. */ "NSLocationAlwaysUsageDescription" = "Defining current location in the background is necessary to fully enjoy the functionality of the app. It is mainly used in the options of following the route and saving your recent travelled path."; diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index cb5a0da089..f3de3a6586 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -397,6 +397,11 @@ 4788738F20EE30B300F6826B /* LayersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4788738D20EE30B300F6826B /* LayersViewController.swift */; }; 4788739020EE30B300F6826B /* LayersViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4788738E20EE30B300F6826B /* LayersViewController.xib */; }; 4788739220EE326500F6826B /* VerticallyAlignedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4788739120EE326400F6826B /* VerticallyAlignedButton.swift */; }; + 47B06DED21B696C20094CCAD /* GeoTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DEC21B696C20094CCAD /* GeoTracker.swift */; }; + 47B06DF021B697230094CCAD /* MWMGeoTrackerCore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DEF21B697230094CCAD /* MWMGeoTrackerCore.mm */; }; + 47B06DF921B95F5E0094CCAD /* IGeoTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DF821B95F5E0094CCAD /* IGeoTracker.swift */; }; + 47B06DFE21B965950094CCAD /* Geo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DFD21B965950094CCAD /* Geo.swift */; }; + 47B06E0021BAAC270094CCAD /* GeoZoneTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DFF21BAAC270094CCAD /* GeoZoneTracker.swift */; }; 47B505542136B0C2009CBB55 /* DiscoveryTutorialBlur.xib in Resources */ = {isa = PBXBuildFile; fileRef = 47B505522136AB41009CBB55 /* DiscoveryTutorialBlur.xib */; }; 47B505552136B0CF009CBB55 /* SearchTutorialBlur.xib in Resources */ = {isa = PBXBuildFile; fileRef = 47B505532136AD69009CBB55 /* SearchTutorialBlur.xib */; }; 47C7F9732191E15A00C2760C /* InAppBilling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C7F9722191E15A00C2760C /* InAppBilling.swift */; }; @@ -1426,6 +1431,14 @@ 4788738D20EE30B300F6826B /* LayersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayersViewController.swift; sourceTree = ""; }; 4788738E20EE30B300F6826B /* LayersViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LayersViewController.xib; sourceTree = ""; }; 4788739120EE326400F6826B /* VerticallyAlignedButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerticallyAlignedButton.swift; sourceTree = ""; }; + 47B06DEC21B696C20094CCAD /* GeoTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoTracker.swift; sourceTree = ""; }; + 47B06DEE21B697230094CCAD /* MWMGeoTrackerCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMGeoTrackerCore.h; sourceTree = ""; }; + 47B06DEF21B697230094CCAD /* MWMGeoTrackerCore.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMGeoTrackerCore.mm; sourceTree = ""; }; + 47B06DF821B95F5E0094CCAD /* IGeoTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IGeoTracker.swift; sourceTree = ""; }; + 47B06DFA21B960080094CCAD /* IMWMGeoTrackerCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IMWMGeoTrackerCore.h; sourceTree = ""; }; + 47B06DFB21B960CE0094CCAD /* IMWMGeoTrackerZone.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IMWMGeoTrackerZone.h; sourceTree = ""; }; + 47B06DFD21B965950094CCAD /* Geo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Geo.swift; sourceTree = ""; }; + 47B06DFF21BAAC270094CCAD /* GeoZoneTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoZoneTracker.swift; sourceTree = ""; }; 47B505522136AB41009CBB55 /* DiscoveryTutorialBlur.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoveryTutorialBlur.xib; sourceTree = ""; }; 47B505532136AD69009CBB55 /* SearchTutorialBlur.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SearchTutorialBlur.xib; sourceTree = ""; }; 47C7F9722191E15A00C2760C /* InAppBilling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppBilling.swift; sourceTree = ""; }; @@ -2377,6 +2390,7 @@ 340475291E081A4600C92850 /* Location */ = { isa = PBXGroup; children = ( + 47B06DEB21B6962D0094CCAD /* GeoTracker */, 3404752A1E081A4600C92850 /* MWMLocationHelpers.h */, 3404752B1E081A4600C92850 /* MWMLocationManager.h */, 3404752C1E081A4600C92850 /* MWMLocationManager.mm */, @@ -3399,6 +3413,29 @@ path = Metrics; sourceTree = ""; }; + 47B06DEB21B6962D0094CCAD /* GeoTracker */ = { + isa = PBXGroup; + children = ( + 47B06DFD21B965950094CCAD /* Geo.swift */, + 47B06DFC21B965450094CCAD /* Impl */, + 47B06DF821B95F5E0094CCAD /* IGeoTracker.swift */, + 47B06DFB21B960CE0094CCAD /* IMWMGeoTrackerZone.h */, + 47B06DFA21B960080094CCAD /* IMWMGeoTrackerCore.h */, + ); + path = GeoTracker; + sourceTree = ""; + }; + 47B06DFC21B965450094CCAD /* Impl */ = { + isa = PBXGroup; + children = ( + 47B06DEC21B696C20094CCAD /* GeoTracker.swift */, + 47B06DFF21BAAC270094CCAD /* GeoZoneTracker.swift */, + 47B06DEE21B697230094CCAD /* MWMGeoTrackerCore.h */, + 47B06DEF21B697230094CCAD /* MWMGeoTrackerCore.mm */, + ); + path = Impl; + sourceTree = ""; + }; 47E3C7232111E2F8008B3B27 /* Modal */ = { isa = PBXGroup; children = ( @@ -4865,6 +4902,7 @@ 34D3AFF61E37A36A004100F9 /* UICollectionView+Cells.swift in Sources */, 473464A7218B0BC000D6AF5B /* MWMPurchaseValidation.mm in Sources */, 4767CDA420AAF66B00BD8166 /* NSAttributedString+HTML.swift in Sources */, + 47B06DFE21B965950094CCAD /* Geo.swift in Sources */, 6741A9A91BF340DE002C974C /* MWMDefaultAlert.mm in Sources */, 340708781F2B5D6C00029ECC /* DimBackground.swift in Sources */, 3490D2DF1CE9DD2500D0B838 /* MWMSideButtons.mm in Sources */, @@ -5061,6 +5099,7 @@ 4788738F20EE30B300F6826B /* LayersViewController.swift in Sources */, 348A8DFB1F66775A00D83026 /* RatingViewSettings.swift in Sources */, 3472B5E1200F86C800DC6CD5 /* MWMEditorHelper.mm in Sources */, + 47B06DF021B697230094CCAD /* MWMGeoTrackerCore.mm in Sources */, F6E2FE3D1E097BA00083EBEC /* MWMMigrationViewController.mm in Sources */, F6E2FD501E097BA00083EBEC /* MWMMapDownloaderAdsTableViewCell.mm in Sources */, 4719A643219CB61D009F9AA7 /* BillingPendingTransaction.swift in Sources */, @@ -5151,6 +5190,7 @@ 3454D7BC1E07F045004AF2AD /* CLLocation+Mercator.mm in Sources */, 47E3C7272111E5A8008B3B27 /* AlertPresentationController.swift in Sources */, F6E2FF4E1E097BA00083EBEC /* MWMAboutController.mm in Sources */, + 47B06DF921B95F5E0094CCAD /* IGeoTracker.swift in Sources */, 34F407381E9E1AFF00E57AC0 /* FacebookBanner.swift in Sources */, 34F5E0D41E3F254800B1C415 /* UIView+Hierarchy.swift in Sources */, 33BCDF8B218C976D00EF5B74 /* TagsCollectionViewLayout.swift in Sources */, @@ -5268,6 +5308,7 @@ 34E50DD81F6FCAB1008EED49 /* UGCSummaryRatingCell.swift in Sources */, 6741AA281BF340DE002C974C /* MWMAlert.mm in Sources */, F6E2FF571E097BA00083EBEC /* MWMMobileInternetViewController.mm in Sources */, + 47B06E0021BAAC270094CCAD /* GeoZoneTracker.swift in Sources */, 3404F4952028A1B80090E401 /* BMCPermissionsCell.swift in Sources */, 340416441E7BED3900E2B6D6 /* PhotosTransitionAnimator.swift in Sources */, 34AB66261FC5AA330078E451 /* RouteManagerDimView.swift in Sources */, @@ -5282,6 +5323,7 @@ 3467CEB6202C6FA900D3C670 /* BMCNotificationsCell.swift in Sources */, 34B846A32029DFEB0081ECCD /* BMCPermissionsHeader.swift in Sources */, F6E2FD9E1E097BA00083EBEC /* MWMEditBookmarkController.mm in Sources */, + 47B06DED21B696C20094CCAD /* GeoTracker.swift in Sources */, 349FC54B1F680DAE00968C9F /* ExpandableTextViewSettings.swift in Sources */, F6E2FE0A1E097BA00083EBEC /* MWMOpeningHoursDeleteScheduleTableViewCell.mm in Sources */, 3454D7DA1E07F045004AF2AD /* UILabel+RuntimeAttributes.mm in Sources */,