[ios] attempt to implement cpp/swift inerop

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
This commit is contained in:
Kiryl Kaveryn 2025-01-04 21:56:38 +04:00
parent 95e797cbb9
commit c018e8a226
15 changed files with 79 additions and 76 deletions

View file

@ -18,7 +18,7 @@ typedef NS_ENUM(NSInteger, ProductsPopupCloseReason) {
NS_ASSUME_NONNULL_BEGIN
typedef void (^SearchInDownloaderCompletions)(NSArray<MWMMapSearchResult *> *results, BOOL finished);
typedef void (^TrackRecordingUpdatedHandler)(TrackInfo * _Nonnull trackInfo);
typedef void (^TrackRecordingUpdatedHandler)(struct GpsTrackInfo trackInfo);
@protocol TrackRecorder <NSObject>

View file

@ -225,8 +225,7 @@ static Framework::ProductsPopupCloseReason ConvertProductPopupCloseReasonToCore(
return;
}
GetFramework().SetTrackRecordingUpdateHandler([trackRecordingDidUpdate](GpsTrackInfo const & gpsTrackInfo) {
TrackInfo * info = [[TrackInfo alloc] initWithGpsTrackInfo:gpsTrackInfo];
trackRecordingDidUpdate(info);
trackRecordingDidUpdate(gpsTrackInfo);
});
}

View file

@ -16,6 +16,7 @@
@property(nonatomic) BOOL zoomHidden;
@property(nonatomic) BOOL sideButtonsHidden;
@property(nonatomic) BOOL trafficButtonHidden;
@property(nonatomic) BOOL trackRecordingButtonHidden;
@property(nonatomic) MWMBottomMenuState menuState;
@property(nonatomic) MWMBottomMenuState menuRestoreState;
@property(nonatomic) BOOL isDirectionViewHidden;

View file

@ -64,16 +64,10 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue";
self.menuState = MWMBottomMenuStateInactive;
self.menuRestoreState = MWMBottomMenuStateInactive;
self.isAddingPlace = NO;
[TrackRecordingManager.shared addObserver:self recordingIsActiveDidChangeHandler:^(TrackRecordingState state, TrackInfo * trackInfo) {
[self setTrackRecordingButtonHidden:state == TrackRecordingStateInactive];
}];
[self setTrackRecordingButtonHidden:TrackRecordingManager.shared.recordingState == TrackRecordingStateInactive];
return self;
}
- (void)dealloc {
[TrackRecordingManager.shared removeObserver:self];
}
- (UIStatusBarStyle)preferredStatusBarStyle {
BOOL const isSearchUnderStatusBar = (self.searchManager.state != MWMSearchManagerStateHidden);
BOOL const isNavigationUnderStatusBar = self.navigationManager.state != MWMNavigationDashboardStateHidden &&
@ -326,9 +320,11 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue";
[MWMMapWidgetsHelper updateLayoutForAvailableArea];
}];
_trackRecordingButton = nil;
_trackRecordingButtonHidden = true;
}
else if (!trackRecordingButtonHidden && !_trackRecordingButton) {
_trackRecordingButton = [[TrackRecordingViewController alloc] init];
_trackRecordingButtonHidden = false;
[MWMMapWidgetsHelper updateLayoutForAvailableArea];
}
}

View file

