From dcbd93f9143e94111455e8616e1aa7d33ff12b5a Mon Sep 17 00:00:00 2001 From: VladiMihaylenko Date: Mon, 20 Mar 2017 18:37:35 +0300 Subject: [PATCH] [ios] Added caching to banners. --- iphone/Maps/Classes/MapsAppDelegate.mm | 2 - iphone/Maps/Maps.xcodeproj/project.pbxproj | 16 ++ iphone/Maps/UI/PlacePage/BannersCache.swift | 96 ++++++++++++ .../PlacePage/CacheableFacebookBanner.swift | 141 ++++++++++++++++++ iphone/Maps/UI/PlacePage/MWMPlacePageData.mm | 67 +++------ .../Maps/UI/PlacePage/MWMPlacePageManager.mm | 1 + 6 files changed, 272 insertions(+), 51 deletions(-) create mode 100644 iphone/Maps/UI/PlacePage/BannersCache.swift create mode 100644 iphone/Maps/UI/PlacePage/CacheableFacebookBanner.swift diff --git a/iphone/Maps/Classes/MapsAppDelegate.mm b/iphone/Maps/Classes/MapsAppDelegate.mm index 5e6e624f75..370a51c4e3 100644 --- a/iphone/Maps/Classes/MapsAppDelegate.mm +++ b/iphone/Maps/Classes/MapsAppDelegate.mm @@ -133,8 +133,6 @@ using namespace osm_auth_ios; @property(nonatomic) NSInteger standbyCounter; -@property(weak, nonatomic) NSTimer * mapStyleSwitchTimer; - @property(nonatomic, readwrite) LocationManager * locationManager; @end diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index ecf9061513..0357a33145 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -825,12 +825,18 @@ F623DA6B1C9C2731006A3436 /* opening_hours_how_to_edit.html in Resources */ = {isa = PBXBuildFile; fileRef = F623DA6A1C9C2731006A3436 /* opening_hours_how_to_edit.html */; }; F623DA6C1C9C2731006A3436 /* opening_hours_how_to_edit.html in Resources */ = {isa = PBXBuildFile; fileRef = F623DA6A1C9C2731006A3436 /* opening_hours_how_to_edit.html */; }; F623DA6F1C9C2E62006A3436 /* MWMAddPlaceNavigationBar.xib in Resources */ = {isa = PBXBuildFile; fileRef = F653CE171C71F62400A453F1 /* MWMAddPlaceNavigationBar.xib */; }; + F624592F1E80185500E7AE9E /* BannersCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = F624592E1E80185500E7AE9E /* BannersCache.swift */; }; + F62459301E80185500E7AE9E /* BannersCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = F624592E1E80185500E7AE9E /* BannersCache.swift */; }; + F62459311E80185500E7AE9E /* BannersCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = F624592E1E80185500E7AE9E /* BannersCache.swift */; }; F626D52E1C3E6CAA00C17D15 /* MWMTableViewCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = F626D52D1C3E6CAA00C17D15 /* MWMTableViewCell.mm */; }; F626D52F1C3E83F800C17D15 /* MWMTableViewCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = F626D52D1C3E6CAA00C17D15 /* MWMTableViewCell.mm */; }; F63774E71B59375E00BCF54D /* MWMRoutingDisclaimerAlert.xib in Resources */ = {isa = PBXBuildFile; fileRef = F63774E61B59375E00BCF54D /* MWMRoutingDisclaimerAlert.xib */; }; F63774EA1B59376F00BCF54D /* MWMRoutingDisclaimerAlert.mm in Sources */ = {isa = PBXBuildFile; fileRef = F63774E91B59376F00BCF54D /* MWMRoutingDisclaimerAlert.mm */; }; F6381BF51CD12045004CA943 /* LocaleTranslator.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6381BF41CD12045004CA943 /* LocaleTranslator.mm */; }; F6381BF61CD12045004CA943 /* LocaleTranslator.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6381BF41CD12045004CA943 /* LocaleTranslator.mm */; }; + F63B242F1E890C8F008C9D3C /* CacheableFacebookBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = F63B242E1E890C8F008C9D3C /* CacheableFacebookBanner.swift */; }; + F63B24301E890C8F008C9D3C /* CacheableFacebookBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = F63B242E1E890C8F008C9D3C /* CacheableFacebookBanner.swift */; }; + F63B24311E890C8F008C9D3C /* CacheableFacebookBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = F63B242E1E890C8F008C9D3C /* CacheableFacebookBanner.swift */; }; F64D9C9F1C899C350063FA30 /* MWMEditorViralAlert.mm in Sources */ = {isa = PBXBuildFile; fileRef = F64D9C9E1C899C350063FA30 /* MWMEditorViralAlert.mm */; }; F64D9CA01C899C350063FA30 /* MWMEditorViralAlert.mm in Sources */ = {isa = PBXBuildFile; fileRef = F64D9C9E1C899C350063FA30 /* MWMEditorViralAlert.mm */; }; F64D9CA21C899C760063FA30 /* MWMEditorViralAlert.xib in Resources */ = {isa = PBXBuildFile; fileRef = F64D9CA11C899C760063FA30 /* MWMEditorViralAlert.xib */; }; @@ -1915,12 +1921,14 @@ F61579351AC2CEB60032D8E9 /* MWMRateAlert.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMRateAlert.xib; sourceTree = ""; }; F6172FA41BBD5A3E0081D325 /* MWMiPadRoutePreview.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMiPadRoutePreview.xib; sourceTree = ""; }; F623DA6A1C9C2731006A3436 /* opening_hours_how_to_edit.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = opening_hours_how_to_edit.html; path = ../../data/opening_hours_how_to_edit.html; sourceTree = ""; }; + F624592E1E80185500E7AE9E /* BannersCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BannersCache.swift; sourceTree = ""; }; F626D52C1C3E6CAA00C17D15 /* MWMTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMTableViewCell.h; sourceTree = ""; }; F626D52D1C3E6CAA00C17D15 /* MWMTableViewCell.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMTableViewCell.mm; sourceTree = ""; }; F63774E61B59375E00BCF54D /* MWMRoutingDisclaimerAlert.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMRoutingDisclaimerAlert.xib; sourceTree = ""; }; F63774E81B59376F00BCF54D /* MWMRoutingDisclaimerAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMRoutingDisclaimerAlert.h; sourceTree = ""; }; F63774E91B59376F00BCF54D /* MWMRoutingDisclaimerAlert.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMRoutingDisclaimerAlert.mm; sourceTree = ""; }; F6381BF41CD12045004CA943 /* LocaleTranslator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LocaleTranslator.mm; sourceTree = ""; }; + F63B242E1E890C8F008C9D3C /* CacheableFacebookBanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheableFacebookBanner.swift; sourceTree = ""; }; F64D9C9D1C899C350063FA30 /* MWMEditorViralAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMEditorViralAlert.h; sourceTree = ""; }; F64D9C9E1C899C350063FA30 /* MWMEditorViralAlert.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMEditorViralAlert.mm; sourceTree = ""; }; F64D9CA11C899C760063FA30 /* MWMEditorViralAlert.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMEditorViralAlert.xib; sourceTree = ""; }; @@ -3664,6 +3672,8 @@ F6E2FC931E097B9F0083EBEC /* DirectionView */, F6E2FC971E097B9F0083EBEC /* MWMPlacePageData.h */, F6E2FC981E097B9F0083EBEC /* MWMPlacePageData.mm */, + F624592E1E80185500E7AE9E /* BannersCache.swift */, + F63B242E1E890C8F008C9D3C /* CacheableFacebookBanner.swift */, F6E2FC991E097B9F0083EBEC /* MWMPlacePageManager.h */, F6E2FC9A1E097B9F0083EBEC /* MWMPlacePageManager.mm */, F6E2FC9B1E097B9F0083EBEC /* MWMPlacePageProtocol.h */, @@ -4975,6 +4985,7 @@ F6E2FD5E1E097BA00083EBEC /* MWMMapDownloaderLargeCountryTableViewCell.mm in Sources */, F6558DA11E642CC0002203AE /* MWMFacilitiesController.mm in Sources */, F6E2FF471E097BA00083EBEC /* SettingsTableViewSelectableCell.swift in Sources */, + F624592F1E80185500E7AE9E /* BannersCache.swift in Sources */, 349D1ACE1E2E325B004A2006 /* MWMBottomMenuCollectionViewCell.mm in Sources */, F6E2FF261E097BA00083EBEC /* MWMSearchTabButtonsView.mm in Sources */, F6E2FE901E097BA00083EBEC /* MWMPlacePageTaxiCell.mm in Sources */, @@ -5107,6 +5118,7 @@ 3404165B1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift in Sources */, 340475701E081A4600C92850 /* MWMSettings.mm in Sources */, 3404756D1E081A4600C92850 /* MWMSearch.mm in Sources */, + F63B242F1E890C8F008C9D3C /* CacheableFacebookBanner.swift in Sources */, 3486B5071E27A4B50069C126 /* LocalNotificationManager.mm in Sources */, F6E2FEFC1E097BA00083EBEC /* MWMSearchCategoryCell.mm in Sources */, 3454D7CA1E07F045004AF2AD /* UIColor+MapsMeColor.mm in Sources */, @@ -5255,6 +5267,7 @@ F6E2FF271E097BA00083EBEC /* MWMSearchTabButtonsView.mm in Sources */, F6558DA21E642CC0002203AE /* MWMFacilitiesController.mm in Sources */, F6E2FE911E097BA00083EBEC /* MWMPlacePageTaxiCell.mm in Sources */, + F62459301E80185500E7AE9E /* BannersCache.swift in Sources */, 349D1ACF1E2E325B004A2006 /* MWMBottomMenuCollectionViewCell.mm in Sources */, F6E2FF451E097BA00083EBEC /* SettingsTableViewLinkCell.swift in Sources */, 34C9BD0A1C6DBCDA000DC38D /* MWMNavigationController.mm in Sources */, @@ -5387,6 +5400,7 @@ 3404165C1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift in Sources */, 34D4FA631E26572D003F53EF /* FirstLaunchController.swift in Sources */, 3404756E1E081A4600C92850 /* MWMSearch.mm in Sources */, + F63B24301E890C8F008C9D3C /* CacheableFacebookBanner.swift in Sources */, 6741AA191BF340DE002C974C /* MWMDownloaderDialogCell.mm in Sources */, F6E2FEFD1E097BA00083EBEC /* MWMSearchCategoryCell.mm in Sources */, 3486B5081E27A4B50069C126 /* LocalNotificationManager.mm in Sources */, @@ -5535,6 +5549,7 @@ F6E2FD601E097BA00083EBEC /* MWMMapDownloaderLargeCountryTableViewCell.mm in Sources */, F6558DA31E642CC0002203AE /* MWMFacilitiesController.mm in Sources */, F6E2FF491E097BA00083EBEC /* SettingsTableViewSelectableCell.swift in Sources */, + F62459311E80185500E7AE9E /* BannersCache.swift in Sources */, 349D1AD01E2E325B004A2006 /* MWMBottomMenuCollectionViewCell.mm in Sources */, F6E2FF281E097BA00083EBEC /* MWMSearchTabButtonsView.mm in Sources */, F6E2FE921E097BA00083EBEC /* MWMPlacePageTaxiCell.mm in Sources */, @@ -5667,6 +5682,7 @@ 3404165D1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift in Sources */, 340475571E081A4600C92850 /* Statistics.mm in Sources */, 3486B5091E27A4B50069C126 /* LocalNotificationManager.mm in Sources */, + F63B24311E890C8F008C9D3C /* CacheableFacebookBanner.swift in Sources */, F6E2FEFE1E097BA00083EBEC /* MWMSearchCategoryCell.mm in Sources */, 3404754B1E081A4600C92850 /* AppInfo.mm in Sources */, 849CF73C1DE842290024A8A5 /* MWMMultilineLabel.mm in Sources */, diff --git a/iphone/Maps/UI/PlacePage/BannersCache.swift b/iphone/Maps/UI/PlacePage/BannersCache.swift new file mode 100644 index 0000000000..1dc47fe624 --- /dev/null +++ b/iphone/Maps/UI/PlacePage/BannersCache.swift @@ -0,0 +1,96 @@ +import Crashlytics + +// MARK: Cacheable protocol +protocol Cacheable { + + typealias EventName = String + typealias ErrorDetails = [String : Any] + typealias Success = (Cacheable) -> Void + typealias Failure = (EventName, ErrorDetails, NSError) -> Void + + func reload(_ success: @escaping Success, failure: @escaping Failure) + func bannerIsOutOfScreen() + func bannerIsOnScreen() + + var isNeedToRetain: Bool { get } + var isPossibleToReload: Bool { get } + var adID: String { get } + var isBannerOnScreen: Bool { get } +} + +// MARK: BannersCache +@objc (MWMBannersCache) +final class BannersCache: NSObject { + + static let cache = BannersCache() + private override init() {} + + private var cache: [String : Cacheable] = [:] + private var requests: Set = [] + + typealias Completion = (Any, Bool) -> Void + private var completion: Completion? + + func get(_ key: String, completion comp: @escaping Completion) { + completion = comp + + + func onBannerFinished(_ cacheable: Cacheable, isAsync: Bool) { + if let compl = completion { + compl(cacheable, isAsync) + cacheable.bannerIsOnScreen() + } + } + + if let cached = cache[key] { + onBannerFinished(cached, isAsync: false) + completion = nil; + + if !cached.isPossibleToReload || cached.isNeedToRetain { + return + } + } + + if requests.contains(key) { + return + } + + let banner = CacheableFacebookBanner(placementID: key) + + banner.reload({ [weak self] cacheable in + guard let s = self else { return } + Statistics.logEvent(kStatPlacePageBannerShow, + withParameters: [kStatBanner : key, kStatProvider : kStatFacebook]) + + s.add(cacheable) + s.removeRequest(atKey: key) + onBannerFinished(cacheable, isAsync: true) + }, failure: { [weak self] event, errorDetails, error in + guard let s = self else { return } + Statistics.logEvent(event, withParameters: errorDetails) + Crashlytics.sharedInstance().recordError(error) + + s.removeRequest(atKey: key) + }) + + addRequest(key) + } + + func bannerIsOutOfScreen(_ bannerID: String) { + if let cached = cache[bannerID], cached.isBannerOnScreen { + cached.bannerIsOutOfScreen() + } + } + + private func add(_ banner: Cacheable) { + cache[banner.adID] = banner + } + + private func removeRequest(atKey key: String) { + requests.remove(key) + } + + private func addRequest(_ key: String) { + requests.insert(key) + } +} diff --git a/iphone/Maps/UI/PlacePage/CacheableFacebookBanner.swift b/iphone/Maps/UI/PlacePage/CacheableFacebookBanner.swift new file mode 100644 index 0000000000..a7449f59b6 --- /dev/null +++ b/iphone/Maps/UI/PlacePage/CacheableFacebookBanner.swift @@ -0,0 +1,141 @@ +import FBAudienceNetwork + +// MARK: CacheableFacebookBanner +final class CacheableFacebookBanner: FBNativeAd, Cacheable { + + fileprivate var success: Cacheable.Success! + fileprivate var failure: Cacheable.Failure! + + func reload(_ suc: @escaping Cacheable.Success, failure fail: @escaping Cacheable.Failure) { + success = suc + failure = fail + + mediaCachePolicy = .all + delegate = self + load() + requestDate = Date() + } + + func bannerIsOutOfScreen() { + stopCountTimeOnScreen() + isBannerOnScreen = false + } + + func bannerIsOnScreen() { + isBannerOnScreen = true + startCountTimeOnScreen() + } + + var isPossibleToReload: Bool { + if let date = requestDate { + return Date().timeIntervalSince(date) > Limits.minTimeSinceLastRequest + } + return true + } + + var isNeedToRetain: Bool = true + var adID: String { return placementID } + private(set) var isBannerOnScreen = false + + // MARK: Helpers + private var requestDate: Date? + private var remainingTime = Limits.minTimeOnScreen + private var loadBannerDate: Date? + + private enum Limits { + static let minTimeOnScreen: TimeInterval = 3 + static let minTimeSinceLastRequest: TimeInterval = 30 + } + + private func startCountTimeOnScreen() { + if loadBannerDate == nil { + loadBannerDate = Date() + } + + if (remainingTime > 0) { + perform(#selector(setEnoughTimeOnScreen), with: nil, afterDelay: remainingTime) + } + } + + private func stopCountTimeOnScreen() { + guard let date = loadBannerDate else { + assert(false) + return + } + + let timePassed = Date().timeIntervalSince(date) + if (timePassed < Limits.minTimeOnScreen) { + remainingTime = Limits.minTimeOnScreen - timePassed + NSObject.cancelPreviousPerformRequests(withTarget: self) + } else { + remainingTime = 0 + } + } + + @objc private func setEnoughTimeOnScreen() { + isNeedToRetain = false + } + + override init(placementID: String) { + super.init(placementID: placementID) + let center = NotificationCenter.default + center.addObserver(self, + selector: #selector(enterForeground), + name: .UIApplicationWillEnterForeground, + object: nil) + center.addObserver(self, + selector: #selector(enterBackground), + name: .UIApplicationDidEnterBackground, + object: nil) + } + + @objc private func enterForeground() { + if isBannerOnScreen { + startCountTimeOnScreen() + } + } + + @objc private func enterBackground() { + if (isBannerOnScreen) { + stopCountTimeOnScreen() + } + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + +} + +// MARK: CacheableFaceebookBanner: FBNativeAdDelegate +extension CacheableFacebookBanner: FBNativeAdDelegate { + + func nativeAdDidLoad(_ nativeAd: FBNativeAd) { + success(self) + } + + func nativeAd(_ nativeAd: FBNativeAd, didFailWithError error: Error) { + + // https://developers.facebook.com/docs/audience-network/testing + var params: [String: Any] = [kStatBanner : nativeAd.placementID, kStatProvider : kStatFacebook] + + let e = error as NSError + let event: String + if e.code == 1001 { + event = kStatPlacePageBannerBlank + } else { + event = kStatPlacePageBannerError + params[kStatErrorCode] = e.code + + var message: String = "" + for (k, v) in e.userInfo { + message += "\(k) : \(v)\n" + } + + params[kStatErrorMessage] = message + } + + failure(event, params, e) + } +} + diff --git a/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm b/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm index 9fe9a76c49..1475b7a96e 100644 --- a/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm +++ b/iphone/Maps/UI/PlacePage/MWMPlacePageData.mm @@ -23,7 +23,7 @@ NSString * const kUserDefaultsLatLonAsDMSKey = @"UserDefaultsLatLonAsDMS"; using namespace place_page; -@interface MWMPlacePageData () +@interface MWMPlacePageData () @property(copy, nonatomic) NSString * cachedMinPrice; @property(nonatomic) FBNativeAd * nativeAd; @@ -103,11 +103,17 @@ using namespace place_page; m_previewRows.push_back(PreviewRows::Space); if (network_policy::CanUseNetwork() && ![MWMSettings adForbidden] && m_info.HasBanner()) { - self.nativeAd = - [[FBNativeAd alloc] initWithPlacementID:@(m_info.GetBanner().m_bannerId.c_str())]; - self.nativeAd.delegate = self; - self.nativeAd.mediaCachePolicy = FBNativeAdsCachePolicyAll; - [self.nativeAd loadAd]; + __weak auto wSelf = self; + [[MWMBannersCache cache] get:@(m_info.GetBanner().m_bannerId.c_str()) completion:^(FBNativeAd * ad, BOOL isAsync) { + __strong auto self = wSelf; + if (!self) + return; + + self.nativeAd = ad; + self->m_previewRows.push_back(PreviewRows::Banner); + if (isAsync) + self.bannerIsReadyCallback(); + }]; } } @@ -293,6 +299,12 @@ using namespace place_page; } } +- (void)dealloc +{ + if (m_info.HasBanner()) + [[MWMBannersCache cache] bannerIsOutOfScreen:@(m_info.GetBanner().m_bannerId.c_str())]; +} + #pragma mark - Getters - (storage::TCountryId const &)countryId { return m_info.m_countryId; } @@ -524,47 +536,4 @@ using namespace place_page; return [result componentsJoinedByString:@", "]; } -#pragma mark - FBNativeAdDelegate - -- (void)nativeAdDidLoad:(FBNativeAd *)nativeAd -{ - if (![nativeAd isEqual:self.nativeAd]) - return; - - [Statistics logEvent:kStatPlacePageBannerShow - withParameters:@{ - kStatTags : self.statisticsTags, - kStatBanner : @(m_info.GetBanner().m_bannerId.c_str()), - kStatProvider : kStatFacebook - }]; - - dispatch_async(dispatch_get_main_queue(), ^{ - self->m_previewRows.push_back(PreviewRows::Banner); - self.bannerIsReadyCallback(); - }); -} - -- (void)nativeAd:(FBNativeAd *)nativeAd didFailWithError:(NSError *)error -{ - // https://developers.facebook.com/docs/audience-network/testing - NSMutableDictionary * params = [@{kStatTags : self.statisticsTags, - kStatBanner : @(m_info.GetBanner().m_bannerId.c_str()), - kStatProvider : kStatFacebook} mutableCopy]; - - NSString * event; - if (error.code == 1001) - { - event = kStatPlacePageBannerBlank; - } - else - { - event = kStatPlacePageBannerError; - params[kStatErrorCode] = @(error.code); - if (NSString * value = error.userInfo[@"FBAdErrorDetailKey"]) - params[kStatErrorMessage] = value; - } - - [Statistics logEvent:event withParameters:params]; -} - @end diff --git a/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm b/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm index b87a33ce6f..aa06f904d6 100644 --- a/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm +++ b/iphone/Maps/UI/PlacePage/MWMPlacePageManager.mm @@ -87,6 +87,7 @@ void logSponsoredEvent(MWMPlacePageData * data, NSString * eventName) - (void)close { [self.layout close]; + self.data = nil; [MWMLocationManager removeObserver:self]; [MWMFrameworkListener removeObserver:self]; }