@ -70,7 +70,7 @@
auto const viewWidth = [MapViewController sharedController].mapView.width;
auto const rulerOffset =
m2::PointF(frame.origin.x * vs, (frame.origin.y + frame.size.height - viewHeight) * vs);
auto const kCompassAdditionalYOffset = [TrackRecordingManager.shared isActive] ? 50 : 0;
auto const kCompassAdditionalYOffset = TrackRecordingManager.shared.recordingState == TrackRecordingStateActive ? 50 : 0;
auto const compassOffset =
m2::PointF((frame.origin.x + frame.size.width - viewWidth) * vs, (frame.origin.y + kCompassAdditionalYOffset) * vs);
m_skin->ForEach([&](gui::EWidget w, gui::Position const & pos) {

View file

@ -1,10 +1,11 @@
import ActivityKit
import map
#if canImport(ActivityKit)
protocol TrackRecordingActivityManager {
func start(with info: TrackInfo) throws
func update(_ info: TrackInfo)
func start(with info: GpsTrackInfo) throws
func update(_ info: GpsTrackInfo)
func stop()
}
@ -22,7 +23,7 @@ final class TrackRecordingLiveActivityManager {
@available(iOS 16.2, *)
extension TrackRecordingLiveActivityManager: TrackRecordingActivityManager {
func start(with info: TrackInfo) throws {
func start(with info: GpsTrackInfo) throws {
guard activity == nil else { return }
let state = TrackRecordingLiveActivityAttributes.ContentState(trackInfo: info)
let content = ActivityContent<TrackRecordingLiveActivityAttributes.ContentState>(state: state, staleDate: nil)
@ -30,7 +31,7 @@ extension TrackRecordingLiveActivityManager: TrackRecordingActivityManager {
activity = try LiveActivityManager.startActivity(attributes, content: content)
}
func update(_ info: TrackInfo) {
func update(_ info: GpsTrackInfo) {
guard let activity else { return }
let state = TrackRecordingLiveActivityAttributes.ContentState(trackInfo: info)
let content = ActivityContent<TrackRecordingLiveActivityAttributes.ContentState>(state: state, staleDate: nil)
@ -47,13 +48,13 @@ extension TrackRecordingLiveActivityManager: TrackRecordingActivityManager {
// MARK: - Wrap TrackRecordingInfo to TrackRecordingLiveActivityAttributes.ContentState
private extension TrackRecordingLiveActivityAttributes.ContentState {
init(trackInfo: TrackInfo) {
let distance = DistanceFormatter.distanceString(fromMeters: trackInfo.distance)
let duration = DurationFormatter.durationString(from: trackInfo.duration)
let maxElevation = AltitudeFormatter.altitudeString(fromMeters: Double(trackInfo.maxElevation))
let minElevation = AltitudeFormatter.altitudeString(fromMeters: Double(trackInfo.minElevation))
let ascent = AltitudeFormatter.altitudeString(fromMeters: Double(trackInfo.ascent))
let descent = AltitudeFormatter.altitudeString(fromMeters: Double(trackInfo.descent))
init(trackInfo: GpsTrackInfo) {
let distance = DistanceFormatter.distanceString(fromMeters: trackInfo.m_length)
let duration = DurationFormatter.durationString(from: trackInfo.m_duration)
let maxElevation = AltitudeFormatter.altitudeString(fromMeters: Double(trackInfo.m_maxElevation))
let minElevation = AltitudeFormatter.altitudeString(fromMeters: Double(trackInfo.m_minElevation))
let ascent = AltitudeFormatter.altitudeString(fromMeters: Double(trackInfo.m_ascent))
let descent = AltitudeFormatter.altitudeString(fromMeters: Double(trackInfo.m_descent))
self.distance = StatisticsViewModel(key: "", value: distance)
self.duration = StatisticsViewModel(key: "", value: duration)

View file

@ -1,3 +1,7 @@
import CoreApi
import map
import kml
@objc
enum TrackRecordingState: Int, Equatable {
case inactive
@ -13,12 +17,15 @@ enum TrackRecordingError: Error {
case locationIsProhibited
}
protocol TrackRecordingObserver: AnyObject {
func addObserver(_ observer: AnyObject, recordingIsActiveDidChangeHandler: @escaping TrackRecordingStateHandler)
func removeObserver(_ observer: AnyObject)
protocol TrackRecordingObservable: AnyObject {
func addObserver(_ observer: TrackRecordingObserver)
func removeObserver(_ observer: TrackRecordingObserver)
}
typealias TrackRecordingStateHandler = (TrackRecordingState, TrackInfo?) -> Void
protocol TrackRecordingObserver: AnyObject {
func trackRecordingStateDidChange(_ state: TrackRecordingState)
func trackRecordingProgressDidChange(_ trackRecordingInfo: GpsTrackInfo)
}
@objcMembers
final class TrackRecordingManager: NSObject {
@ -30,11 +37,6 @@ final class TrackRecordingManager: NSObject {
case saveWithName(String? = nil)
}
fileprivate struct Observation {
weak var observer: AnyObject?
var recordingStateDidChangeHandler: TrackRecordingStateHandler?
}
static let shared: TrackRecordingManager = {
let trackRecorder = FrameworkHelper.self
var activityManager: TrackRecordingActivityManager? = nil
@ -48,8 +50,8 @@ final class TrackRecordingManager: NSObject {
private let trackRecorder: TrackRecorder.Type
private var activityManager: TrackRecordingActivityManager?
private var observations: [Observation] = []
private var trackRecordingInfo: TrackInfo?
private let listenerContainer = ListenerContainer<TrackRecordingObserver>()
private var trackRecordingInfo = GpsTrackInfo()
var recordingState: TrackRecordingState {
trackRecorder.isTrackRecordingEnabled() ? .active : .inactive
@ -60,6 +62,8 @@ final class TrackRecordingManager: NSObject {
self.activityManager = activityManager
super.init()
subscribeOnAppLifecycleEvents()
map.GpsTrackCollection()
}
// MARK: - Public methods
@ -79,11 +83,6 @@ final class TrackRecordingManager: NSObject {
}
}
@objc
func isActive() -> Bool {
recordingState == .active
}
func processAction(_ action: TrackRecordingAction, completion: (CompletionHandler)? = nil) {
switch action {
case .start:
@ -113,7 +112,7 @@ final class TrackRecordingManager: NSObject {
private func willResignActive() {
guard let activityManager, recordingState == .active else { return }
do {
try activityManager.start(with: trackRecordingInfo ?? .empty())
try activityManager.start(with: trackRecordingInfo)
} catch {
handleError(error)
}
@ -135,14 +134,14 @@ final class TrackRecordingManager: NSObject {
trackRecorder.setTrackRecordingUpdateHandler { [weak self] info in
guard let self else { return }
self.trackRecordingInfo = info
self.notifyObservers()
self.activityManager?.update(info)
self.listenerContainer.forEach { $0.trackRecordingProgressDidChange(info) }
}
}
private func unsubscribeFromTrackRecordingProgressUpdates() {
trackRecorder.setTrackRecordingUpdateHandler(nil)
trackRecordingInfo = nil
trackRecordingInfo = GpsTrackInfo()
}
// MARK: - Handle Start/Stop event and Errors
@ -154,7 +153,10 @@ final class TrackRecordingManager: NSObject {
case .inactive:
subscribeOnTrackRecordingProgressUpdates()
trackRecorder.startTrackRecording()
notifyObservers()
listenerContainer.forEach {
$0.trackRecordingStateDidChange(self.recordingState)
$0.trackRecordingProgressDidChange(self.trackRecordingInfo)
}
case .active:
break
}
@ -188,8 +190,9 @@ final class TrackRecordingManager: NSObject {
unsubscribeFromTrackRecordingProgressUpdates()
trackRecorder.stopTrackRecording()
activityManager?.stop()
notifyObservers()
listenerContainer.forEach {
$0.trackRecordingStateDidChange(self.recordingState)
}
switch savingOption {
case .withoutSaving:
break
@ -222,23 +225,16 @@ final class TrackRecordingManager: NSObject {
}
}
// MARK: - TrackRecordingObserver
// MARK: - TrackRecordingObservable
extension TrackRecordingManager: TrackRecordingObserver {
@objc
func addObserver(_ observer: AnyObject, recordingIsActiveDidChangeHandler: @escaping TrackRecordingStateHandler) {
let observation = Observation(observer: observer, recordingStateDidChangeHandler: recordingIsActiveDidChangeHandler)
observations.append(observation)
recordingIsActiveDidChangeHandler(recordingState, trackRecordingInfo)
extension TrackRecordingManager: TrackRecordingObservable {
func addObserver(_ observer: TrackRecordingObserver) {
listenerContainer.addListener(observer)
observer.trackRecordingStateDidChange(recordingState)
observer.trackRecordingProgressDidChange(trackRecordingInfo)
}
@objc
func removeObserver(_ observer: AnyObject) {
observations.removeAll { $0.observer === observer }
}
private func notifyObservers() {
observations = observations.filter { $0.observer != nil }
observations.forEach { $0.recordingStateDidChangeHandler?(recordingState, trackRecordingInfo) }
func removeObserver(_ observer: TrackRecordingObserver) {
listenerContainer.removeListener(observer)
}
}

View file

@ -81,6 +81,7 @@ extension BottomMenuInteractor: BottomMenuInteractorProtocol {
func toggleTrackRecording() {
trackRecorder.processAction(trackRecorder.recordingState == .active ? .stop : .start) { [weak self] in
self?.close()
MWMMapViewControlsManager.manager().trackRecordingButtonHidden = false
}
}
}

4
kml/module.modulemap Normal file
View file

@ -0,0 +1,4 @@
module kml {
header "types.hpp"
export *
}

View file

@ -319,10 +319,10 @@ Framework::Framework(FrameworkParams const & params, bool loadMaps)
m_stringsBundle.SetDefaultString("postal_code", "Postal Code");
m_featuresFetcher.InitClassificator();
m_featuresFetcher.SetOnMapDeregisteredCallback(bind(&Framework::OnMapDeregistered, this, _1));
m_featuresFetcher.SetOnMapDeregisteredCallback(std::bind(&Framework::OnMapDeregistered, this, _1));
LOG(LDEBUG, ("Classificator initialized"));
m_displayedCategories = make_unique<search::DisplayedCategories>(GetDefaultCategories());
m_displayedCategories = std::make_unique<search::DisplayedCategories>(GetDefaultCategories());
// To avoid possible races - init country info getter in constructor.
InitCountryInfoGetter();
@ -331,14 +331,14 @@ Framework::Framework(FrameworkParams const & params, bool loadMaps)
InitSearchAPI(params.m_numSearchAPIThreads);
LOG(LDEBUG, ("Search API initialized, part 1"));
m_bmManager = make_unique<BookmarkManager>(BookmarkManager::Callbacks(
m_bmManager = std::make_unique<BookmarkManager>(BookmarkManager::Callbacks(
[this]() -> StringsBundle const & { return m_stringsBundle; },
[this]() -> SearchAPI & { return GetSearchAPI(); },
[this](vector<BookmarkInfo> const & marks) { GetSearchAPI().OnBookmarksCreated(marks); },
[this](vector<BookmarkInfo> const & marks) { GetSearchAPI().OnBookmarksUpdated(marks); },
[this](vector<kml::MarkId> const & marks) { GetSearchAPI().OnBookmarksDeleted(marks); },
[this](vector<BookmarkGroupInfo> const & marks) { GetSearchAPI().OnBookmarksAttached(marks); },
[this](vector<BookmarkGroupInfo> const & marks) { GetSearchAPI().OnBookmarksDetached(marks); }));
[this](std::vector<BookmarkInfo> const & marks) { GetSearchAPI().OnBookmarksCreated(marks); },
[this](std::vector<BookmarkInfo> const & marks) { GetSearchAPI().OnBookmarksUpdated(marks); },
[this](std::vector<kml::MarkId> const & marks) { GetSearchAPI().OnBookmarksDeleted(marks); },
[this](std::vector<BookmarkGroupInfo> const & marks) { GetSearchAPI().OnBookmarksAttached(marks); },
[this](std::vector<BookmarkGroupInfo> const & marks) { GetSearchAPI().OnBookmarksDetached(marks); }));
m_bmManager->InitRegionAddressGetter(m_featuresFetcher.GetDataSource(), *m_infoGetter);

View file

@ -2,7 +2,7 @@
#include "map/api_mark_point.hpp"
#include "map/bookmark.hpp"
#include "map/bookmark_manager.hpp"
//#include "map/bookmark_manager.hpp"
#include "map/features_fetcher.hpp"
#include "map/isolines_manager.hpp"
#include "map/mwm_url.hpp"

View file

@ -1,10 +1,11 @@
module map {
// header "framework.hpp"
// header "bookmark_manager.hpp"
header "bookmark.hpp"
header "track.hpp"
header "elevation_info.hpp"
header "gps_track_collection.hpp"
header "track.hpp"
header "bookmark.hpp"
header "user_mark.hpp"
header "user_mark_layer.hpp"
header "elevation_info.hpp"
export *
}

View file

@ -18,6 +18,8 @@
#include <string>
#include <vector>
class Track;
namespace place_page
{
enum class OpeningMode

View file

@ -71,6 +71,7 @@
E2DC9C8C25264E0C0098174E /* types_v6.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = types_v6.hpp; sourceTree = "<group>"; };
E2DC9C8D25264E0C0098174E /* types_v8.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = types_v8.hpp; sourceTree = "<group>"; };
E2DC9C9025264E3E0098174E /* types.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = types.cpp; sourceTree = "<group>"; };
EDCA7CC12D29AA9C003366CE /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
FA67C84A26BB365600B33DCA /* libplatform.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libplatform.a; sourceTree = BUILT_PRODUCTS_DIR; };
FA67C84C26BB365C00B33DCA /* libbase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libbase.a; sourceTree = BUILT_PRODUCTS_DIR; };
FA67C85026BB370C00B33DCA /* libcoding.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libcoding.a; sourceTree = BUILT_PRODUCTS_DIR; };
@ -154,6 +155,7 @@
E2DC9C9025264E3E0098174E /* types.cpp */,
45E4559420584ABA00D9F45E /* types.hpp */,
45E4559120584ABA00D9F45E /* visitors.hpp */,
EDCA7CC12D29AA9C003366CE /* module.modulemap */,
);
name = kml;
path = ../../kml;

View file

@ -57,9 +57,9 @@
675346481A4054E800A0A8C3 /* bookmark_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 675345D91A4054E800A0A8C3 /* bookmark_manager.cpp */; };
675346491A4054E800A0A8C3 /* bookmark_manager.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 675345DA1A4054E800A0A8C3 /* bookmark_manager.hpp */; };
6753464A1A4054E800A0A8C3 /* bookmark.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 675345DB1A4054E800A0A8C3 /* bookmark.cpp */; };
6753464B1A4054E800A0A8C3 /* bookmark.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 675345DC1A4054E800A0A8C3 /* bookmark.hpp */; };
6753464B1A4054E800A0A8C3 /* bookmark.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 675345DC1A4054E800A0A8C3 /* bookmark.hpp */; settings = {ATTRIBUTES = (Public, ); }; };
675346641A4054E800A0A8C3 /* framework.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 675345F51A4054E800A0A8C3 /* framework.cpp */; };
675346651A4054E800A0A8C3 /* framework.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 675345F61A4054E800A0A8C3 /* framework.hpp */; };
675346651A4054E800A0A8C3 /* framework.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 675345F61A4054E800A0A8C3 /* framework.hpp */; settings = {ATTRIBUTES = (Public, ); }; };
675346741A4054E800A0A8C3 /* mwm_url.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 675346051A4054E800A0A8C3 /* mwm_url.cpp */; };
675346751A4054E800A0A8C3 /* mwm_url.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 675346061A4054E800A0A8C3 /* mwm_url.hpp */; };
6753469B1A4054E800A0A8C3 /* track.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6753462C1A4054E800A0A8C3 /* track.cpp */; };
@ -239,8 +239,8 @@
BBD9E2C51EE9D01900DF189A /* routing_mark.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = routing_mark.hpp; sourceTree = "<group>"; };
BBFC7E38202D29BF00531BE7 /* user_mark_layer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user_mark_layer.cpp; sourceTree = "<group>"; };
BBFC7E39202D29BF00531BE7 /* user_mark_layer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = user_mark_layer.hpp; sourceTree = "<group>"; };
ED2EB95B2D297E8200062C31 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
ED49D74B2CEF3CE3004AF27E /* elevation_info_tests.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = elevation_info_tests.cpp; sourceTree = "<group>"; };
ED79CAC32D28259F00E63864 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
F6B282FB1C1B03320081957A /* gps_track_collection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gps_track_collection.cpp; sourceTree = "<group>"; };
F6B282FC1C1B03320081957A /* gps_track_collection.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = gps_track_collection.hpp; sourceTree = "<group>"; };
F6B282FD1C1B03320081957A /* gps_track_filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gps_track_filter.cpp; sourceTree = "<group>"; };
@ -457,7 +457,7 @@
3DA5722F20C195EC007BDE27 /* viewport_search_callback.cpp */,
3DA5722C20C195EC007BDE27 /* viewport_search_callback.hpp */,
3D4E99811FB462B60025B48C /* viewport_search_params.hpp */,
ED79CAC32D28259F00E63864 /* module.modulemap */,
ED2EB95B2D297E8200062C31 /* module.modulemap */,
);
name = map;
path = ../../map;