Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,417 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
472644DE24400C0400B9C053 /* ExpandedTouchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472644DD24400C0300B9C053 /* ExpandedTouchView.swift */; };
47375E3C2420E94E00FFCC49 /* ChartPresentationData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47375E392420E94C00FFCC49 /* ChartPresentationData.swift */; };
47375E3E2420E94E00FFCC49 /* ChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47375E3B2420E94E00FFCC49 /* ChartData.swift */; };
47375E4B2420E97100FFCC49 /* ChartXAxisView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47375E3F2420E96C00FFCC49 /* ChartXAxisView.swift */; };
47375E4C2420E97100FFCC49 /* ChartLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47375E402420E96D00FFCC49 /* ChartLineView.swift */; };
47375E4D2420E97100FFCC49 /* ChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47375E412420E96D00FFCC49 /* ChartView.swift */; };
47375E4E2420E97100FFCC49 /* ChartInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47375E432420E96F00FFCC49 /* ChartInfoView.swift */; };
47375E4F2420E97100FFCC49 /* ChartPointInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47375E442420E96F00FFCC49 /* ChartPointInfoView.swift */; };
47375E502420E97100FFCC49 /* ChartPointIntersectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47375E452420E96F00FFCC49 /* ChartPointIntersectionView.swift */; };
47375E512420E97100FFCC49 /* ChartMyPositionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47375E462420E96F00FFCC49 /* ChartMyPositionView.swift */; };
47375E522420E97100FFCC49 /* ChartYAxisView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47375E472420E96F00FFCC49 /* ChartYAxisView.swift */; };
47375E542420E97100FFCC49 /* ChartPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47375E492420E97100FFCC49 /* ChartPreviewView.swift */; };
47D48BD324302FE200FEFB1F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 47D48BD524302FE200FEFB1F /* Localizable.strings */; };
ED46DE1E2D09B8A6007CACD6 /* ChartPathBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED46DE1C2D09B8A6007CACD6 /* ChartPathBuilder.swift */; };
ED46DE1F2D09B8A6007CACD6 /* ChartPresentationLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED46DE1D2D09B8A6007CACD6 /* ChartPresentationLine.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
472644DD24400C0300B9C053 /* ExpandedTouchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandedTouchView.swift; sourceTree = "<group>"; };
47375D962420D4DB00FFCC49 /* Chart.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Chart.framework; sourceTree = BUILT_PRODUCTS_DIR; };
47375D9A2420D4DB00FFCC49 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
47375E392420E94C00FFCC49 /* ChartPresentationData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartPresentationData.swift; sourceTree = "<group>"; };
47375E3B2420E94E00FFCC49 /* ChartData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartData.swift; sourceTree = "<group>"; };
47375E3F2420E96C00FFCC49 /* ChartXAxisView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxisView.swift; sourceTree = "<group>"; };
47375E402420E96D00FFCC49 /* ChartLineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartLineView.swift; sourceTree = "<group>"; };
47375E412420E96D00FFCC49 /* ChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartView.swift; sourceTree = "<group>"; };
47375E432420E96F00FFCC49 /* ChartInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartInfoView.swift; sourceTree = "<group>"; };
47375E442420E96F00FFCC49 /* ChartPointInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartPointInfoView.swift; sourceTree = "<group>"; };
47375E452420E96F00FFCC49 /* ChartPointIntersectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartPointIntersectionView.swift; sourceTree = "<group>"; };
47375E462420E96F00FFCC49 /* ChartMyPositionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartMyPositionView.swift; sourceTree = "<group>"; };
47375E472420E96F00FFCC49 /* ChartYAxisView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartYAxisView.swift; sourceTree = "<group>"; };
47375E492420E97100FFCC49 /* ChartPreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartPreviewView.swift; sourceTree = "<group>"; };
47D48BD6243030A200FEFB1F /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Localizable.strings"; sourceTree = "<group>"; };
47D48BD7243030B900FEFB1F /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
47D48BD8243030C300FEFB1F /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
47D48BD9243030CB00FEFB1F /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BDA243030DF00FEFB1F /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BDB243030EB00FEFB1F /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BDC243030F300FEFB1F /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BDD2430310000FEFB1F /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BDE2430312400FEFB1F /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BDF2430312C00FEFB1F /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BE02430313E00FEFB1F /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BE1243031A900FEFB1F /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BE2243031C700FEFB1F /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BE3243031D300FEFB1F /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BE4243031DC00FEFB1F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BE5243031EE00FEFB1F /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BE6243031F600FEFB1F /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BE72430320100FEFB1F /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BE82430320F00FEFB1F /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BEA243032D800FEFB1F /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BEB243032DF00FEFB1F /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BEC243032E700FEFB1F /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BED2430330B00FEFB1F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BEE2430332200FEFB1F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BEF2430332800FEFB1F /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BF02430333000FEFB1F /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BF12430333900FEFB1F /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BF22430334B00FEFB1F /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
47D48BF32430335F00FEFB1F /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
ED46DE1C2D09B8A6007CACD6 /* ChartPathBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartPathBuilder.swift; sourceTree = "<group>"; };
ED46DE1D2D09B8A6007CACD6 /* ChartPresentationLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartPresentationLine.swift; sourceTree = "<group>"; };
FA4C8E53263B1FA80048FA99 /* common-release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "common-release.xcconfig"; path = "../../xcode/common-release.xcconfig"; sourceTree = "<group>"; };
FA4C8E54263B1FA80048FA99 /* common-debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "common-debug.xcconfig"; path = "../../xcode/common-debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
47375D932420D4DB00FFCC49 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
47375D8C2420D4DB00FFCC49 = {
isa = PBXGroup;
children = (
FA4C8E54263B1FA80048FA99 /* common-debug.xcconfig */,
FA4C8E53263B1FA80048FA99 /* common-release.xcconfig */,
47375D982420D4DB00FFCC49 /* Chart */,
47375D972420D4DB00FFCC49 /* Products */,
);
indentWidth = 4;
sourceTree = "<group>";
tabWidth = 4;
};
47375D972420D4DB00FFCC49 /* Products */ = {
isa = PBXGroup;
children = (
47375D962420D4DB00FFCC49 /* Chart.framework */,
);
name = Products;
sourceTree = "<group>";
};
47375D982420D4DB00FFCC49 /* Chart */ = {
isa = PBXGroup;
children = (
47375DC22420D60200FFCC49 /* ChartData */,
47375DC72420D60300FFCC49 /* Views */,
47375D9A2420D4DB00FFCC49 /* Info.plist */,
47D48BD524302FE200FEFB1F /* Localizable.strings */,
);
path = Chart;
sourceTree = "<group>";
};
47375DC22420D60200FFCC49 /* ChartData */ = {
isa = PBXGroup;
children = (
ED46DE1C2D09B8A6007CACD6 /* ChartPathBuilder.swift */,
ED46DE1D2D09B8A6007CACD6 /* ChartPresentationLine.swift */,
47375E3B2420E94E00FFCC49 /* ChartData.swift */,
47375E392420E94C00FFCC49 /* ChartPresentationData.swift */,
);
path = ChartData;
sourceTree = "<group>";
};
47375DC72420D60300FFCC49 /* Views */ = {
isa = PBXGroup;
children = (
47375E422420E96F00FFCC49 /* ChartInfo */,
47375E412420E96D00FFCC49 /* ChartView.swift */,
47375E402420E96D00FFCC49 /* ChartLineView.swift */,
47375E492420E97100FFCC49 /* ChartPreviewView.swift */,
47375E3F2420E96C00FFCC49 /* ChartXAxisView.swift */,
47375E472420E96F00FFCC49 /* ChartYAxisView.swift */,
472644DD24400C0300B9C053 /* ExpandedTouchView.swift */,
);
path = Views;
sourceTree = "<group>";
};
47375E422420E96F00FFCC49 /* ChartInfo */ = {
isa = PBXGroup;
children = (
47375E432420E96F00FFCC49 /* ChartInfoView.swift */,
47375E442420E96F00FFCC49 /* ChartPointInfoView.swift */,
47375E452420E96F00FFCC49 /* ChartPointIntersectionView.swift */,
47375E462420E96F00FFCC49 /* ChartMyPositionView.swift */,
);
path = ChartInfo;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
47375D912420D4DB00FFCC49 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
47375D952420D4DB00FFCC49 /* Chart */ = {
isa = PBXNativeTarget;
buildConfigurationList = 47375D9E2420D4DB00FFCC49 /* Build configuration list for PBXNativeTarget "Chart" */;
buildPhases = (
47375D912420D4DB00FFCC49 /* Headers */,
47375D922420D4DB00FFCC49 /* Sources */,
47375D932420D4DB00FFCC49 /* Frameworks */,
47375D942420D4DB00FFCC49 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = Chart;
productName = Chart;
productReference = 47375D962420D4DB00FFCC49 /* Chart.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
47375D8D2420D4DB00FFCC49 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
DefaultBuildSystemTypeForWorkspace = Latest;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = CoMaps;
TargetAttributes = {
47375D952420D4DB00FFCC49 = {
CreatedOnToolsVersion = 11.3;
};
};
};
buildConfigurationList = 47375D902420D4DB00FFCC49 /* Build configuration list for PBXProject "Chart" */;
compatibilityVersion = "Xcode 12.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
"en-GB",
"zh-Hans",
"zh-Hant",
ar,
cs,
da,
de,
el,
es,
fr,
ja,
fi,
hu,
id,
it,
ko,
nb,
nl,
pl,
pt,
ro,
ru,
tr,
th,
sv,
vi,
sk,
uk,
);
mainGroup = 47375D8C2420D4DB00FFCC49;
productRefGroup = 47375D972420D4DB00FFCC49 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
47375D952420D4DB00FFCC49 /* Chart */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
47375D942420D4DB00FFCC49 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
47D48BD324302FE200FEFB1F /* Localizable.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
47375D922420D4DB00FFCC49 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
47375E512420E97100FFCC49 /* ChartMyPositionView.swift in Sources */,
47375E3E2420E94E00FFCC49 /* ChartData.swift in Sources */,
47375E4C2420E97100FFCC49 /* ChartLineView.swift in Sources */,
ED46DE1E2D09B8A6007CACD6 /* ChartPathBuilder.swift in Sources */,
ED46DE1F2D09B8A6007CACD6 /* ChartPresentationLine.swift in Sources */,
47375E502420E97100FFCC49 /* ChartPointIntersectionView.swift in Sources */,
47375E522420E97100FFCC49 /* ChartYAxisView.swift in Sources */,
47375E4B2420E97100FFCC49 /* ChartXAxisView.swift in Sources */,
47375E542420E97100FFCC49 /* ChartPreviewView.swift in Sources */,
47375E3C2420E94E00FFCC49 /* ChartPresentationData.swift in Sources */,
47375E4D2420E97100FFCC49 /* ChartView.swift in Sources */,
472644DE24400C0400B9C053 /* ExpandedTouchView.swift in Sources */,
47375E4F2420E97100FFCC49 /* ChartPointInfoView.swift in Sources */,
47375E4E2420E97100FFCC49 /* ChartInfoView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
47D48BD524302FE200FEFB1F /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
47D48BD6243030A200FEFB1F /* en-GB */,
47D48BD7243030B900FEFB1F /* zh-Hans */,
47D48BD8243030C300FEFB1F /* zh-Hant */,
47D48BD9243030CB00FEFB1F /* ar */,
47D48BDA243030DF00FEFB1F /* cs */,
47D48BDB243030EB00FEFB1F /* da */,
47D48BDC243030F300FEFB1F /* de */,
47D48BDD2430310000FEFB1F /* el */,
47D48BDE2430312400FEFB1F /* es */,
47D48BDF2430312C00FEFB1F /* fr */,
47D48BE02430313E00FEFB1F /* ja */,
47D48BE1243031A900FEFB1F /* fi */,
47D48BE2243031C700FEFB1F /* hu */,
47D48BE3243031D300FEFB1F /* id */,
47D48BE4243031DC00FEFB1F /* it */,
47D48BE5243031EE00FEFB1F /* ko */,
47D48BE6243031F600FEFB1F /* nb */,
47D48BE72430320100FEFB1F /* nl */,
47D48BE82430320F00FEFB1F /* pl */,
47D48BEA243032D800FEFB1F /* pt */,
47D48BEB243032DF00FEFB1F /* ro */,
47D48BEC243032E700FEFB1F /* ru */,
47D48BED2430330B00FEFB1F /* en */,
47D48BEE2430332200FEFB1F /* tr */,
47D48BEF2430332800FEFB1F /* th */,
47D48BF02430333000FEFB1F /* sv */,
47D48BF12430333900FEFB1F /* vi */,
47D48BF22430334B00FEFB1F /* sk */,
47D48BF32430335F00FEFB1F /* uk */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
47375D9C2420D4DB00FFCC49 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FA4C8E54263B1FA80048FA99 /* common-debug.xcconfig */;
buildSettings = {
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
SDKROOT = iphoneos;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
47375D9D2420D4DB00FFCC49 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FA4C8E53263B1FA80048FA99 /* common-release.xcconfig */;
buildSettings = {
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
47375D9F2420D4DB00FFCC49 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Chart/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = app.comaps.chart;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
};
name = Debug;
};
47375DA02420D4DB00FFCC49 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Chart/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = app.comaps.chart;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
47375D902420D4DB00FFCC49 /* Build configuration list for PBXProject "Chart" */ = {
isa = XCConfigurationList;
buildConfigurations = (
47375D9C2420D4DB00FFCC49 /* Debug */,
47375D9D2420D4DB00FFCC49 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
47375D9E2420D4DB00FFCC49 /* Build configuration list for PBXNativeTarget "Chart" */ = {
isa = XCConfigurationList;
buildConfigurations = (
47375D9F2420D4DB00FFCC49 /* Debug */,
47375DA02420D4DB00FFCC49 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 47375D8D2420D4DB00FFCC49 /* Project object */;
}

View file

@ -0,0 +1,50 @@
import UIKit
public enum ChartType {
case regular
case percentage
}
public enum ChartLineType: String {
case line
case lineArea
}
public protocol ChartFormatter {
func xAxisString(from value: Double) -> String
func yAxisString(from value: Double) -> String
func yAxisLowerBound(from value: CGFloat) -> CGFloat
func yAxisUpperBound(from value: CGFloat) -> CGFloat
func yAxisSteps(lowerBound: CGFloat, upperBound: CGFloat) -> [CGFloat]
}
public protocol ChartData {
var xAxisValues: [Double] { get }
var lines: [ChartLine] { get }
var type: ChartType { get }
}
public protocol ChartLine {
var values: [ChartValue] { get }
var color: UIColor { get }
var type: ChartLineType { get }
}
public struct ChartValue {
let x: CGFloat
let y: CGFloat
public init(xValues: CGFloat, y: CGFloat) {
self.x = xValues
self.y = y
}
}
extension Array where Element == ChartValue {
var maxDistance: CGFloat { return map { $0.x }.max() ?? 0 }
func altitude(at relativeDistance: CGFloat) -> CGFloat {
guard let distance = last?.x else { return 0 }
return first { $0.x >= distance * relativeDistance }?.y ?? 0
}
}

View file

@ -0,0 +1,131 @@
import UIKit
protocol ChartPathBuilder {
func build(_ lines: [ChartPresentationLine])
func makeLinePath(line: ChartPresentationLine) -> UIBezierPath
func makePercentLinePath(line: ChartPresentationLine, bottomLine: ChartPresentationLine?) -> UIBezierPath
}
extension ChartPathBuilder {
func makeLinePreviewPath(line: ChartPresentationLine) -> UIBezierPath {
let path = UIBezierPath()
let values = line.values
let xScale = CGFloat(values.count) / values.maxDistance
for i in 0..<values.count {
let x = values[i].x * xScale
let y = values[i].y - line.minY
if i == 0 {
path.move(to: CGPoint(x: x, y: y))
} else {
path.addLine(to: CGPoint(x: x, y: y))
}
}
return path
}
func makeLinePath(line: ChartPresentationLine) -> UIBezierPath {
let path = UIBezierPath()
let values = line.values
let xScale = CGFloat(values.count) / values.maxDistance
for i in 0..<values.count {
let x = values[i].x * xScale
let y = values[i].y - line.minY
if i == 0 {
path.move(to: CGPoint(x: x, y: y))
} else {
path.addLine(to: CGPoint(x: x, y: y))
}
}
return path
}
func makePercentLinePreviewPath(line: ChartPresentationLine, bottomLine: ChartPresentationLine?) -> UIBezierPath {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
let aggregatedValues = line.aggregatedValues
let xScale = CGFloat(aggregatedValues.count) / aggregatedValues.maxDistance
for i in 0..<aggregatedValues.count {
let x = aggregatedValues[i].x * xScale
let y = aggregatedValues[i].y - CGFloat(line.minY)
path.addLine(to: CGPoint(x: x, y: y))
}
path.addLine(to: CGPoint(x: aggregatedValues.maxDistance, y: 0))
path.close()
return path
}
func makePercentLinePath(line: ChartPresentationLine, bottomLine: ChartPresentationLine?) -> UIBezierPath {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
let aggregatedValues = line.aggregatedValues
let xScale = CGFloat(aggregatedValues.count) / aggregatedValues.maxDistance
for i in 0..<aggregatedValues.count {
let x = aggregatedValues[i].x * xScale
let y = aggregatedValues[i].y - CGFloat(line.minY)
path.addLine(to: CGPoint(x: x, y: y))
}
path.addLine(to: CGPoint(x: aggregatedValues.maxDistance, y: 0))
path.close()
return path
}
}
final class DefaultChartPathBuilder {
private let builders: [ChartType: ChartPathBuilder] = [
.regular : LinePathBuilder(),
.percentage : PercentagePathBuilder()
]
func build(_ lines: [ChartPresentationLine], type: ChartType) {
builders[type]?.build(lines)
}
}
final class LinePathBuilder: ChartPathBuilder {
func build(_ lines: [ChartPresentationLine]) {
lines.forEach {
$0.aggregatedValues = $0.values
if $0.type == .lineArea {
$0.minY = 0
for val in $0.values {
$0.maxY = max(val.y, $0.maxY)
}
$0.path = makePercentLinePath(line: $0, bottomLine: nil)
$0.previewPath = makePercentLinePreviewPath(line: $0, bottomLine: nil)
} else {
for val in $0.values {
$0.minY = min(val.y, $0.minY)
$0.maxY = max(val.y, $0.maxY)
}
$0.path = makeLinePath(line: $0)
$0.previewPath = makeLinePreviewPath(line: $0)
}
}
}
}
final class PercentagePathBuilder: ChartPathBuilder {
func build(_ lines: [ChartPresentationLine]) {
lines.forEach {
$0.minY = 0
$0.maxY = CGFloat(Int.min)
}
for i in 0..<lines[0].values.count {
let sum = CGFloat(lines.reduce(0) { (r, l) in r + l.values[i].y })
var aggrPercentage: CGFloat = 0
lines.forEach {
aggrPercentage += CGFloat($0.values[i].y) / sum * 100
$0.aggregatedValues.append(ChartValue(xValues: lines[0].values[i].x, y: aggrPercentage))
$0.maxY = max(round(aggrPercentage), CGFloat($0.maxY))
}
}
var prevLine: ChartPresentationLine? = nil
lines.forEach {
$0.path = makePercentLinePath(line: $0, bottomLine: prevLine)
$0.previewPath = makePercentLinePreviewPath(line: $0, bottomLine: prevLine)
prevLine = $0
}
}
}

View file

@ -0,0 +1,56 @@
import UIKit
public class ChartPresentationData {
private let chartData: ChartData
private var presentationLines: [ChartPresentationLine]
private let pathBuilder = DefaultChartPathBuilder()
let formatter: ChartFormatter
var linesCount: Int { chartData.lines.count }
var pointsCount: Int { chartData.xAxisValues.count }
var xAxisValues: [Double] { chartData.xAxisValues }
var type: ChartType { chartData.type }
var labels: [String]
var lower = CGFloat(Int.max)
var upper = CGFloat(Int.min)
public init(_ chartData: ChartData, formatter: ChartFormatter) {
self.chartData = chartData
self.formatter = formatter
self.presentationLines = chartData.lines.map { ChartPresentationLine($0) }
self.labels = chartData.xAxisValues.map { formatter.xAxisString(from: $0) }
recalculateBounds()
}
func labelAt(_ point: CGFloat) -> String {
formatter.xAxisString(from: xAxisValueAt(point))
}
func xAxisValueAt(_ point: CGFloat) -> Double {
let distance = chartData.xAxisValues.last!
let p1 = floor(point)
let p2 = ceil(point)
let v1 = p1 / CGFloat(pointsCount) * distance
let v2 = p2 / CGFloat(pointsCount) * distance
return v1 + (v2 - v1) * Double(point.truncatingRemainder(dividingBy: 1))
}
func lineAt(_ index: Int) -> ChartPresentationLine {
presentationLines[index]
}
private func recalculateBounds() {
presentationLines.forEach { $0.aggregatedValues = [] }
pathBuilder.build(presentationLines, type: type)
var l = CGFloat(Int.max)
var u = CGFloat(Int.min)
presentationLines.forEach {
l = min($0.minY, l)
u = max($0.maxY, u)
}
lower = l
upper = u
}
}

View file

@ -0,0 +1,19 @@
import UIKit
final class ChartPresentationLine {
private let chartLine: ChartLine
var aggregatedValues: [ChartValue] = []
var minY: CGFloat = CGFloat(Int.max)
var maxY: CGFloat = CGFloat(Int.min)
var path = UIBezierPath()
var previewPath = UIBezierPath()
var values: [ChartValue] { chartLine.values }
var color: UIColor { chartLine.color }
var type: ChartLineType { chartLine.type }
init(_ chartLine: ChartLine) {
self.chartLine = chartLine
}
}

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>

View file

@ -0,0 +1,197 @@
import UIKit
struct ChartLineInfo {
let color: UIColor
let point: CGPoint
let formattedValue: String
}
protocol ChartInfoViewDelegate: AnyObject {
func chartInfoView(_ view: ChartInfoView, infoAtPointX pointX: CGFloat) -> (String, [ChartLineInfo])?
func chartInfoView(_ view: ChartInfoView, didCaptureInfoView captured: Bool)
func chartInfoView(_ view: ChartInfoView, didMoveToPoint pointX: CGFloat)
}
class ChartInfoView: ExpandedTouchView {
weak var delegate: ChartInfoViewDelegate?
private let pointInfoView = ChartPointInfoView()
private let pointsView = ChartPointIntersectionView(frame: CGRect(x: 0, y: 0, width: 2, height: 0))
private let myPositionView = ChartMyPositionView(frame: CGRect(x: 0, y: 0, width: 2, height: 0))
private var lineInfo: ChartLineInfo?
fileprivate var captured = false {
didSet {
delegate?.chartInfoView(self, didCaptureInfoView: captured)
}
}
private var _infoX: CGFloat = 0
var infoX: CGFloat {
get { _infoX }
set {
_infoX = newValue
update(bounds.width * _infoX)
}
}
var myPositionX: CGFloat = -1 {
didSet {
if myPositionX < 0 || myPositionX > 1 {
myPositionView.isHidden = true
return
}
myPositionView.isHidden = false
updateMyPosition()
}
}
var tooltipBackgroundColor: UIColor = UIColor.white {
didSet {
pointInfoView.backgroundColor = tooltipBackgroundColor
}
}
var font: UIFont = UIFont.systemFont(ofSize: 12, weight: .regular) {
didSet {
pointInfoView.font = font
}
}
var textColor: UIColor = UIColor.black {
didSet {
pointInfoView.textColor = textColor
}
}
public var infoBackgroundColor: UIColor = UIColor.white {
didSet {
pointInfoView.backgroundColor = infoBackgroundColor
}
}
public var infoShadowColor: UIColor = UIColor.black {
didSet {
pointInfoView.layer.shadowColor = infoShadowColor.cgColor
}
}
public var infoShadowOpacity: Float = 0.25 {
didSet {
pointInfoView.layer.shadowOpacity = infoShadowOpacity
}
}
var panGR: UIPanGestureRecognizer!
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(myPositionView)
myPositionView.isHidden = true
addSubview(pointsView)
addSubview(pointInfoView)
isExclusiveTouch = true
panGR = UIPanGestureRecognizer(target: self, action: #selector(onPan(_:)))
panGR.delegate = self
addGestureRecognizer(panGR)
pointInfoView.textColor = textColor
pointInfoView.backgroundColor = tooltipBackgroundColor
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
func update(_ x: CGFloat? = nil) {
guard bounds.width > 0 else { return }
let x = x ?? pointsView.center.x
_infoX = x / bounds.width
guard let delegate = delegate,
let (label, intersectionPoints) = delegate.chartInfoView(self, infoAtPointX: x) else { return }
lineInfo = intersectionPoints[0]
pointsView.updatePoint(lineInfo!)
pointInfoView.update(x: x, label: label, points: intersectionPoints)
updateViews(point: lineInfo!.point)
}
private func updateMyPosition() {
myPositionView.center = CGPoint(x: bounds.width * myPositionX, y: myPositionView.center.y)
guard let (_, myPositionPoints) = delegate?.chartInfoView(self, infoAtPointX: myPositionView.center.x) else { return }
myPositionView.pinY = myPositionPoints[0].point.y
}
@objc func onPan(_ sender: UIPanGestureRecognizer) {
let x = sender.location(in: self).x
switch sender.state {
case .possible:
break
case .began:
guard let lineInfo = lineInfo else { return }
captured = abs(x - lineInfo.point.x) <= 22
case .changed:
if captured {
if x < bounds.minX || x > bounds.maxX {
return
}
update(x)
delegate?.chartInfoView(self, didMoveToPoint: x)
}
case .ended, .cancelled, .failed:
captured = false
@unknown default:
fatalError()
}
}
private func updateViews(point: CGPoint) {
pointsView.alpha = 1
pointsView.center = CGPoint(x: point.x, y: bounds.midY)
let s = pointInfoView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
pointInfoView.frame.size = s
let y = max(pointInfoView.frame.height / 2 + 5,
min(bounds.height - pointInfoView.frame.height / 2 - 5, bounds.height - lineInfo!.point.y));
let orientationChangeX = pointInfoView.alignment == .left ? s.width + 40 : bounds.width - s.width - 40
if point.x > orientationChangeX {
pointInfoView.alignment = .left
pointInfoView.center = CGPoint(x: point.x - s.width / 2 - 20, y: y)
} else {
pointInfoView.alignment = .right
pointInfoView.center = CGPoint(x: point.x + s.width / 2 + 20, y: y)
}
var f = pointInfoView.frame
if f.minX < 0 {
f.origin.x = 0
pointInfoView.frame = f
} else if f.minX + f.width > bounds.width {
f.origin.x = bounds.width - f.width
pointInfoView.frame = f
}
let arrowPoint = convert(CGPoint(x: 0, y: bounds.height - lineInfo!.point.y), to: pointInfoView)
pointInfoView.arrowY = arrowPoint.y
}
override func layoutSubviews() {
super.layoutSubviews()
var pf = pointsView.frame
pf.origin.y = bounds.minY
pf.size.height = bounds.height
pointsView.frame = pf
var mf = myPositionView.frame
mf.origin.y = bounds.minY
mf.size.height = bounds.height
myPositionView.frame = mf
update(bounds.width * infoX)
updateMyPosition()
}
}
extension ChartInfoView: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return !captured
}
}

View file

@ -0,0 +1,94 @@
import UIKit
class ChartMyPositionView: UIView {
override class var layerClass: AnyClass { CAShapeLayer.self }
var shapeLayer: CAShapeLayer { layer as! CAShapeLayer }
var pinY: CGFloat = 0 {
didSet {
updatePin()
}
}
fileprivate let pinView = MyPositionPinView(frame: CGRect(x: 0, y: 0, width: 12, height: 16))
override init(frame: CGRect) {
super.init(frame: frame)
shapeLayer.lineDashPattern = [3, 2]
shapeLayer.lineWidth = 2
shapeLayer.strokeColor = UIColor(red: 0.142, green: 0.614, blue: 0.95, alpha: 0.3).cgColor
addSubview(pinView)
transform = CGAffineTransform.identity.scaledBy(x: 1, y: -1)
pinView.transform = CGAffineTransform.identity.scaledBy(x: 1, y: -1)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath()
path.move(to: CGPoint(x: bounds.width / 2, y: 0))
path.addLine(to: CGPoint(x: bounds.width / 2, y: bounds.height))
shapeLayer.path = path.cgPath
updatePin()
}
private func updatePin() {
pinView.center = CGPoint(x: bounds.midX, y: pinY + pinView.bounds.height / 2)
}
}
fileprivate class MyPositionPinView: UIView {
override class var layerClass: AnyClass { CAShapeLayer.self }
var shapeLayer: CAShapeLayer { layer as! CAShapeLayer }
var path: UIBezierPath = {
let p = UIBezierPath()
p.addArc(withCenter: CGPoint(x: 6, y: 6),
radius: 6,
startAngle: -CGFloat.pi / 2,
endAngle: atan(3.5 / 4.8733971724),
clockwise: true)
p.addLine(to: CGPoint(x: 6 + 0.75, y: 15.6614378))
p.addArc(withCenter: CGPoint(x: 6, y: 15),
radius: 1,
startAngle: atan(0.6614378 / 0.75),
endAngle: CGFloat.pi - atan(0.6614378 / 0.75),
clockwise: true)
p.addLine(to: CGPoint(x: 6 - 4.8733971724, y: 9.5))
p.addArc(withCenter: CGPoint(x: 6, y: 6),
radius: 6,
startAngle: CGFloat.pi - atan(3.5 / 4.8733971724),
endAngle: -CGFloat.pi / 2,
clockwise: true)
p.close()
p.append(UIBezierPath(ovalIn: CGRect(x: 3, y: 3, width: 6, height: 6)))
return p
}()
override init(frame: CGRect) {
super.init(frame: frame)
shapeLayer.lineWidth = 0
shapeLayer.fillColor = UIColor(red: 0.142, green: 0.614, blue: 0.95, alpha: 0.5).cgColor
shapeLayer.fillRule = .evenOdd
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
let sx = bounds.width / path.bounds.width
let sy = bounds.height / path.bounds.height
let p = path.copy() as! UIBezierPath
p.apply(CGAffineTransform(scaleX: sx, y: sy))
shapeLayer.path = p.cgPath
}
}

View file

@ -0,0 +1,127 @@
import UIKit
final class ChartPointInfoView: UIView {
enum Alignment {
case left
case right
}
private let distanceLabel = UILabel()
private let altitudeLabel = UILabel()
private let stackView = UIStackView()
private let maskLayer = CAShapeLayer()
private var maskPath: UIBezierPath?
private let isInterfaceRightToLeft = UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft
var arrowY: CGFloat? {
didSet {
setNeedsLayout()
}
}
var alignment = Alignment.left {
didSet {
updateMask()
}
}
var font: UIFont = UIFont.systemFont(ofSize: 12, weight: .regular) {
didSet {
distanceLabel.font = font
altitudeLabel.font = font
}
}
var textColor: UIColor = .lightGray {
didSet {
distanceLabel.textColor = textColor
altitudeLabel.textColor = textColor
}
}
override var backgroundColor: UIColor? {
didSet {
maskLayer.fillColor = backgroundColor?.cgColor
}
}
override init(frame: CGRect) {
super.init(frame: frame)
layer.cornerRadius = 5
backgroundColor = .clear
layer.shadowColor = UIColor(white: 0, alpha: 1).cgColor
layer.shadowOpacity = 0.25
layer.shadowRadius = 2
layer.shadowOffset = CGSize(width: 0, height: 2)
maskLayer.fillColor = backgroundColor?.cgColor
layer.addSublayer(maskLayer)
stackView.alignment = .leading
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
NSLayoutConstraint.activate([
stackView.leftAnchor.constraint(equalTo: leftAnchor, constant: 6),
stackView.topAnchor.constraint(equalTo: topAnchor, constant: 6),
stackView.rightAnchor.constraint(equalTo: rightAnchor, constant: -6),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -6)
])
stackView.addArrangedSubview(distanceLabel)
stackView.addArrangedSubview(altitudeLabel)
stackView.setCustomSpacing(6, after: distanceLabel)
distanceLabel.font = font
altitudeLabel.font = font
distanceLabel.textColor = textColor
altitudeLabel.textColor = textColor
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
func set(x: CGFloat, label: String, points: [ChartLineInfo]) {
distanceLabel.text = label
altitudeLabel.text = altitudeText(points[0])
}
func update(x: CGFloat, label: String, points: [ChartLineInfo]) {
distanceLabel.text = label
altitudeLabel.text = altitudeText(points[0])
setNeedsLayout()
}
private func altitudeText(_ point: ChartLineInfo) -> String {
return String(isInterfaceRightToLeft ? "\(point.formattedValue)" : "\(point.formattedValue)")
}
override func layoutSubviews() {
super.layoutSubviews()
let y = arrowY ?? bounds.midY
let path = UIBezierPath(roundedRect: bounds, cornerRadius: 3)
let trianglePath = UIBezierPath()
trianglePath.move(to: CGPoint(x: bounds.maxX, y: y - 3))
trianglePath.addLine(to: CGPoint(x: bounds.maxX + 5, y: y))
trianglePath.addLine(to: CGPoint(x: bounds.maxX, y: y + 3))
trianglePath.close()
path.append(trianglePath)
maskPath = path
updateMask()
}
private func updateMask() {
guard let path = maskPath?.copy() as? UIBezierPath else { return }
if alignment == .right {
path.apply(CGAffineTransform.identity.scaledBy(x: -1, y: 1).translatedBy(x: -bounds.width, y: 0))
}
maskLayer.path = path.cgPath
layer.shadowPath = path.cgPath
}
}

View file

@ -0,0 +1,78 @@
import UIKit
fileprivate class CircleView: UIView {
override class var layerClass: AnyClass { return CAShapeLayer.self }
var color: UIColor? {
didSet {
shapeLayer.fillColor = color?.withAlphaComponent(0.5).cgColor
ringLayer.fillColor = UIColor.white.cgColor
centerLayer.fillColor = color?.cgColor
}
}
var shapeLayer: CAShapeLayer {
return layer as! CAShapeLayer
}
let ringLayer = CAShapeLayer()
let centerLayer = CAShapeLayer()
override var frame: CGRect {
didSet {
let p = UIBezierPath(ovalIn: bounds)
shapeLayer.path = p.cgPath
ringLayer.frame = shapeLayer.bounds.insetBy(dx: shapeLayer.bounds.width / 6, dy: shapeLayer.bounds.height / 6)
ringLayer.path = UIBezierPath(ovalIn: ringLayer.bounds).cgPath
centerLayer.frame = shapeLayer.bounds.insetBy(dx: shapeLayer.bounds.width / 3, dy: shapeLayer.bounds.height / 3)
centerLayer.path = UIBezierPath(ovalIn: centerLayer.bounds).cgPath
}
}
override init(frame: CGRect) {
super.init(frame: frame)
shapeLayer.fillColor = color?.withAlphaComponent(0.5).cgColor
shapeLayer.lineWidth = 4
shapeLayer.fillRule = .evenOdd
shapeLayer.addSublayer(ringLayer)
shapeLayer.addSublayer(centerLayer)
ringLayer.fillColor = UIColor.white.cgColor
centerLayer.fillColor = color?.cgColor
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
class ChartPointIntersectionView: UIView {
fileprivate var intersectionView = CircleView()
var color: UIColor = UIColor(red: 0.14, green: 0.61, blue: 0.95, alpha: 1) {
didSet {
intersectionView.color = color
backgroundColor = color.withAlphaComponent(0.5)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = color.withAlphaComponent(0.5)
transform = CGAffineTransform.identity.scaledBy(x: 1, y: -1)
intersectionView.color = color
intersectionView.frame = CGRect(x: 0, y: 0, width: 24, height: 24)
intersectionView.center = CGPoint(x: bounds.midX, y: bounds.midY)
addSubview(intersectionView)
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
func updatePoint(_ point: ChartLineInfo) {
intersectionView.center = CGPoint(x: bounds.midX, y: point.point.y)
color = point.color
}
}

View file

@ -0,0 +1,112 @@
import UIKit
class ChartLineView: UIView {
override class var layerClass: AnyClass { return CAShapeLayer.self }
private var minX = 0
private var maxX = 0
private var minY: CGFloat = 0
private var maxY: CGFloat = 0
var isPreview = false
var lineWidth: CGFloat = 1 {
didSet {
shapeLayer.lineWidth = lineWidth
}
}
var chartLine: ChartPresentationLine! {
didSet {
guard let chartLine = chartLine else { return }
maxX = chartLine.values.count - 1
minY = chartLine.minY
maxY = chartLine.maxY
switch chartLine.type {
case .line:
shapeLayer.strokeColor = chartLine.color.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineJoin = .round
case .lineArea:
shapeLayer.strokeColor = UIColor.clear.cgColor
shapeLayer.fillColor = chartLine.color.cgColor
}
shapeLayer.lineWidth = lineWidth
updateGraph()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
transform = CGAffineTransform.identity.scaledBy(x: 1, y: -1)
isUserInteractionEnabled = false
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
var shapeLayer: CAShapeLayer {
return layer as! CAShapeLayer
}
func setViewport(minX: Int, maxX: Int, minY: CGFloat, maxY: CGFloat, animationStyle: ChartAnimation = .none) {
guard minX < maxX && minY < maxY else { return }
self.minX = minX
self.maxX = maxX
self.minY = minY
self.maxY = maxY
updateGraph(animationStyle: animationStyle)
}
func setX(min: Int, max: Int) {
guard min < max else { return }
minX = min
maxX = max
updateGraph()
}
func setY(min: CGFloat, max: CGFloat, animationStyle: ChartAnimation = .none) {
guard min < max else { return }
minY = min
maxY = max
updateGraph(animationStyle: animationStyle)
}
private func updateGraph(animationStyle: ChartAnimation = .none) {
let p = isPreview ? chartLine.previewPath : chartLine.path
guard let realPath = p.copy() as? UIBezierPath else { return }
let xScale = bounds.width / CGFloat(maxX - minX)
let xTranslate = -bounds.width * CGFloat(minX) / CGFloat(maxX - minX)
let yScale = (bounds.height - 1) / CGFloat(maxY - minY)
let yTranslate = (bounds.height - 1) * CGFloat(chartLine.minY - minY) / CGFloat(maxY - minY) + 0.5
let scale = CGAffineTransform.identity.scaledBy(x: xScale, y: yScale)
let translate = CGAffineTransform.identity.translatedBy(x: xTranslate, y: yTranslate)
let transform = scale.concatenating(translate)
realPath.apply(transform)
if animationStyle != .none {
let timingFunction = CAMediaTimingFunction(name: animationStyle == .interactive ? .linear : .easeInEaseOut)
if shapeLayer.animationKeys()?.contains("path") ?? false,
let presentation = shapeLayer.presentation(),
let path = presentation.path {
shapeLayer.removeAnimation(forKey: "path")
shapeLayer.path = path
}
let animation = CABasicAnimation(keyPath: "path")
animation.duration = animationStyle.rawValue
animation.fromValue = shapeLayer.path
animation.timingFunction = timingFunction
layer.add(animation, forKey: "path")
}
shapeLayer.path = realPath.cgPath
}
override func layoutSubviews() {
super.layoutSubviews()
updateGraph()
}
}

View file

@ -0,0 +1,246 @@
import UIKit
protocol ChartPreviewViewDelegate: AnyObject {
func chartPreviewView(_ view: ChartPreviewView, didChangeMinX minX: Int, maxX: Int)
}
class TintView: UIView {
let maskLayer = CAShapeLayer()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
maskLayer.fillRule = .evenOdd
layer.mask = maskLayer
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
func updateViewport(_ viewport: CGRect) {
let cornersMask = UIBezierPath(roundedRect: bounds, cornerRadius: 5)
let rectMask = UIBezierPath(rect: viewport.insetBy(dx: 11, dy: 1))
let result = UIBezierPath()
result.append(cornersMask)
result.append(rectMask)
result.usesEvenOddFillRule = true
maskLayer.path = result.cgPath
}
}
class ViewPortView: ExpandedTouchView {
let maskLayer = CAShapeLayer()
var tintView: TintView?
override init(frame: CGRect = .zero) {
super.init(frame: frame)
maskLayer.fillRule = .evenOdd
layer.mask = maskLayer
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
override var frame: CGRect {
didSet {
maskLayer.path = makeMaskPath().cgPath
tintView?.updateViewport(convert(bounds, to: tintView))
}
}
func makeMaskPath() -> UIBezierPath {
let cornersMask = UIBezierPath(roundedRect: bounds, cornerRadius: 5)
let rectMask = UIBezierPath(rect: bounds.insetBy(dx: 11, dy: 1))
let result = UIBezierPath()
result.append(cornersMask)
result.append(rectMask)
result.usesEvenOddFillRule = true
return result
}
}
class ChartPreviewView: ExpandedTouchView {
let previewContainerView = UIView()
let viewPortView = ViewPortView()
let leftBoundView = UIView()
let rightBoundView = UIView()
let tintView = TintView()
var previewViews: [ChartLineView] = []
var selectorColor: UIColor = UIColor.white {
didSet {
viewPortView.backgroundColor = selectorColor
}
}
var selectorTintColor: UIColor = UIColor.clear {
didSet {
tintView.backgroundColor = selectorTintColor
}
}
var minX = 0
var maxX = 0
weak var delegate: ChartPreviewViewDelegate?
override var frame: CGRect {
didSet {
if chartData != nil {
updateViewPort()
}
}
}
var chartData: ChartPresentationData! {
didSet {
previewViews.forEach { $0.removeFromSuperview() }
previewViews.removeAll()
for i in (0..<chartData.linesCount).reversed() {
let line = chartData.lineAt(i)
let v = ChartLineView()
v.isPreview = true
v.chartLine = line
v.frame = previewContainerView.bounds
v.autoresizingMask = [.flexibleWidth, .flexibleHeight]
previewContainerView.addSubview(v)
previewViews.insert(v, at: 0)
}
previewViews.forEach { $0.setY(min: chartData.lower, max: chartData.upper) }
let count = chartData.pointsCount - 1
minX = 0
maxX = count
updateViewPort()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
previewContainerView.translatesAutoresizingMaskIntoConstraints = false
previewContainerView.layer.cornerRadius = 5
previewContainerView.clipsToBounds = true
addSubview(previewContainerView)
let t = previewContainerView.topAnchor.constraint(equalTo: topAnchor)
let b = previewContainerView.bottomAnchor.constraint(equalTo: bottomAnchor)
t.priority = .defaultHigh
b.priority = .defaultHigh
t.constant = 1
b.constant = -1
NSLayoutConstraint.activate([
previewContainerView.leftAnchor.constraint(equalTo: leftAnchor),
previewContainerView.rightAnchor.constraint(equalTo: rightAnchor),
t,
b])
tintView.frame = bounds
tintView.backgroundColor = selectorTintColor
tintView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(tintView)
viewPortView.tintView = tintView
viewPortView.backgroundColor = selectorColor
viewPortView.translatesAutoresizingMaskIntoConstraints = false
addSubview(viewPortView)
viewPortView.addSubview(leftBoundView)
viewPortView.addSubview(rightBoundView)
let pan = UIPanGestureRecognizer(target: self, action: #selector(onPan(_:)))
viewPortView.addGestureRecognizer(pan)
let leftPan = UIPanGestureRecognizer(target: self, action: #selector(onLeftPan(_:)))
let rightPan = UIPanGestureRecognizer(target: self, action: #selector(onRightPan(_:)))
leftBoundView.addGestureRecognizer(leftPan)
rightBoundView.addGestureRecognizer(rightPan)
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
@objc func onPan(_ sender: UIPanGestureRecognizer) {
if sender.state != .changed { return }
let p = sender.translation(in: viewPortView)
let count = chartData.labels.count - 1
let x = Int((viewPortView.frame.minX + p.x) / bounds.width * CGFloat(count))
let dx = maxX - minX
let mx = x + dx
if x > 0 && mx < count {
viewPortView.frame = viewPortView.frame.offsetBy(dx: p.x, dy: 0)
sender.setTranslation(CGPoint(x: 0, y: 0), in: viewPortView)
if x != minX {
minX = x
maxX = mx
delegate?.chartPreviewView(self, didChangeMinX: minX, maxX: maxX)
}
} else if minX > 0 && x <= 0 {
setX(min: 0, max: dx)
} else if maxX < count && mx >= count {
setX(min: count - dx, max: count)
}
}
@objc func onLeftPan(_ sender: UIPanGestureRecognizer) {
if sender.state != .changed { return }
let p = sender.translation(in: leftBoundView)
let count = chartData.labels.count - 1
let x = Int((viewPortView.frame.minX + p.x) / bounds.width * CGFloat(count))
if x > 0 && x < maxX && maxX - x >= count / 10 {
var f = viewPortView.frame
f = CGRect(x: f.minX + p.x, y: f.minY, width: f.width - p.x, height: f.height)
viewPortView.frame = f
rightBoundView.frame = CGRect(x: viewPortView.bounds.width - 14, y: 0, width: 44, height: viewPortView.bounds.height)
sender.setTranslation(CGPoint(x: 0, y: 0), in: leftBoundView)
if x != minX {
minX = x
delegate?.chartPreviewView(self, didChangeMinX: minX, maxX: maxX)
}
} else if x <= 0 && minX > 0 {
setX(min: 0, max: maxX)
}
}
@objc func onRightPan(_ sender: UIPanGestureRecognizer) {
if sender.state != .changed { return }
let p = sender.translation(in: viewPortView)
let count = chartData.labels.count - 1
let x = Int((viewPortView.frame.maxX + p.x) / bounds.width * CGFloat(count))
if x > minX && x < count && x - minX >= count / 10 {
var f = viewPortView.frame
f = CGRect(x: f.minX, y: f.minY, width: f.width + p.x, height: f.height)
viewPortView.frame = f
rightBoundView.frame = CGRect(x: viewPortView.bounds.width - 14, y: 0, width: 44, height: viewPortView.bounds.height)
sender.setTranslation(CGPoint(x: 0, y: 0), in: rightBoundView)
if x != maxX {
maxX = x
delegate?.chartPreviewView(self, didChangeMinX: minX, maxX: maxX)
}
} else if x >= count && maxX < count {
setX(min: minX, max: count)
}
}
func setX(min: Int, max: Int) {
guard min < max else { return }
minX = min
maxX = max
updateViewPort()
delegate?.chartPreviewView(self, didChangeMinX: minX, maxX: maxX)
}
func updateViewPort() {
let count = CGFloat(chartData.labels.count - 1)
viewPortView.frame = CGRect(x: CGFloat(minX) / count * bounds.width,
y: bounds.minY,
width: CGFloat(maxX - minX) / count * bounds.width,
height: bounds.height)
leftBoundView.frame = CGRect(x: -30, y: 0, width: 44, height: viewPortView.bounds.height)
rightBoundView.frame = CGRect(x: viewPortView.bounds.width - 14, y: 0, width: 44, height: viewPortView.bounds.height)
}
}

View file

@ -0,0 +1,374 @@
import UIKit
enum ChartAnimation: TimeInterval {
case none = 0.0
case animated = 0.3
case interactive = 0.1
}
public class ChartView: UIView {
let chartsContainerView = ExpandedTouchView()
let chartPreviewView = ChartPreviewView()
let yAxisView = ChartYAxisView()
let xAxisView = ChartXAxisView()
let chartInfoView = ChartInfoView()
var lineViews: [ChartLineView] = []
var showPreview: Bool = false // Set true to show the preview
private var tapGR: UITapGestureRecognizer!
private var selectedPointDistance: Double = 0
private var panStartPoint = 0
private var panGR: UIPanGestureRecognizer!
private var pinchStartLower = 0
private var pinchStartUpper = 0
private var pinchGR: UIPinchGestureRecognizer!
public var myPosition: Double = -1 {
didSet {
setMyPosition(myPosition)
}
}
public var previewSelectorColor: UIColor = UIColor.lightGray.withAlphaComponent(0.9) {
didSet {
chartPreviewView.selectorColor = previewSelectorColor
}
}
public var previewTintColor: UIColor = UIColor.lightGray.withAlphaComponent(0.5) {
didSet {
chartPreviewView.selectorTintColor = previewTintColor
}
}
public var infoBackgroundColor: UIColor = UIColor.white {
didSet {
chartInfoView.infoBackgroundColor = infoBackgroundColor
yAxisView.textBackgroundColor = infoBackgroundColor.withAlphaComponent(0.7)
}
}
public var infoShadowColor: UIColor = UIColor.black {
didSet {
chartInfoView.infoShadowColor = infoShadowColor
}
}
public var infoShadowOpacity: Float = 0.25 {
didSet {
chartInfoView.infoShadowOpacity = infoShadowOpacity
}
}
public var font: UIFont = UIFont.systemFont(ofSize: 12, weight: .regular) {
didSet {
xAxisView.font = font
yAxisView.font = font
chartInfoView.font = font
}
}
public var textColor: UIColor = UIColor(white: 0, alpha: 0.2) {
didSet {
xAxisView.textColor = textColor
yAxisView.textColor = textColor
chartInfoView.textColor = textColor
}
}
public var gridColor: UIColor = UIColor(white: 0, alpha: 0.2) {
didSet {
yAxisView.gridColor = gridColor
}
}
public override var backgroundColor: UIColor? {
didSet {
chartInfoView.tooltipBackgroundColor = backgroundColor ?? .white
}
}
public var chartData: ChartPresentationData! {
didSet {
lineViews.forEach { $0.removeFromSuperview() }
lineViews.removeAll()
for i in (0..<chartData.linesCount).reversed() {
let line = chartData.lineAt(i)
let v = ChartLineView()
v.clipsToBounds = true
v.chartLine = line
v.lineWidth = 3
v.frame = chartsContainerView.bounds
v.autoresizingMask = [.flexibleWidth, .flexibleHeight]
chartsContainerView.addSubview(v)
lineViews.insert(v, at: 0)
}
yAxisView.frame = chartsContainerView.bounds
yAxisView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
yAxisView.transform = CGAffineTransform.identity.scaledBy(x: 1, y: -1)
chartsContainerView.addSubview(yAxisView)
chartInfoView.frame = chartsContainerView.bounds
chartInfoView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
chartInfoView.delegate = self
chartInfoView.textColor = textColor
chartsContainerView.addSubview(chartInfoView)
xAxisView.values = chartData.xAxisValues.enumerated().map { ChartXAxisView.Value(index: $0.offset, value: $0.element, text: chartData.labels[$0.offset]) }
chartPreviewView.chartData = chartData
xAxisView.setBounds(lower: chartPreviewView.minX, upper: chartPreviewView.maxX)
updateCharts()
}
}
public var isChartViewInfoHidden: Bool = false {
didSet {
chartInfoView.isHidden = isChartViewInfoHidden
chartInfoView.isUserInteractionEnabled = !isChartViewInfoHidden
}
}
public typealias OnSelectedPointChangedClosure = (_ px: CGFloat) -> Void
public var onSelectedPointChanged: OnSelectedPointChangedClosure?
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
isUserInteractionEnabled = false
xAxisView.font = font
xAxisView.textColor = textColor
yAxisView.font = font
yAxisView.textColor = textColor
yAxisView.gridColor = textColor
chartInfoView.font = font
chartPreviewView.selectorTintColor = previewTintColor
chartPreviewView.selectorColor = previewSelectorColor
chartInfoView.tooltipBackgroundColor = backgroundColor ?? .white
yAxisView.textBackgroundColor = infoBackgroundColor.withAlphaComponent(0.7)
tapGR = UITapGestureRecognizer(target: self, action: #selector(onTap(_:)))
chartsContainerView.addGestureRecognizer(tapGR)
panGR = UIPanGestureRecognizer(target: self, action: #selector(onPan(_:)))
chartsContainerView.addGestureRecognizer(panGR)
pinchGR = UIPinchGestureRecognizer(target: self, action: #selector(onPinch(_:)))
chartsContainerView.addGestureRecognizer(pinchGR)
addSubview(chartsContainerView)
if showPreview {
addSubview(chartPreviewView)
}
chartPreviewView.delegate = self
addSubview(xAxisView)
}
public func setSelectedPoint(_ x: Double) {
guard selectedPointDistance != x else { return }
selectedPointDistance = x
let routeLength = chartData.xAxisValueAt(CGFloat(chartData.pointsCount - 1))
let upper = chartData.xAxisValueAt(CGFloat(chartPreviewView.maxX))
var lower = chartData.xAxisValueAt(CGFloat(chartPreviewView.minX))
let rangeLength = upper - lower
if x < lower || x > upper {
let current = Double(chartInfoView.infoX) * rangeLength + lower
let dx = x - current
let dIdx = Int(dx / routeLength * Double(chartData.pointsCount))
var lowerIdx = chartPreviewView.minX + dIdx
var upperIdx = chartPreviewView.maxX + dIdx
if lowerIdx < 0 {
upperIdx -= lowerIdx
lowerIdx = 0
} else if upperIdx >= chartData.pointsCount {
lowerIdx -= upperIdx - chartData.pointsCount - 1
upperIdx = chartData.pointsCount - 1
}
chartPreviewView.setX(min: lowerIdx, max: upperIdx)
lower = chartData.xAxisValueAt(CGFloat(chartPreviewView.minX))
}
chartInfoView.infoX = CGFloat((x - lower) / rangeLength)
}
fileprivate func setMyPosition(_ x: Double) {
let upper = chartData.xAxisValueAt(CGFloat(chartPreviewView.maxX))
let lower = chartData.xAxisValueAt(CGFloat(chartPreviewView.minX))
let rangeLength = upper - lower
chartInfoView.myPositionX = CGFloat((x - lower) / rangeLength)
}
override public func layoutSubviews() {
super.layoutSubviews()
let previewFrame = showPreview ? CGRect(x: bounds.minX, y: bounds.maxY - 30, width: bounds.width, height: 30) : .zero
chartPreviewView.frame = previewFrame
let xAxisFrame = CGRect(x: bounds.minX, y: bounds.maxY - previewFrame.height - 26, width: bounds.width, height: 26)
xAxisView.frame = xAxisFrame
let chartsFrame = CGRect(x: bounds.minX,
y: bounds.minY,
width: bounds.width,
height: bounds.maxY - previewFrame.height - xAxisFrame.height)
chartsContainerView.frame = chartsFrame
}
override public func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let rect = bounds.insetBy(dx: -30, dy: 0)
return rect.contains(point)
}
@objc func onTap(_ sender: UITapGestureRecognizer) {
guard sender.state == .ended else {
return
}
let point = sender.location(in: chartInfoView)
updateCharts(animationStyle: .none)
chartInfoView.update(point.x)
chartInfoView(chartInfoView, didMoveToPoint: point.x)
}
@objc func onPinch(_ sender: UIPinchGestureRecognizer) {
if sender.state == .began {
pinchStartLower = xAxisView.lowerBound
pinchStartUpper = xAxisView.upperBound
}
if sender.state != .changed {
return
}
let rangeLength = CGFloat(pinchStartUpper - pinchStartLower)
let dx = Int(round((rangeLength * sender.scale - rangeLength) / 2))
let lower = max(pinchStartLower + dx, 0)
let upper = min(pinchStartUpper - dx, chartData.labels.count - 1)
guard upper - lower > max(1, chartData.labels.count / 10) else {
return
}
chartPreviewView.setX(min: lower, max: upper)
xAxisView.setBounds(lower: lower, upper: upper)
updateCharts(animationStyle: .none)
chartInfoView.update()
}
@objc func onPan(_ sender: UIPanGestureRecognizer) {
let t = sender.translation(in: chartsContainerView)
if sender.state == .began {
panStartPoint = xAxisView.lowerBound
}
if sender.state != .changed {
return
}
let dx = Int(round(t.x / chartsContainerView.bounds.width * CGFloat(xAxisView.upperBound - xAxisView.lowerBound)))
let lower = panStartPoint - dx
let upper = lower + xAxisView.upperBound - xAxisView.lowerBound
if lower < 0 || upper > chartData.labels.count - 1 {
return
}
chartPreviewView.setX(min: lower, max: upper)
xAxisView.setBounds(lower: lower, upper: upper)
updateCharts(animationStyle: .none)
chartInfoView.update()
}
func updateCharts(animationStyle: ChartAnimation = .none) {
var lower = CGFloat(Int.max)
var upper = CGFloat(Int.min)
for i in 0..<chartData.linesCount {
let line = chartData.lineAt(i)
let subrange = line.aggregatedValues[xAxisView.lowerBound...xAxisView.upperBound]
subrange.forEach {
upper = max($0.y, upper)
if line.type == .line || line.type == .lineArea {
lower = min($0.y, lower)
}
}
}
let padding = round((upper - lower) / 10)
lower = chartData.formatter.yAxisLowerBound(from: max(0, lower - padding))
upper = chartData.formatter.yAxisUpperBound(from: upper + padding)
let steps = chartData.formatter.yAxisSteps(lowerBound: lower, upperBound: upper)
if yAxisView.upperBound != upper || yAxisView.lowerBound != lower {
yAxisView.setBounds(lower: lower,
upper: upper,
lowerLabel: chartData.formatter.yAxisString(from: Double(lower)),
upperLabel: chartData.formatter.yAxisString(from: Double(upper)),
steps: steps,
animationStyle: animationStyle)
}
lineViews.forEach {
$0.setViewport(minX: xAxisView.lowerBound,
maxX: xAxisView.upperBound,
minY: lower,
maxY: upper,
animationStyle: animationStyle)
}
}
}
extension ChartView: ChartPreviewViewDelegate {
func chartPreviewView(_ view: ChartPreviewView, didChangeMinX minX: Int, maxX: Int) {
xAxisView.setBounds(lower: minX, upper: maxX)
updateCharts(animationStyle: .none)
chartInfoView.update()
setMyPosition(myPosition)
let x = chartInfoView.infoX * CGFloat(xAxisView.upperBound - xAxisView.lowerBound) + CGFloat(xAxisView.lowerBound)
onSelectedPointChanged?(x)
}
}
extension ChartView: ChartInfoViewDelegate {
func chartInfoView(_ view: ChartInfoView, didMoveToPoint pointX: CGFloat) {
let p = convert(CGPoint(x: pointX, y: 0), from: view)
let x = (p.x / bounds.width) * CGFloat(xAxisView.upperBound - xAxisView.lowerBound) + CGFloat(xAxisView.lowerBound)
onSelectedPointChanged?(x)
}
func chartInfoView(_ view: ChartInfoView, didCaptureInfoView captured: Bool) {
panGR.isEnabled = !captured
}
func chartInfoView(_ view: ChartInfoView, infoAtPointX pointX: CGFloat) -> (String, [ChartLineInfo])? {
let p = convert(CGPoint(x: pointX, y: .zero), from: view)
let x = (p.x / bounds.width) * CGFloat(xAxisView.upperBound - xAxisView.lowerBound) + CGFloat(xAxisView.lowerBound)
let x1 = floor(x)
let x2 = ceil(x)
guard !pointX.isZero, Int(x1) < chartData.labels.count && x >= 0 else { return nil }
let label = chartData.labelAt(x)
var result: [ChartLineInfo] = []
for i in 0..<chartData.linesCount {
let line = chartData.lineAt(i)
guard line.type != .lineArea else { continue }
let y1 = line.values.altitude(at: x1 / CGFloat(chartData.pointsCount))
let y2 = line.values.altitude(at: x2 / CGFloat(chartData.pointsCount))
let dx = x - x1
let y = dx * (y2 - y1) + y1
let py = round(chartsContainerView.bounds.height * CGFloat(y - yAxisView.lowerBound) /
CGFloat(yAxisView.upperBound - yAxisView.lowerBound))
let v = round(dx * CGFloat(y2 - y1)) + CGFloat(y1)
result.append(ChartLineInfo(color: line.color,
point: chartsContainerView.convert(CGPoint(x: p.x, y: py), to: view),
formattedValue: chartData.formatter.yAxisString(from: Double(v))))
}
return (label, result)
}
}

View file

@ -0,0 +1,130 @@
import UIKit
fileprivate class ChartXAxisInnerView: UIView {
var lowerBound = 0
var upperBound = 0
var steps: [String] = []
var labels: [UILabel] = []
var font: UIFont = UIFont.systemFont(ofSize: 12, weight: .regular) {
didSet {
labels.forEach { $0.font = font }
}
}
var textColor: UIColor = UIColor(white: 0, alpha: 0.3) {
didSet {
labels.forEach { $0.textColor = textColor }
}
}
override var frame: CGRect {
didSet {
if upperBound > 0 {
updateLabels()
}
}
}
private func makeLabel(text: String) -> UILabel {
let label = UILabel()
label.font = font
label.textColor = textColor
label.text = text
label.frame = CGRect(x: 0, y: 0, width: 60, height: 15)
return label
}
func setBounds(lower: Int, upper: Int, steps: [String]) {
lowerBound = lower
upperBound = upper
self.steps = steps
labels.forEach { $0.removeFromSuperview() }
labels.removeAll()
for i in 0..<steps.count {
let step = steps[i]
let label = makeLabel(text: step)
if i == 0 {
label.textAlignment = .left
} else if i == steps.count - 1 {
label.textAlignment = .right
} else {
label.textAlignment = .center
}
labels.append(label)
addSubview(label)
}
updateLabels()
}
private func updateLabels() {
let step = CGFloat(upperBound - lowerBound) / CGFloat(labels.count - 1)
for i in 0..<labels.count {
let x = bounds.width * step * CGFloat(i) / CGFloat(upperBound - lowerBound)
let l = labels[i]
var f = l.frame
let adjust = bounds.width > 0 ? x / bounds.width : 0
f.origin = CGPoint(x: x - f.width * adjust, y: 0)
l.frame = f
}
}
}
class ChartXAxisView: UIView {
struct Value {
let index: Int
let value: Double
let text: String
}
var lowerBound = 0
var upperBound = 0
var values: [Value] = []
var font: UIFont = UIFont.systemFont(ofSize: 12, weight: .regular) {
didSet {
labelsView?.font = font
}
}
var textColor: UIColor = UIColor(white: 0, alpha: 0.3) {
didSet {
labelsView?.textColor = textColor
}
}
private var labelsView: ChartXAxisInnerView?
func setBounds(lower: Int, upper: Int) {
lowerBound = lower
upperBound = upper
let begin = values[lower].value
let end = values[upper].value
let step = CGFloat(end - begin) / 5
var labels: [String] = []
for i in 0..<5 {
if let x = values.first(where: { $0.value >= (begin + step * CGFloat(i)) }) {
labels.append(x.text)
}
}
labels.append(values[upper].text)
let lv = ChartXAxisInnerView()
lv.frame = bounds
lv.textColor = textColor
lv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(lv)
if let labelsView = labelsView {
labelsView.removeFromSuperview()
}
lv.setBounds(lower: lower, upper: upper, steps: labels)
labelsView = lv
}
}

View file

@ -0,0 +1,252 @@
import UIKit
enum ChartYAxisViewAlignment {
case left
case right
}
fileprivate class ChartYAxisInnerView: UIView {
override class var layerClass: AnyClass { return CAShapeLayer.self }
private static let font = UIFont.systemFont(ofSize: 12, weight: .regular)
var lowerBound: CGFloat = 0
var upperBound: CGFloat = 0
var steps: [CGFloat] = []
let lowerLabel: UILabel
let upperLabel: UILabel
let lowerLabelBackground = UIView()
let upperLabelBackground = UIView()
var alignment: ChartYAxisViewAlignment = .left
var font: UIFont = UIFont.systemFont(ofSize: 12, weight: .regular) {
didSet {
lowerLabel.font = font
upperLabel.font = font
}
}
var textColor: UIColor = UIColor(white: 0, alpha: 0.3) {
didSet {
lowerLabel.textColor = textColor
upperLabel.textColor = textColor
}
}
var textBackgroundColor: UIColor = UIColor(white: 1, alpha: 0.7) {
didSet {
lowerLabelBackground.backgroundColor = textBackgroundColor
upperLabelBackground.backgroundColor = textBackgroundColor
}
}
var gridColor: UIColor = UIColor.white {
didSet {
shapeLayer.strokeColor = gridColor.cgColor
}
}
private var path: UIBezierPath?
var shapeLayer: CAShapeLayer {
return layer as! CAShapeLayer
}
override init(frame: CGRect) {
lowerLabel = ChartYAxisInnerView.makeLabel()
upperLabel = ChartYAxisInnerView.makeLabel()
super.init(frame: frame)
lowerLabel.translatesAutoresizingMaskIntoConstraints = false
upperLabel.translatesAutoresizingMaskIntoConstraints = false
lowerLabelBackground.translatesAutoresizingMaskIntoConstraints = false
upperLabelBackground.translatesAutoresizingMaskIntoConstraints = false
lowerLabelBackground.addSubview(lowerLabel)
upperLabelBackground.addSubview(upperLabel)
addSubview(lowerLabelBackground)
addSubview(upperLabelBackground)
NSLayoutConstraint.activate([
lowerLabel.leftAnchor.constraint(equalTo: lowerLabelBackground.leftAnchor, constant: 5),
lowerLabel.topAnchor.constraint(equalTo: lowerLabelBackground.topAnchor),
lowerLabel.rightAnchor.constraint(equalTo: lowerLabelBackground.rightAnchor, constant: -5),
lowerLabel.bottomAnchor.constraint(equalTo: lowerLabelBackground.bottomAnchor),
upperLabel.leftAnchor.constraint(equalTo: upperLabelBackground.leftAnchor, constant: 5),
upperLabel.topAnchor.constraint(equalTo: upperLabelBackground.topAnchor),
upperLabel.rightAnchor.constraint(equalTo: upperLabelBackground.rightAnchor, constant: -5),
upperLabel.bottomAnchor.constraint(equalTo: upperLabelBackground.bottomAnchor),
lowerLabelBackground.topAnchor.constraint(equalTo: topAnchor, constant: 5),
lowerLabelBackground.rightAnchor.constraint(equalTo: rightAnchor, constant: -5),
upperLabelBackground.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5),
upperLabelBackground.rightAnchor.constraint(equalTo: rightAnchor, constant: -5)
])
lowerLabel.textColor = textColor
upperLabel.textColor = textColor
lowerLabelBackground.backgroundColor = textBackgroundColor
upperLabelBackground.backgroundColor = textBackgroundColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = gridColor.cgColor
shapeLayer.lineDashPattern = [2, 3]
shapeLayer.lineWidth = 1
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
if upperBound > 0 && lowerBound > 0 {
updateGrid()
}
lowerLabelBackground.layer.cornerRadius = lowerLabelBackground.frame.height / 2
upperLabelBackground.layer.cornerRadius = upperLabelBackground.frame.height / 2
}
static func makeLabel() -> UILabel {
let label = UILabel()
label.font = ChartYAxisInnerView.font
label.transform = CGAffineTransform.identity.scaledBy(x: 1, y: -1)
return label
}
func setBounds(lower: CGFloat, upper: CGFloat, lowerLabelText: String, upperLabelText: String, steps: [CGFloat]) {
lowerBound = lower
upperBound = upper
lowerLabel.text = lowerLabelText
upperLabel.text = upperLabelText
self.steps = steps
updateGrid()
}
func updateBounds(lower: CGFloat, upper: CGFloat, animationStyle: ChartAnimation = .none) {
lowerBound = lower
upperBound = upper
updateGrid(animationStyle: animationStyle)
}
func updateGrid(animationStyle: ChartAnimation = .none) {
let p = UIBezierPath()
for step in steps {
p.move(to: CGPoint(x: 0, y: step))
p.addLine(to: CGPoint(x: bounds.width, y: step))
}
let realPath = p
let yScale = (bounds.height) / CGFloat(upperBound - lowerBound)
let yTranslate = (bounds.height) * CGFloat(-lowerBound) / CGFloat(upperBound - lowerBound)
let scale = CGAffineTransform.identity.scaledBy(x: 1, y: yScale)
let translate = CGAffineTransform.identity.translatedBy(x: 0, y: yTranslate)
let transform = scale.concatenating(translate)
realPath.apply(transform)
if animationStyle != .none {
let timingFunction = CAMediaTimingFunction(name: animationStyle == .interactive ? .linear : .easeInEaseOut)
if shapeLayer.animationKeys()?.contains("path") ?? false,
let presentation = shapeLayer.presentation(),
let path = presentation.path {
shapeLayer.removeAnimation(forKey: "path")
shapeLayer.path = path
}
let animation = CABasicAnimation(keyPath: "path")
let duration = animationStyle.rawValue
animation.duration = duration
animation.fromValue = shapeLayer.path
animation.timingFunction = timingFunction
layer.add(animation, forKey: "path")
}
shapeLayer.path = realPath.cgPath
}
}
class ChartYAxisView: UIView {
var lowerBound: CGFloat = 0
var upperBound: CGFloat = 0
var alignment: ChartYAxisViewAlignment = .right
var font: UIFont = UIFont.systemFont(ofSize: 12, weight: .regular) {
didSet {
gridView?.font = font
}
}
var textColor: UIColor = UIColor(white: 0, alpha: 0.3) {
didSet {
gridView?.textColor = textColor
}
}
var textBackgroundColor: UIColor = UIColor(white: 0, alpha: 0.3) {
didSet {
gridView?.textBackgroundColor = textBackgroundColor
}
}
var gridColor: UIColor = UIColor(white: 0, alpha: 0.3) {
didSet {
gridView?.gridColor = gridColor
}
}
override var frame: CGRect {
didSet {
gridView?.updateGrid()
}
}
private var gridView: ChartYAxisInnerView?
func setBounds(lower: CGFloat,
upper: CGFloat,
lowerLabel: String,
upperLabel: String,
steps: [CGFloat],
animationStyle: ChartAnimation = .none) {
let gv = ChartYAxisInnerView()
gv.alignment = alignment
gv.textColor = textColor
gv.gridColor = gridColor
gv.textBackgroundColor = textBackgroundColor
gv.frame = bounds
gv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(gv)
if let gridView = gridView {
if animationStyle == .animated {
gv.setBounds(lower: lowerBound,
upper: upperBound,
lowerLabelText: lowerLabel,
upperLabelText: upperLabel,
steps: steps)
gv.alpha = 0
gv.updateBounds(lower: lower, upper:upper, animationStyle: animationStyle)
gridView.updateBounds(lower: lower, upper:upper, animationStyle: animationStyle)
UIView.animate(withDuration: animationStyle.rawValue, animations: {
gv.alpha = 1
gridView.alpha = 0
}) { _ in
gridView.removeFromSuperview()
}
} else {
gv.setBounds(lower: lower, upper: upper, lowerLabelText: lowerLabel, upperLabelText: upperLabel, steps: steps)
gridView.removeFromSuperview()
}
} else {
gv.setBounds(lower: lower, upper: upper, lowerLabelText: lowerLabel, upperLabelText: upperLabel, steps: steps)
}
gridView = gv
lowerBound = lower
upperBound = upper
}
}

View file

@ -0,0 +1,8 @@
import UIKit
class ExpandedTouchView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let rect = bounds.insetBy(dx: -30, dy: 0)
return rect.contains(point)
}
}

View file

@ -0,0 +1,2 @@
"placepage_distance" = "المسافة";
"you" = "أنت";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Vzdálenost";
"you" = "Vy";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Afstand";
"you" = "Du";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Entfernung";
"you" = "Sie";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Distance";
"you" = "Εσείς";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Distance";
"you" = "You";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Distance";
"you" = "You";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Distancia";
"you" = "Usted";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Etäisyys";
"you" = "Sinä";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Distance";
"you" = "Vous";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Távolság";
"you" = "Ön";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Jarak";
"you" = "Anda";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Distanza";
"you" = "Voi";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "距離";
"you" = "君";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "거리";
"you" = "당신";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Avstand";
"you" = "Du";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Afstand";
"you" = "Je";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Dystans";
"you" = "Ty";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Distância";
"you" = "Você";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Distanță";
"you" = "Tu";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Расстояние";
"you" = "Вы";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Vzdialenosť";
"you" = "Vy";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Avstånd";
"you" = "Du";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "ระยะห่าง";
"you" = "คุณ";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Mesafe";
"you" = "Sen";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Відстань";
"you" = "Ви";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "Khoảng cách";
"you" = "Bạn";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "距离";
"you" = "您";

View file

@ -0,0 +1,2 @@
"placepage_distance" = "距离";
"you" = "您";

View file

@ -0,0 +1,15 @@
framework module CoreApi {
header "AppInfo.h"
header "MWMBookmarkColor.h"
header "MWMCommon.h"
header "MWMTypes.h"
header "MWMBookmarksObserver.h"
header "MWMBookmarksManager.h"
header "MWMBookmarkGroup.h"
header "MWMFrameworkHelper.h"
header "MWMCarPlayBookmarkObject.h"
header "MWMMapOverlayManager.h"
header "MWMSearchFrameworkHelper.h"
header "MWMNetworkPolicy.h"
header "PlacePageBookmarkData.h"
}

View file

@ -0,0 +1,792 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
470015F42342509C00EBF03D /* CoreApi.h in Headers */ = {isa = PBXBuildFile; fileRef = 470015F22342509C00EBF03D /* CoreApi.h */; settings = {ATTRIBUTES = (Public, ); }; };
471527392491EDAA00E91BBA /* MWMBookmarkColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 471527382491EDA400E91BBA /* MWMBookmarkColor.h */; settings = {ATTRIBUTES = (Public, ); }; };
4718C4322355FC3C00640DF1 /* MWMNetworkPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4718C4302355FC3C00640DF1 /* MWMNetworkPolicy.h */; settings = {ATTRIBUTES = (Public, ); }; };
4718C4332355FC3C00640DF1 /* MWMNetworkPolicy.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4718C4312355FC3C00640DF1 /* MWMNetworkPolicy.mm */; };
471AB98D23AB925D00F56D49 /* MWMMapSearchResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 471AB98B23AB925D00F56D49 /* MWMMapSearchResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
471AB98E23AB925D00F56D49 /* MWMMapSearchResult.mm in Sources */ = {isa = PBXBuildFile; fileRef = 471AB98C23AB925D00F56D49 /* MWMMapSearchResult.mm */; };
471AB99123AB931000F56D49 /* MWMMapSearchResult+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 471AB98F23AB931000F56D49 /* MWMMapSearchResult+Core.h */; };
472602A824092C5B00731135 /* MWMGeoUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 472602A624092C5B00731135 /* MWMGeoUtil.h */; settings = {ATTRIBUTES = (Public, ); }; };
472602A924092C5B00731135 /* MWMGeoUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 472602A724092C5B00731135 /* MWMGeoUtil.mm */; };
475784C22344B422008291A4 /* Framework.h in Headers */ = {isa = PBXBuildFile; fileRef = 475784C02344B421008291A4 /* Framework.h */; settings = {ATTRIBUTES = (Public, ); }; };
475784C32344B422008291A4 /* Framework.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 475784C12344B422008291A4 /* Framework.cpp */; };
47942D6B237CC3D600DEFAE3 /* PlacePageData.h in Headers */ = {isa = PBXBuildFile; fileRef = 47942D51237CC3B500DEFAE3 /* PlacePageData.h */; settings = {ATTRIBUTES = (Public, ); }; };
47942D6C237CC3DE00DEFAE3 /* PlacePageData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47942D52237CC3B500DEFAE3 /* PlacePageData.mm */; };
47942D6D237CC3E300DEFAE3 /* PlacePagePreviewData.h in Headers */ = {isa = PBXBuildFile; fileRef = 47942D53237CC3B500DEFAE3 /* PlacePagePreviewData.h */; settings = {ATTRIBUTES = (Public, ); }; };
47942D6E237CC3E800DEFAE3 /* PlacePagePreviewData+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 47942D54237CC3B500DEFAE3 /* PlacePagePreviewData+Core.h */; };
47942D6F237CC3F400DEFAE3 /* PlacePageInfoData.h in Headers */ = {isa = PBXBuildFile; fileRef = 47942D56237CC3B500DEFAE3 /* PlacePageInfoData.h */; settings = {ATTRIBUTES = (Public, ); }; };
47942D70237CC40400DEFAE3 /* PlacePageInfoData+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 47942D57237CC3B500DEFAE3 /* PlacePageInfoData+Core.h */; };
47942D71237CC40800DEFAE3 /* PlacePageInfoData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47942D58237CC3B500DEFAE3 /* PlacePageInfoData.mm */; };
47942D72237CC40B00DEFAE3 /* OpeningHours.h in Headers */ = {isa = PBXBuildFile; fileRef = 47942D59237CC3B500DEFAE3 /* OpeningHours.h */; settings = {ATTRIBUTES = (Public, ); }; };
47942D73237CC41400DEFAE3 /* OpeningHours.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47942D5A237CC3B500DEFAE3 /* OpeningHours.mm */; };
47942D82237CC52A00DEFAE3 /* MWMOpeningHours.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47942D80237CC52A00DEFAE3 /* MWMOpeningHours.mm */; };
47942D83237CC52E00DEFAE3 /* MWMOpeningHours.h in Headers */ = {isa = PBXBuildFile; fileRef = 47942D81237CC52A00DEFAE3 /* MWMOpeningHours.h */; settings = {ATTRIBUTES = (Public, ); }; };
47942D86237CC55500DEFAE3 /* MWMOpeningHoursCommon.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47942D85237CC55500DEFAE3 /* MWMOpeningHoursCommon.mm */; };
47942D87237CC55800DEFAE3 /* MWMOpeningHoursCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 47942D84237CC55400DEFAE3 /* MWMOpeningHoursCommon.h */; settings = {ATTRIBUTES = (Public, ); }; };
47942D88237CCA8800DEFAE3 /* PlacePagePreviewData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47942D55237CC3B500DEFAE3 /* PlacePagePreviewData.mm */; };
47942D9C237D927800DEFAE3 /* PlacePageBookmarkData.h in Headers */ = {isa = PBXBuildFile; fileRef = 47942D9A237D927800DEFAE3 /* PlacePageBookmarkData.h */; settings = {ATTRIBUTES = (Public, ); }; };
47942D9D237D927800DEFAE3 /* PlacePageBookmarkData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47942D9B237D927800DEFAE3 /* PlacePageBookmarkData.mm */; };
47942DA0237D954400DEFAE3 /* PlacePageBookmarkData+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 47942D9E237D954400DEFAE3 /* PlacePageBookmarkData+Core.h */; };
47942DAB237ED9FE00DEFAE3 /* IOpeningHoursLocalization.h in Headers */ = {isa = PBXBuildFile; fileRef = 47942DAA237ED9FE00DEFAE3 /* IOpeningHoursLocalization.h */; settings = {ATTRIBUTES = (Public, ); }; };
479F7047234F774100011E2E /* MWMFrameworkHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 479F7046234F774000011E2E /* MWMFrameworkHelper.mm */; };
479F704A234F785B00011E2E /* MWMTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 479F7049234F785B00011E2E /* MWMTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
479F704B234F78AB00011E2E /* MWMFrameworkHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 479F7045234F774000011E2E /* MWMFrameworkHelper.h */; settings = {ATTRIBUTES = (Public, ); }; };
479F7056234FB7F200011E2E /* MWMBookmarksManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 479F7054234FB7F100011E2E /* MWMBookmarksManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
479F7057234FB7F200011E2E /* MWMBookmarksManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 479F7055234FB7F100011E2E /* MWMBookmarksManager.mm */; };
479F705E234FBB8C00011E2E /* MWMBookmarkGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 479F705C234FBB8B00011E2E /* MWMBookmarkGroup.m */; };
479F705F234FBB8F00011E2E /* MWMBookmarkGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 479F705D234FBB8C00011E2E /* MWMBookmarkGroup.h */; settings = {ATTRIBUTES = (Public, ); }; };
479F7062234FBC4700011E2E /* MWMCarPlayBookmarkObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = 479F7061234FBC4600011E2E /* MWMCarPlayBookmarkObject.mm */; };
479F7063234FBC5900011E2E /* MWMCarPlayBookmarkObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 479F7060234FBC4500011E2E /* MWMCarPlayBookmarkObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
47C637D62354AEBE00E12DE0 /* MWMMapOverlayManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47C637D42354AEBD00E12DE0 /* MWMMapOverlayManager.mm */; };
47C637D72354AEBE00E12DE0 /* MWMMapOverlayManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 47C637D52354AEBE00E12DE0 /* MWMMapOverlayManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
47C637DC2354B79B00E12DE0 /* MWMSearchFrameworkHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47C637DA2354B79A00E12DE0 /* MWMSearchFrameworkHelper.mm */; };
47C637DD2354B79B00E12DE0 /* MWMSearchFrameworkHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 47C637DB2354B79B00E12DE0 /* MWMSearchFrameworkHelper.h */; settings = {ATTRIBUTES = (Public, ); }; };
47CA68DD2502022400671019 /* MWMBookmark.h in Headers */ = {isa = PBXBuildFile; fileRef = 47CA68DB2502022400671019 /* MWMBookmark.h */; settings = {ATTRIBUTES = (Public, ); }; };
47CA68DE2502022400671019 /* MWMBookmark.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47CA68DC2502022400671019 /* MWMBookmark.mm */; };
47CA68E12506C01F00671019 /* MWMBookmark+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 47CA68DF2506C01F00671019 /* MWMBookmark+Core.h */; };
47CA68E42506D29000671019 /* MWMBookmarkColor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47CA68E32506D29000671019 /* MWMBookmarkColor.mm */; };
47CA68E82506F61300671019 /* MWMTrack.h in Headers */ = {isa = PBXBuildFile; fileRef = 47CA68E62506F61300671019 /* MWMTrack.h */; settings = {ATTRIBUTES = (Public, ); }; };
47CA68E92506F61400671019 /* MWMTrack.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47CA68E72506F61300671019 /* MWMTrack.mm */; };
47CA68EC2506F6F100671019 /* MWMTrack+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 47CA68EA2506F6F100671019 /* MWMTrack+Core.h */; };
47D609DC234FE625008ECC47 /* MWMBookmarksObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 47D609DB234FE625008ECC47 /* MWMBookmarksObserver.h */; settings = {ATTRIBUTES = (Public, ); }; };
47D9019523AC22E500D9364C /* MWMMapUpdateInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 47D9019323AC22E500D9364C /* MWMMapUpdateInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
47D9019623AC22E500D9364C /* MWMMapUpdateInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47D9019423AC22E500D9364C /* MWMMapUpdateInfo.mm */; };
47D9019923AC236100D9364C /* MWMMapUpdateInfo+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 47D9019723AC236100D9364C /* MWMMapUpdateInfo+Core.h */; settings = {ATTRIBUTES = (Public, ); }; };
47E8163623B1889C008FD836 /* MWMStorage.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47E8163423B1889B008FD836 /* MWMStorage.mm */; };
47E8163723B188D3008FD836 /* MWMStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 47E8163523B1889B008FD836 /* MWMStorage.h */; settings = {ATTRIBUTES = (Public, ); }; };
47EEAFF42350CEDB005CF316 /* AppInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47EEAFF22350CEDA005CF316 /* AppInfo.mm */; };
47EEAFF62350CF48005CF316 /* AppInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 47EEAFF32350CEDB005CF316 /* AppInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
47EEAFF72350D060005CF316 /* MWMCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 47EEAFF52350CEF6005CF316 /* MWMCommon.h */; settings = {ATTRIBUTES = (Public, ); }; };
47F0D2152516847F00BC685E /* MWMBookmarksSection.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F0D2132516847F00BC685E /* MWMBookmarksSection.h */; settings = {ATTRIBUTES = (Public, ); }; };
47F0D2162516847F00BC685E /* MWMBookmarksSection.m in Sources */ = {isa = PBXBuildFile; fileRef = 47F0D2142516847F00BC685E /* MWMBookmarksSection.m */; };
47F4F1F923A3336C0022FD56 /* MWMMapNodeAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F4F1F723A3336B0022FD56 /* MWMMapNodeAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; };
47F4F1FA23A3336C0022FD56 /* MWMMapNodeAttributes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47F4F1F823A3336C0022FD56 /* MWMMapNodeAttributes.mm */; };
47F4F1FD23A3D1AC0022FD56 /* MWMMapNodeAttributes+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F4F1FB23A3D1AC0022FD56 /* MWMMapNodeAttributes+Core.h */; settings = {ATTRIBUTES = (Public, ); }; };
47F701EF238C86F000D18E95 /* PlacePageButtonsData.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F701ED238C86F000D18E95 /* PlacePageButtonsData.h */; settings = {ATTRIBUTES = (Public, ); }; };
47F701F0238C86F000D18E95 /* PlacePageButtonsData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47F701EE238C86F000D18E95 /* PlacePageButtonsData.mm */; };
47F701F3238C877C00D18E95 /* PlacePageButtonsData+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 47F701F1238C877C00D18E95 /* PlacePageButtonsData+Core.h */; };
9940622023EAC57900493D1A /* ElevationHeightPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 9940621E23EAC57900493D1A /* ElevationHeightPoint.h */; settings = {ATTRIBUTES = (Public, ); }; };
9940622123EAC57900493D1A /* ElevationHeightPoint.m in Sources */ = {isa = PBXBuildFile; fileRef = 9940621F23EAC57900493D1A /* ElevationHeightPoint.m */; };
9957FACE237AB01400855F48 /* DeepLinkParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 9957FACC237AB01400855F48 /* DeepLinkParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
9957FACF237AB01400855F48 /* DeepLinkParser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9957FACD237AB01400855F48 /* DeepLinkParser.mm */; };
9957FADB237ACB1100855F48 /* DeepLinkSearchData.h in Headers */ = {isa = PBXBuildFile; fileRef = 9957FAD9237ACB1100855F48 /* DeepLinkSearchData.h */; settings = {ATTRIBUTES = (Public, ); }; };
9957FADC237ACB1100855F48 /* DeepLinkSearchData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9957FADA237ACB1100855F48 /* DeepLinkSearchData.mm */; };
9957FAE8237AE5B000855F48 /* Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = 9957FAE6237AE5B000855F48 /* Logger.h */; settings = {ATTRIBUTES = (Public, ); }; };
9957FAE9237AE5B000855F48 /* Logger.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9957FAE7237AE5B000855F48 /* Logger.mm */; };
9974CA2923DF1968003FE824 /* ElevationProfileData.h in Headers */ = {isa = PBXBuildFile; fileRef = 9974CA2723DF1968003FE824 /* ElevationProfileData.h */; settings = {ATTRIBUTES = (Public, ); }; };
9974CA2A23DF1968003FE824 /* ElevationProfileData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9974CA2823DF1968003FE824 /* ElevationProfileData.mm */; };
9974CA2D23DF197B003FE824 /* ElevationProfileData+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 9974CA2B23DF197B003FE824 /* ElevationProfileData+Core.h */; };
AC6A585728057EF6003EABAF /* StringUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = AC6A585628057CC1003EABAF /* StringUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
CE838D272D9D834F00476DD0 /* PlacePagePhone.h in Headers */ = {isa = PBXBuildFile; fileRef = CE838D252D9D834F00476DD0 /* PlacePagePhone.h */; settings = {ATTRIBUTES = (Public, ); }; };
CE838D282D9D834F00476DD0 /* PlacePagePhone.m in Sources */ = {isa = PBXBuildFile; fileRef = CE838D262D9D834F00476DD0 /* PlacePagePhone.m */; };
ED0B1FF42CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.h in Headers */ = {isa = PBXBuildFile; fileRef = ED0B1FF22CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.h */; settings = {ATTRIBUTES = (Public, ); }; };
ED0B1FF52CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED0B1FF32CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.mm */; };
ED46DE3A2D09BE21007CACD6 /* PlacePageTrackData.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED46DE382D09BE21007CACD6 /* PlacePageTrackData.mm */; };
ED46DE3B2D09BE21007CACD6 /* PlacePageTrackData.h in Headers */ = {isa = PBXBuildFile; fileRef = ED46DE372D09BE21007CACD6 /* PlacePageTrackData.h */; settings = {ATTRIBUTES = (Public, ); }; };
ED46DE3C2D09BE21007CACD6 /* PlacePageTrackData+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = ED46DE392D09BE21007CACD6 /* PlacePageTrackData+Core.h */; };
ED7306F42D0C5D2400523AA1 /* TrackInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED7306F12D0C5D2400523AA1 /* TrackInfo.mm */; };
ED7306F52D0C5D2400523AA1 /* TrackInfo+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7306F22D0C5D2400523AA1 /* TrackInfo+Core.h */; };
ED7306F62D0C5D2400523AA1 /* TrackInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = ED7306F02D0C5D2400523AA1 /* TrackInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
ED965B0D2CD67A470049E39E /* DistanceFormatter.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED965B0A2CD67A470049E39E /* DistanceFormatter.mm */; };
ED965B102CD67A470049E39E /* DistanceFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = ED965B092CD67A470049E39E /* DistanceFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
ED965B132CD67A9B0049E39E /* AltitudeFormatter.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED965B122CD67A9B0049E39E /* AltitudeFormatter.mm */; };
ED965B142CD67A9B0049E39E /* AltitudeFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = ED965B112CD67A9B0049E39E /* AltitudeFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
ED965B222CD8F5AA0049E39E /* DurationFormatter.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED965B212CD8F5AA0049E39E /* DurationFormatter.mm */; };
ED965B232CD8F5AA0049E39E /* DurationFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = ED965B202CD8F5AA0049E39E /* DurationFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
ED965B2A2CDA1C440049E39E /* DateTimeFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED965B282CDA159C0049E39E /* DateTimeFormatter.swift */; };
EDC4E3512C5D222D009286A2 /* RecentlyDeletedCategory.mm in Sources */ = {isa = PBXBuildFile; fileRef = EDC4E34E2C5D222D009286A2 /* RecentlyDeletedCategory.mm */; };
EDC4E3522C5D222D009286A2 /* RecentlyDeletedCategory.h in Headers */ = {isa = PBXBuildFile; fileRef = EDC4E34F2C5D222D009286A2 /* RecentlyDeletedCategory.h */; settings = {ATTRIBUTES = (Public, ); }; };
EDC4E3532C5D222D009286A2 /* RecentlyDeletedCategory+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = EDC4E3502C5D222D009286A2 /* RecentlyDeletedCategory+Core.h */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
470015EF2342509C00EBF03D /* CoreApi.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreApi.framework; sourceTree = BUILT_PRODUCTS_DIR; };
470015F22342509C00EBF03D /* CoreApi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreApi.h; sourceTree = "<group>"; };
470015F32342509C00EBF03D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4700161223425CFD00EBF03D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
470016142342633D00EBF03D /* CoreApi.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = CoreApi.modulemap; sourceTree = "<group>"; };
471527382491EDA400E91BBA /* MWMBookmarkColor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMBookmarkColor.h; sourceTree = "<group>"; };
4718C4302355FC3C00640DF1 /* MWMNetworkPolicy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMNetworkPolicy.h; sourceTree = "<group>"; };
4718C4312355FC3C00640DF1 /* MWMNetworkPolicy.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMNetworkPolicy.mm; sourceTree = "<group>"; };
471AB98B23AB925D00F56D49 /* MWMMapSearchResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMMapSearchResult.h; sourceTree = "<group>"; };
471AB98C23AB925D00F56D49 /* MWMMapSearchResult.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMMapSearchResult.mm; sourceTree = "<group>"; };
471AB98F23AB931000F56D49 /* MWMMapSearchResult+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MWMMapSearchResult+Core.h"; sourceTree = "<group>"; };
472602A624092C5B00731135 /* MWMGeoUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMGeoUtil.h; sourceTree = "<group>"; };
472602A724092C5B00731135 /* MWMGeoUtil.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMGeoUtil.mm; sourceTree = "<group>"; };
475784C02344B421008291A4 /* Framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Framework.h; sourceTree = "<group>"; };
475784C12344B422008291A4 /* Framework.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Framework.cpp; sourceTree = "<group>"; };
47942D51237CC3B500DEFAE3 /* PlacePageData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlacePageData.h; sourceTree = "<group>"; };
47942D52237CC3B500DEFAE3 /* PlacePageData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlacePageData.mm; sourceTree = "<group>"; };
47942D53237CC3B500DEFAE3 /* PlacePagePreviewData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlacePagePreviewData.h; sourceTree = "<group>"; };
47942D54237CC3B500DEFAE3 /* PlacePagePreviewData+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PlacePagePreviewData+Core.h"; sourceTree = "<group>"; };
47942D55237CC3B500DEFAE3 /* PlacePagePreviewData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlacePagePreviewData.mm; sourceTree = "<group>"; };
47942D56237CC3B500DEFAE3 /* PlacePageInfoData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlacePageInfoData.h; sourceTree = "<group>"; };
47942D57237CC3B500DEFAE3 /* PlacePageInfoData+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PlacePageInfoData+Core.h"; sourceTree = "<group>"; };
47942D58237CC3B500DEFAE3 /* PlacePageInfoData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlacePageInfoData.mm; sourceTree = "<group>"; };
47942D59237CC3B500DEFAE3 /* OpeningHours.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpeningHours.h; sourceTree = "<group>"; };
47942D5A237CC3B500DEFAE3 /* OpeningHours.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OpeningHours.mm; sourceTree = "<group>"; };
47942D80237CC52A00DEFAE3 /* MWMOpeningHours.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMOpeningHours.mm; sourceTree = "<group>"; };
47942D81237CC52A00DEFAE3 /* MWMOpeningHours.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMOpeningHours.h; sourceTree = "<group>"; };
47942D84237CC55400DEFAE3 /* MWMOpeningHoursCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMOpeningHoursCommon.h; sourceTree = "<group>"; };
47942D85237CC55500DEFAE3 /* MWMOpeningHoursCommon.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMOpeningHoursCommon.mm; sourceTree = "<group>"; };
47942D9A237D927800DEFAE3 /* PlacePageBookmarkData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlacePageBookmarkData.h; sourceTree = "<group>"; };
47942D9B237D927800DEFAE3 /* PlacePageBookmarkData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlacePageBookmarkData.mm; sourceTree = "<group>"; };
47942D9E237D954400DEFAE3 /* PlacePageBookmarkData+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PlacePageBookmarkData+Core.h"; sourceTree = "<group>"; };
47942DAA237ED9FE00DEFAE3 /* IOpeningHoursLocalization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IOpeningHoursLocalization.h; sourceTree = "<group>"; };
479834EE23426C0100724D1E /* common-debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "common-debug.xcconfig"; path = "../../xcode/common-debug.xcconfig"; sourceTree = "<group>"; };
479834EF23426C0100724D1E /* common-release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "common-release.xcconfig"; path = "../../xcode/common-release.xcconfig"; sourceTree = "<group>"; };
479F7045234F774000011E2E /* MWMFrameworkHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMFrameworkHelper.h; sourceTree = "<group>"; };
479F7046234F774000011E2E /* MWMFrameworkHelper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMFrameworkHelper.mm; sourceTree = "<group>"; };
479F7049234F785B00011E2E /* MWMTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMTypes.h; sourceTree = "<group>"; };
479F7054234FB7F100011E2E /* MWMBookmarksManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMBookmarksManager.h; sourceTree = "<group>"; };
479F7055234FB7F100011E2E /* MWMBookmarksManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMBookmarksManager.mm; sourceTree = "<group>"; };
479F705C234FBB8B00011E2E /* MWMBookmarkGroup.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MWMBookmarkGroup.m; sourceTree = "<group>"; };
479F705D234FBB8C00011E2E /* MWMBookmarkGroup.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMBookmarkGroup.h; sourceTree = "<group>"; };
479F7060234FBC4500011E2E /* MWMCarPlayBookmarkObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMCarPlayBookmarkObject.h; sourceTree = "<group>"; };
479F7061234FBC4600011E2E /* MWMCarPlayBookmarkObject.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMCarPlayBookmarkObject.mm; sourceTree = "<group>"; };
47C637D42354AEBD00E12DE0 /* MWMMapOverlayManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMMapOverlayManager.mm; sourceTree = "<group>"; };
47C637D52354AEBE00E12DE0 /* MWMMapOverlayManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMMapOverlayManager.h; sourceTree = "<group>"; };
47C637DA2354B79A00E12DE0 /* MWMSearchFrameworkHelper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMSearchFrameworkHelper.mm; sourceTree = "<group>"; };
47C637DB2354B79B00E12DE0 /* MWMSearchFrameworkHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMSearchFrameworkHelper.h; sourceTree = "<group>"; };
47CA68DB2502022400671019 /* MWMBookmark.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMBookmark.h; sourceTree = "<group>"; };
47CA68DC2502022400671019 /* MWMBookmark.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMBookmark.mm; sourceTree = "<group>"; };
47CA68DF2506C01F00671019 /* MWMBookmark+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MWMBookmark+Core.h"; sourceTree = "<group>"; };
47CA68E32506D29000671019 /* MWMBookmarkColor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMBookmarkColor.mm; sourceTree = "<group>"; };
47CA68E52506D38700671019 /* MWMBookmarkColor+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MWMBookmarkColor+Core.h"; sourceTree = "<group>"; };
47CA68E62506F61300671019 /* MWMTrack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMTrack.h; sourceTree = "<group>"; };
47CA68E72506F61300671019 /* MWMTrack.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMTrack.mm; sourceTree = "<group>"; };
47CA68EA2506F6F100671019 /* MWMTrack+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MWMTrack+Core.h"; sourceTree = "<group>"; };
47D609DB234FE625008ECC47 /* MWMBookmarksObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMBookmarksObserver.h; sourceTree = "<group>"; };
47D9019323AC22E500D9364C /* MWMMapUpdateInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMMapUpdateInfo.h; sourceTree = "<group>"; };
47D9019423AC22E500D9364C /* MWMMapUpdateInfo.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMMapUpdateInfo.mm; sourceTree = "<group>"; };
47D9019723AC236100D9364C /* MWMMapUpdateInfo+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MWMMapUpdateInfo+Core.h"; sourceTree = "<group>"; };
47E8163423B1889B008FD836 /* MWMStorage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMStorage.mm; sourceTree = "<group>"; };
47E8163523B1889B008FD836 /* MWMStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMStorage.h; sourceTree = "<group>"; };
47EEAFF22350CEDA005CF316 /* AppInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = AppInfo.mm; sourceTree = "<group>"; };
47EEAFF32350CEDB005CF316 /* AppInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppInfo.h; sourceTree = "<group>"; };
47EEAFF52350CEF6005CF316 /* MWMCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMCommon.h; sourceTree = "<group>"; };
47F0D2132516847F00BC685E /* MWMBookmarksSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMBookmarksSection.h; sourceTree = "<group>"; };
47F0D2142516847F00BC685E /* MWMBookmarksSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MWMBookmarksSection.m; sourceTree = "<group>"; };
47F4F1F723A3336B0022FD56 /* MWMMapNodeAttributes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMMapNodeAttributes.h; sourceTree = "<group>"; };
47F4F1F823A3336C0022FD56 /* MWMMapNodeAttributes.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMMapNodeAttributes.mm; sourceTree = "<group>"; };
47F4F1FB23A3D1AC0022FD56 /* MWMMapNodeAttributes+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MWMMapNodeAttributes+Core.h"; sourceTree = "<group>"; };
47F701ED238C86F000D18E95 /* PlacePageButtonsData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlacePageButtonsData.h; sourceTree = "<group>"; };
47F701EE238C86F000D18E95 /* PlacePageButtonsData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlacePageButtonsData.mm; sourceTree = "<group>"; };
47F701F1238C877C00D18E95 /* PlacePageButtonsData+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PlacePageButtonsData+Core.h"; sourceTree = "<group>"; };
9940621E23EAC57900493D1A /* ElevationHeightPoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ElevationHeightPoint.h; sourceTree = "<group>"; };
9940621F23EAC57900493D1A /* ElevationHeightPoint.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ElevationHeightPoint.m; sourceTree = "<group>"; };
9957FACC237AB01400855F48 /* DeepLinkParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeepLinkParser.h; sourceTree = "<group>"; };
9957FACD237AB01400855F48 /* DeepLinkParser.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DeepLinkParser.mm; sourceTree = "<group>"; };
9957FAD9237ACB1100855F48 /* DeepLinkSearchData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeepLinkSearchData.h; sourceTree = "<group>"; };
9957FADA237ACB1100855F48 /* DeepLinkSearchData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DeepLinkSearchData.mm; sourceTree = "<group>"; };
9957FAE6237AE5B000855F48 /* Logger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Logger.h; sourceTree = "<group>"; };
9957FAE7237AE5B000855F48 /* Logger.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Logger.mm; sourceTree = "<group>"; };
9974CA2723DF1968003FE824 /* ElevationProfileData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ElevationProfileData.h; sourceTree = "<group>"; };
9974CA2823DF1968003FE824 /* ElevationProfileData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ElevationProfileData.mm; sourceTree = "<group>"; };
9974CA2B23DF197B003FE824 /* ElevationProfileData+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ElevationProfileData+Core.h"; sourceTree = "<group>"; };
AC6A585628057CC1003EABAF /* StringUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringUtils.h; sourceTree = "<group>"; };
CE838D252D9D834F00476DD0 /* PlacePagePhone.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlacePagePhone.h; sourceTree = "<group>"; };
CE838D262D9D834F00476DD0 /* PlacePagePhone.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlacePagePhone.m; sourceTree = "<group>"; };
ED0B1FF22CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeepLinkInAppFeatureHighlightData.h; sourceTree = "<group>"; };
ED0B1FF32CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DeepLinkInAppFeatureHighlightData.mm; sourceTree = "<group>"; };
ED46DE372D09BE21007CACD6 /* PlacePageTrackData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlacePageTrackData.h; sourceTree = "<group>"; };
ED46DE382D09BE21007CACD6 /* PlacePageTrackData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlacePageTrackData.mm; sourceTree = "<group>"; };
ED46DE392D09BE21007CACD6 /* PlacePageTrackData+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PlacePageTrackData+Core.h"; sourceTree = "<group>"; };
ED7306F02D0C5D2400523AA1 /* TrackInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TrackInfo.h; sourceTree = "<group>"; };
ED7306F12D0C5D2400523AA1 /* TrackInfo.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TrackInfo.mm; sourceTree = "<group>"; };
ED7306F22D0C5D2400523AA1 /* TrackInfo+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TrackInfo+Core.h"; sourceTree = "<group>"; };
ED965B092CD67A470049E39E /* DistanceFormatter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DistanceFormatter.h; sourceTree = "<group>"; };
ED965B0A2CD67A470049E39E /* DistanceFormatter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DistanceFormatter.mm; sourceTree = "<group>"; };
ED965B112CD67A9B0049E39E /* AltitudeFormatter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AltitudeFormatter.h; sourceTree = "<group>"; };
ED965B122CD67A9B0049E39E /* AltitudeFormatter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AltitudeFormatter.mm; sourceTree = "<group>"; };
ED965B202CD8F5AA0049E39E /* DurationFormatter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DurationFormatter.h; sourceTree = "<group>"; };
ED965B212CD8F5AA0049E39E /* DurationFormatter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DurationFormatter.mm; sourceTree = "<group>"; };
ED965B282CDA159C0049E39E /* DateTimeFormatter.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = DateTimeFormatter.swift; sourceTree = "<group>"; tabWidth = 4; };
EDC4E34E2C5D222D009286A2 /* RecentlyDeletedCategory.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RecentlyDeletedCategory.mm; sourceTree = "<group>"; };
EDC4E34F2C5D222D009286A2 /* RecentlyDeletedCategory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecentlyDeletedCategory.h; sourceTree = "<group>"; };
EDC4E3502C5D222D009286A2 /* RecentlyDeletedCategory+Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RecentlyDeletedCategory+Core.h"; sourceTree = "<group>"; };
FAA6D8CF29205D2D00E8D50D /* libcppjansson.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libcppjansson.a; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
470015EC2342509C00EBF03D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
470015E52342509C00EBF03D = {
isa = PBXGroup;
children = (
479834EE23426C0100724D1E /* common-debug.xcconfig */,
479834EF23426C0100724D1E /* common-release.xcconfig */,
4700160B2342562200EBF03D /* Target Support Files */,
470015F12342509C00EBF03D /* CoreApi */,
470015F02342509C00EBF03D /* Products */,
4700161123425CFC00EBF03D /* Frameworks */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
};
470015F02342509C00EBF03D /* Products */ = {
isa = PBXGroup;
children = (
470015EF2342509C00EBF03D /* CoreApi.framework */,
);
name = Products;
sourceTree = "<group>";
};
470015F12342509C00EBF03D /* CoreApi */ = {
isa = PBXGroup;
children = (
ED965B0B2CD67A470049E39E /* Formatting */,
47F4F1F623A333280022FD56 /* Storage */,
9957FAE5237AE59C00855F48 /* Logger */,
9957FAC1237AABD800855F48 /* DeepLink */,
47942D6A237CC3B500DEFAE3 /* PlacePageData */,
4718C42F2355FC0D00640DF1 /* NetworkPolicy */,
47C637D92354B76700E12DE0 /* Search */,
47C637D32354AEA800E12DE0 /* Traffic */,
479F704C234FB5C800011E2E /* Bookmarks */,
479F7048234F784B00011E2E /* Common */,
477C775523435B69001C5B4E /* Framework */,
470015F22342509C00EBF03D /* CoreApi.h */,
470015F32342509C00EBF03D /* Info.plist */,
);
path = CoreApi;
sourceTree = "<group>";
};
4700160B2342562200EBF03D /* Target Support Files */ = {
isa = PBXGroup;
children = (
470016142342633D00EBF03D /* CoreApi.modulemap */,
);
name = "Target Support Files";
sourceTree = "<group>";
};
4700161123425CFC00EBF03D /* Frameworks */ = {
isa = PBXGroup;
children = (
FAA6D8CF29205D2D00E8D50D /* libcppjansson.a */,
4700161223425CFD00EBF03D /* UIKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
4718C42F2355FC0D00640DF1 /* NetworkPolicy */ = {
isa = PBXGroup;
children = (
4718C4302355FC3C00640DF1 /* MWMNetworkPolicy.h */,
4718C4312355FC3C00640DF1 /* MWMNetworkPolicy.mm */,
);
path = NetworkPolicy;
sourceTree = "<group>";
};
477C775523435B69001C5B4E /* Framework */ = {
isa = PBXGroup;
children = (
475784C12344B422008291A4 /* Framework.cpp */,
475784C02344B421008291A4 /* Framework.h */,
479F7045234F774000011E2E /* MWMFrameworkHelper.h */,
479F7046234F774000011E2E /* MWMFrameworkHelper.mm */,
);
path = Framework;
sourceTree = "<group>";
};
47942D5B237CC3B500DEFAE3 /* Common */ = {
isa = PBXGroup;
children = (
ED46DE372D09BE21007CACD6 /* PlacePageTrackData.h */,
ED46DE382D09BE21007CACD6 /* PlacePageTrackData.mm */,
ED46DE392D09BE21007CACD6 /* PlacePageTrackData+Core.h */,
47942D9A237D927800DEFAE3 /* PlacePageBookmarkData.h */,
47942D9E237D954400DEFAE3 /* PlacePageBookmarkData+Core.h */,
47942D9B237D927800DEFAE3 /* PlacePageBookmarkData.mm */,
CE838D252D9D834F00476DD0 /* PlacePagePhone.h */,
CE838D262D9D834F00476DD0 /* PlacePagePhone.m */,
47942D53237CC3B500DEFAE3 /* PlacePagePreviewData.h */,
47942D54237CC3B500DEFAE3 /* PlacePagePreviewData+Core.h */,
47942D55237CC3B500DEFAE3 /* PlacePagePreviewData.mm */,
47942D56237CC3B500DEFAE3 /* PlacePageInfoData.h */,
47942D57237CC3B500DEFAE3 /* PlacePageInfoData+Core.h */,
47942D58237CC3B500DEFAE3 /* PlacePageInfoData.mm */,
47942D59237CC3B500DEFAE3 /* OpeningHours.h */,
47942D5A237CC3B500DEFAE3 /* OpeningHours.mm */,
47942D81237CC52A00DEFAE3 /* MWMOpeningHours.h */,
47942D80237CC52A00DEFAE3 /* MWMOpeningHours.mm */,
47942D84237CC55400DEFAE3 /* MWMOpeningHoursCommon.h */,
47942D85237CC55500DEFAE3 /* MWMOpeningHoursCommon.mm */,
47942DAA237ED9FE00DEFAE3 /* IOpeningHoursLocalization.h */,
47F701ED238C86F000D18E95 /* PlacePageButtonsData.h */,
47F701F1238C877C00D18E95 /* PlacePageButtonsData+Core.h */,
47F701EE238C86F000D18E95 /* PlacePageButtonsData.mm */,
);
path = Common;
sourceTree = "<group>";
};
47942D6A237CC3B500DEFAE3 /* PlacePageData */ = {
isa = PBXGroup;
children = (
47942D51237CC3B500DEFAE3 /* PlacePageData.h */,
47942D52237CC3B500DEFAE3 /* PlacePageData.mm */,
47942D5B237CC3B500DEFAE3 /* Common */,
9974CA2623DF1931003FE824 /* ElevationProfile */,
);
path = PlacePageData;
sourceTree = "<group>";
};
479F7048234F784B00011E2E /* Common */ = {
isa = PBXGroup;
children = (
AC6A585628057CC1003EABAF /* StringUtils.h */,
479F7049234F785B00011E2E /* MWMTypes.h */,
47EEAFF52350CEF6005CF316 /* MWMCommon.h */,
47EEAFF32350CEDB005CF316 /* AppInfo.h */,
47EEAFF22350CEDA005CF316 /* AppInfo.mm */,
472602A624092C5B00731135 /* MWMGeoUtil.h */,
472602A724092C5B00731135 /* MWMGeoUtil.mm */,
);
path = Common;
sourceTree = "<group>";
};
479F704C234FB5C800011E2E /* Bookmarks */ = {
isa = PBXGroup;
children = (
479F7054234FB7F100011E2E /* MWMBookmarksManager.h */,
479F7055234FB7F100011E2E /* MWMBookmarksManager.mm */,
479F705D234FBB8C00011E2E /* MWMBookmarkGroup.h */,
479F705C234FBB8B00011E2E /* MWMBookmarkGroup.m */,
479F7060234FBC4500011E2E /* MWMCarPlayBookmarkObject.h */,
479F7061234FBC4600011E2E /* MWMCarPlayBookmarkObject.mm */,
47D609DB234FE625008ECC47 /* MWMBookmarksObserver.h */,
471527382491EDA400E91BBA /* MWMBookmarkColor.h */,
47CA68E52506D38700671019 /* MWMBookmarkColor+Core.h */,
47CA68E32506D29000671019 /* MWMBookmarkColor.mm */,
47CA68DB2502022400671019 /* MWMBookmark.h */,
47CA68DF2506C01F00671019 /* MWMBookmark+Core.h */,
47CA68DC2502022400671019 /* MWMBookmark.mm */,
47CA68E62506F61300671019 /* MWMTrack.h */,
47CA68EA2506F6F100671019 /* MWMTrack+Core.h */,
ED7306F02D0C5D2400523AA1 /* TrackInfo.h */,
ED7306F12D0C5D2400523AA1 /* TrackInfo.mm */,
ED7306F22D0C5D2400523AA1 /* TrackInfo+Core.h */,
47CA68E72506F61300671019 /* MWMTrack.mm */,
47F0D2132516847F00BC685E /* MWMBookmarksSection.h */,
47F0D2142516847F00BC685E /* MWMBookmarksSection.m */,
EDC4E3542C5D2251009286A2 /* RecentlyDeletedCategory */,
);
path = Bookmarks;
sourceTree = "<group>";
};
47C637D32354AEA800E12DE0 /* Traffic */ = {
isa = PBXGroup;
children = (
47C637D52354AEBE00E12DE0 /* MWMMapOverlayManager.h */,
47C637D42354AEBD00E12DE0 /* MWMMapOverlayManager.mm */,
);
path = Traffic;
sourceTree = "<group>";
};
47C637D92354B76700E12DE0 /* Search */ = {
isa = PBXGroup;
children = (
47C637DB2354B79B00E12DE0 /* MWMSearchFrameworkHelper.h */,
47C637DA2354B79A00E12DE0 /* MWMSearchFrameworkHelper.mm */,
);
path = Search;
sourceTree = "<group>";
};
47F4F1F623A333280022FD56 /* Storage */ = {
isa = PBXGroup;
children = (
47E8163523B1889B008FD836 /* MWMStorage.h */,
47E8163423B1889B008FD836 /* MWMStorage.mm */,
47F4F1F723A3336B0022FD56 /* MWMMapNodeAttributes.h */,
47F4F1FB23A3D1AC0022FD56 /* MWMMapNodeAttributes+Core.h */,
47F4F1F823A3336C0022FD56 /* MWMMapNodeAttributes.mm */,
471AB98B23AB925D00F56D49 /* MWMMapSearchResult.h */,
471AB98F23AB931000F56D49 /* MWMMapSearchResult+Core.h */,
471AB98C23AB925D00F56D49 /* MWMMapSearchResult.mm */,
47D9019323AC22E500D9364C /* MWMMapUpdateInfo.h */,
47D9019723AC236100D9364C /* MWMMapUpdateInfo+Core.h */,
47D9019423AC22E500D9364C /* MWMMapUpdateInfo.mm */,
);
path = Storage;
sourceTree = "<group>";
};
9957FAC1237AABD800855F48 /* DeepLink */ = {
isa = PBXGroup;
children = (
9957FADA237ACB1100855F48 /* DeepLinkSearchData.mm */,
9957FAD9237ACB1100855F48 /* DeepLinkSearchData.h */,
9957FACC237AB01400855F48 /* DeepLinkParser.h */,
9957FACD237AB01400855F48 /* DeepLinkParser.mm */,
ED0B1FF22CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.h */,
ED0B1FF32CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.mm */,
);
path = DeepLink;
sourceTree = "<group>";
};
9957FAE5237AE59C00855F48 /* Logger */ = {
isa = PBXGroup;
children = (
9957FAE6237AE5B000855F48 /* Logger.h */,
9957FAE7237AE5B000855F48 /* Logger.mm */,
);
path = Logger;
sourceTree = "<group>";
};
9974CA2623DF1931003FE824 /* ElevationProfile */ = {
isa = PBXGroup;
children = (
9974CA2723DF1968003FE824 /* ElevationProfileData.h */,
9974CA2823DF1968003FE824 /* ElevationProfileData.mm */,
9974CA2B23DF197B003FE824 /* ElevationProfileData+Core.h */,
9940621E23EAC57900493D1A /* ElevationHeightPoint.h */,
9940621F23EAC57900493D1A /* ElevationHeightPoint.m */,
);
path = ElevationProfile;
sourceTree = "<group>";
};
ED965B0B2CD67A470049E39E /* Formatting */ = {
isa = PBXGroup;
children = (
ED965B112CD67A9B0049E39E /* AltitudeFormatter.h */,
ED965B122CD67A9B0049E39E /* AltitudeFormatter.mm */,
ED965B092CD67A470049E39E /* DistanceFormatter.h */,
ED965B0A2CD67A470049E39E /* DistanceFormatter.mm */,
ED965B282CDA159C0049E39E /* DateTimeFormatter.swift */,
ED965B202CD8F5AA0049E39E /* DurationFormatter.h */,
ED965B212CD8F5AA0049E39E /* DurationFormatter.mm */,
);
path = Formatting;
sourceTree = "<group>";
};
EDC4E3542C5D2251009286A2 /* RecentlyDeletedCategory */ = {
isa = PBXGroup;
children = (
EDC4E34F2C5D222D009286A2 /* RecentlyDeletedCategory.h */,
EDC4E34E2C5D222D009286A2 /* RecentlyDeletedCategory.mm */,
EDC4E3502C5D222D009286A2 /* RecentlyDeletedCategory+Core.h */,
);
path = RecentlyDeletedCategory;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
470015EA2342509C00EBF03D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
9957FAE8237AE5B000855F48 /* Logger.h in Headers */,
470015F42342509C00EBF03D /* CoreApi.h in Headers */,
479F705F234FBB8F00011E2E /* MWMBookmarkGroup.h in Headers */,
471AB99123AB931000F56D49 /* MWMMapSearchResult+Core.h in Headers */,
ED965B102CD67A470049E39E /* DistanceFormatter.h in Headers */,
47942D6D237CC3E300DEFAE3 /* PlacePagePreviewData.h in Headers */,
47CA68DD2502022400671019 /* MWMBookmark.h in Headers */,
ED7306F52D0C5D2400523AA1 /* TrackInfo+Core.h in Headers */,
ED7306F62D0C5D2400523AA1 /* TrackInfo.h in Headers */,
9940622023EAC57900493D1A /* ElevationHeightPoint.h in Headers */,
9957FACE237AB01400855F48 /* DeepLinkParser.h in Headers */,
9974CA2D23DF197B003FE824 /* ElevationProfileData+Core.h in Headers */,
AC6A585728057EF6003EABAF /* StringUtils.h in Headers */,
47F701EF238C86F000D18E95 /* PlacePageButtonsData.h in Headers */,
47EEAFF72350D060005CF316 /* MWMCommon.h in Headers */,
ED965B142CD67A9B0049E39E /* AltitudeFormatter.h in Headers */,
47F0D2152516847F00BC685E /* MWMBookmarksSection.h in Headers */,
ED46DE3B2D09BE21007CACD6 /* PlacePageTrackData.h in Headers */,
ED46DE3C2D09BE21007CACD6 /* PlacePageTrackData+Core.h in Headers */,
47CA68E82506F61300671019 /* MWMTrack.h in Headers */,
472602A824092C5B00731135 /* MWMGeoUtil.h in Headers */,
47D9019523AC22E500D9364C /* MWMMapUpdateInfo.h in Headers */,
9957FADB237ACB1100855F48 /* DeepLinkSearchData.h in Headers */,
479F7056234FB7F200011E2E /* MWMBookmarksManager.h in Headers */,
47942D83237CC52E00DEFAE3 /* MWMOpeningHours.h in Headers */,
4718C4322355FC3C00640DF1 /* MWMNetworkPolicy.h in Headers */,
47CA68EC2506F6F100671019 /* MWMTrack+Core.h in Headers */,
47F4F1FD23A3D1AC0022FD56 /* MWMMapNodeAttributes+Core.h in Headers */,
9974CA2923DF1968003FE824 /* ElevationProfileData.h in Headers */,
47C637DD2354B79B00E12DE0 /* MWMSearchFrameworkHelper.h in Headers */,
47942D70237CC40400DEFAE3 /* PlacePageInfoData+Core.h in Headers */,
47942DA0237D954400DEFAE3 /* PlacePageBookmarkData+Core.h in Headers */,
479F704B234F78AB00011E2E /* MWMFrameworkHelper.h in Headers */,
47D9019923AC236100D9364C /* MWMMapUpdateInfo+Core.h in Headers */,
47942D6E237CC3E800DEFAE3 /* PlacePagePreviewData+Core.h in Headers */,
47942D87237CC55800DEFAE3 /* MWMOpeningHoursCommon.h in Headers */,
CE838D272D9D834F00476DD0 /* PlacePagePhone.h in Headers */,
47942DAB237ED9FE00DEFAE3 /* IOpeningHoursLocalization.h in Headers */,
479F7063234FBC5900011E2E /* MWMCarPlayBookmarkObject.h in Headers */,
47E8163723B188D3008FD836 /* MWMStorage.h in Headers */,
47EEAFF62350CF48005CF316 /* AppInfo.h in Headers */,
EDC4E3532C5D222D009286A2 /* RecentlyDeletedCategory+Core.h in Headers */,
47F701F3238C877C00D18E95 /* PlacePageButtonsData+Core.h in Headers */,
47CA68E12506C01F00671019 /* MWMBookmark+Core.h in Headers */,
471527392491EDAA00E91BBA /* MWMBookmarkColor.h in Headers */,
47F4F1F923A3336C0022FD56 /* MWMMapNodeAttributes.h in Headers */,
479F704A234F785B00011E2E /* MWMTypes.h in Headers */,
47C637D72354AEBE00E12DE0 /* MWMMapOverlayManager.h in Headers */,
47942D9C237D927800DEFAE3 /* PlacePageBookmarkData.h in Headers */,
EDC4E3522C5D222D009286A2 /* RecentlyDeletedCategory.h in Headers */,
47942D72237CC40B00DEFAE3 /* OpeningHours.h in Headers */,
ED965B232CD8F5AA0049E39E /* DurationFormatter.h in Headers */,
ED0B1FF42CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.h in Headers */,
47942D6B237CC3D600DEFAE3 /* PlacePageData.h in Headers */,
47D609DC234FE625008ECC47 /* MWMBookmarksObserver.h in Headers */,
471AB98D23AB925D00F56D49 /* MWMMapSearchResult.h in Headers */,
47942D6F237CC3F400DEFAE3 /* PlacePageInfoData.h in Headers */,
475784C22344B422008291A4 /* Framework.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
470015EE2342509C00EBF03D /* CoreApi */ = {
isa = PBXNativeTarget;
buildConfigurationList = 470015F72342509C00EBF03D /* Build configuration list for PBXNativeTarget "CoreApi" */;
buildPhases = (
470015EA2342509C00EBF03D /* Headers */,
470015EB2342509C00EBF03D /* Sources */,
470015EC2342509C00EBF03D /* Frameworks */,
470015ED2342509C00EBF03D /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = CoreApi;
productName = CoreApi;
productReference = 470015EF2342509C00EBF03D /* CoreApi.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
470015E62342509C00EBF03D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
DefaultBuildSystemTypeForWorkspace = Latest;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = CoMaps;
TargetAttributes = {
470015EE2342509C00EBF03D = {
CreatedOnToolsVersion = 10.2;
LastSwiftMigration = 1600;
};
};
};
buildConfigurationList = 470015E92342509C00EBF03D /* Build configuration list for PBXProject "CoreApi" */;
compatibilityVersion = "Xcode 12.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 470015E52342509C00EBF03D;
productRefGroup = 470015F02342509C00EBF03D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
470015EE2342509C00EBF03D /* CoreApi */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
470015ED2342509C00EBF03D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
470015EB2342509C00EBF03D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9957FADC237ACB1100855F48 /* DeepLinkSearchData.mm in Sources */,
ED0B1FF52CAAE3FF006E31A4 /* DeepLinkInAppFeatureHighlightData.mm in Sources */,
9957FACF237AB01400855F48 /* DeepLinkParser.mm in Sources */,
47F4F1FA23A3336C0022FD56 /* MWMMapNodeAttributes.mm in Sources */,
47942D88237CCA8800DEFAE3 /* PlacePagePreviewData.mm in Sources */,
47942D9D237D927800DEFAE3 /* PlacePageBookmarkData.mm in Sources */,
47942D86237CC55500DEFAE3 /* MWMOpeningHoursCommon.mm in Sources */,
9974CA2A23DF1968003FE824 /* ElevationProfileData.mm in Sources */,
47942D82237CC52A00DEFAE3 /* MWMOpeningHours.mm in Sources */,
ED965B132CD67A9B0049E39E /* AltitudeFormatter.mm in Sources */,
47942D73237CC41400DEFAE3 /* OpeningHours.mm in Sources */,
47C637DC2354B79B00E12DE0 /* MWMSearchFrameworkHelper.mm in Sources */,
479F7062234FBC4700011E2E /* MWMCarPlayBookmarkObject.mm in Sources */,
47942D6C237CC3DE00DEFAE3 /* PlacePageData.mm in Sources */,
479F705E234FBB8C00011E2E /* MWMBookmarkGroup.m in Sources */,
ED7306F42D0C5D2400523AA1 /* TrackInfo.mm in Sources */,
47F0D2162516847F00BC685E /* MWMBookmarksSection.m in Sources */,
479F7057234FB7F200011E2E /* MWMBookmarksManager.mm in Sources */,
47942D71237CC40800DEFAE3 /* PlacePageInfoData.mm in Sources */,
479F7047234F774100011E2E /* MWMFrameworkHelper.mm in Sources */,
47D9019623AC22E500D9364C /* MWMMapUpdateInfo.mm in Sources */,
47CA68E42506D29000671019 /* MWMBookmarkColor.mm in Sources */,
471AB98E23AB925D00F56D49 /* MWMMapSearchResult.mm in Sources */,
47C637D62354AEBE00E12DE0 /* MWMMapOverlayManager.mm in Sources */,
475784C32344B422008291A4 /* Framework.cpp in Sources */,
4718C4332355FC3C00640DF1 /* MWMNetworkPolicy.mm in Sources */,
47CA68E92506F61400671019 /* MWMTrack.mm in Sources */,
472602A924092C5B00731135 /* MWMGeoUtil.mm in Sources */,
47F701F0238C86F000D18E95 /* PlacePageButtonsData.mm in Sources */,
9940622123EAC57900493D1A /* ElevationHeightPoint.m in Sources */,
CE838D282D9D834F00476DD0 /* PlacePagePhone.m in Sources */,
ED965B2A2CDA1C440049E39E /* DateTimeFormatter.swift in Sources */,
47EEAFF42350CEDB005CF316 /* AppInfo.mm in Sources */,
47E8163623B1889C008FD836 /* MWMStorage.mm in Sources */,
ED965B0D2CD67A470049E39E /* DistanceFormatter.mm in Sources */,
EDC4E3512C5D222D009286A2 /* RecentlyDeletedCategory.mm in Sources */,
ED46DE3A2D09BE21007CACD6 /* PlacePageTrackData.mm in Sources */,
47CA68DE2502022400671019 /* MWMBookmark.mm in Sources */,
9957FAE9237AE5B000855F48 /* Logger.mm in Sources */,
ED965B222CD8F5AA0049E39E /* DurationFormatter.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
470015F52342509C00EBF03D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 479834EE23426C0100724D1E /* common-debug.xcconfig */;
buildSettings = {
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
GCC_PRECOMPILE_PREFIX_HEADER = NO;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(OMIM_ROOT)/3party/glm",
"$(OMIM_ROOT)/3party/pugixml/pugixml/src",
);
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
470015F62342509C00EBF03D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 479834EF23426C0100724D1E /* common-release.xcconfig */;
buildSettings = {
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
ENABLE_NS_ASSERTIONS = NO;
GCC_PRECOMPILE_PREFIX_HEADER = NO;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(OMIM_ROOT)/3party/glm",
"$(OMIM_ROOT)/3party/pugixml/pugixml/src",
);
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
470015F82342509C00EBF03D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = CoreApi/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACH_O_TYPE = staticlib;
MODULEMAP_FILE = CoreApi.modulemap;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
};
name = Debug;
};
470015F92342509C00EBF03D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = CoreApi/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACH_O_TYPE = staticlib;
MODULEMAP_FILE = CoreApi.modulemap;
PRODUCT_BUNDLE_IDENTIFIER = app.comaps.coreapi;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
470015E92342509C00EBF03D /* Build configuration list for PBXProject "CoreApi" */ = {
isa = XCConfigurationList;
buildConfigurations = (
470015F52342509C00EBF03D /* Debug */,
470015F62342509C00EBF03D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
470015F72342509C00EBF03D /* Build configuration list for PBXNativeTarget "CoreApi" */ = {
isa = XCConfigurationList;
buildConfigurations = (
470015F82342509C00EBF03D /* Debug */,
470015F92342509C00EBF03D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 470015E62342509C00EBF03D /* Project object */;
}

View file

@ -0,0 +1,9 @@
#import "MWMBookmark.h"
#include <CoreApi/Framework.h>
@interface MWMBookmark (Core)
- (instancetype)initWithMarkId:(MWMMarkID)markId bookmarkData:(Bookmark const *)bookmark;
@end

View file

@ -0,0 +1,21 @@
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import "MWMBookmarkColor.h"
#import "MWMTypes.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(Bookmark)
@interface MWMBookmark : NSObject
@property(nonatomic, readonly) MWMMarkID bookmarkId;
@property(nonatomic, readonly) NSString *bookmarkName;
@property(nonatomic, readonly, nullable) NSString *bookmarkType;
@property(nonatomic, readonly) MWMBookmarkColor bookmarkColor;
@property(nonatomic, readonly) NSString *bookmarkIconName;
@property(nonatomic, readonly) CLLocationCoordinate2D locationCoordinate;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,29 @@
#import "MWMBookmark+Core.h"
#import "MWMBookmarkColor+Core.h"
@implementation MWMBookmark
@end
@implementation MWMBookmark (Core)
- (instancetype)initWithMarkId:(MWMMarkID)markId bookmarkData:(Bookmark const *)bookmark {
self = [super init];
if (self) {
_bookmarkId = markId;
_bookmarkName = @(bookmark->GetPreferredName().c_str());
_bookmarkColor = convertKmlColor(bookmark->GetColor());
_bookmarkIconName = [NSString stringWithFormat:@"%@%@",
@"ic_bm_",
[@(kml::ToString(bookmark->GetData().m_icon).c_str()) lowercaseString]];
auto const &types = bookmark->GetData().m_featureTypes;
if (!types.empty()) {
_bookmarkType = @(kml::GetLocalizedFeatureType(types).c_str());
}
auto latlon = bookmark->GetLatLon();
_locationCoordinate = CLLocationCoordinate2DMake(latlon.m_lat, latlon.m_lon);
}
return self;
}
@end

View file

@ -0,0 +1,11 @@
#import "MWMBookmarkColor.h"
#include <CoreApi/Framework.h>
#ifdef __cplusplus
extern "C" {
#endif
MWMBookmarkColor convertKmlColor(kml::PredefinedColor kmlColor);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,25 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, MWMBookmarkColor) {
MWMBookmarkColorNone = 0,
MWMBookmarkColorRed,
MWMBookmarkColorBlue,
MWMBookmarkColorPurple,
MWMBookmarkColorYellow,
MWMBookmarkColorPink,
MWMBookmarkColorBrown,
MWMBookmarkColorGreen,
MWMBookmarkColorOrange,
// Extended colors.
MWMBookmarkColorDeepPurple,
MWMBookmarkColorLightBlue,
MWMBookmarkColorCyan,
MWMBookmarkColorTeal,
MWMBookmarkColorLime,
MWMBookmarkColorDeepOrange,
MWMBookmarkColorGray,
MWMBookmarkColorBlueGray,
MWMBookmarkColorCount
} NS_SWIFT_NAME(BookmarkColor);

View file

@ -0,0 +1,42 @@
#import "MWMBookmarkColor+Core.h"
MWMBookmarkColor convertKmlColor(kml::PredefinedColor kmlColor) {
switch (kmlColor) {
case kml::PredefinedColor::None:
return MWMBookmarkColorNone;
case kml::PredefinedColor::Red:
return MWMBookmarkColorRed;
case kml::PredefinedColor::Blue:
return MWMBookmarkColorBlue;
case kml::PredefinedColor::Purple:
return MWMBookmarkColorPurple;
case kml::PredefinedColor::Yellow:
return MWMBookmarkColorYellow;
case kml::PredefinedColor::Pink:
return MWMBookmarkColorPink;
case kml::PredefinedColor::Brown:
return MWMBookmarkColorBrown;
case kml::PredefinedColor::Green:
return MWMBookmarkColorGreen;
case kml::PredefinedColor::Orange:
return MWMBookmarkColorOrange;
case kml::PredefinedColor::DeepPurple:
return MWMBookmarkColorDeepPurple;
case kml::PredefinedColor::LightBlue:
return MWMBookmarkColorLightBlue;
case kml::PredefinedColor::Cyan:
return MWMBookmarkColorCyan;
case kml::PredefinedColor::Teal:
return MWMBookmarkColorTeal;
case kml::PredefinedColor::Lime:
return MWMBookmarkColorLime;
case kml::PredefinedColor::DeepOrange:
return MWMBookmarkColorDeepOrange;
case kml::PredefinedColor::Gray:
return MWMBookmarkColorGray;
case kml::PredefinedColor::BlueGray:
return MWMBookmarkColorBlueGray;
case kml::PredefinedColor::Count:
return MWMBookmarkColorCount;
}
}

View file

@ -0,0 +1,41 @@
#import <Foundation/Foundation.h>
#import "MWMTypes.h"
@class MWMBookmark;
@class MWMTrack;
NS_ASSUME_NONNULL_BEGIN
@class MWMBookmarksManager;
NS_SWIFT_NAME(BookmarkGroup)
@interface MWMBookmarkGroup : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithCategoryId:(MWMMarkGroupID)categoryId
bookmarksManager:(MWMBookmarksManager *)manager;
@property(nonatomic, readonly) MWMMarkGroupID categoryId;
@property(nonatomic, readonly) NSString *title;
@property(nonatomic, readonly) NSString *author;
@property(nonatomic, readonly) NSString *annotation;
@property(nonatomic, readonly) NSString *detailedAnnotation;
@property(nonatomic, readonly) NSString *serverId;
@property(nonatomic, readonly, nullable) NSURL *imageUrl;
@property(nonatomic, readonly) NSInteger bookmarksCount;
@property(nonatomic, readonly) NSInteger trackCount;
@property(nonatomic, readonly, getter=isVisible) BOOL visible;
@property(nonatomic, readonly, getter=isEmpty) BOOL empty;
@property(nonatomic, readonly) BOOL hasDescription;
@property(nonatomic, readonly) BOOL isHtmlDescription;
@property(nonatomic, readonly) MWMBookmarkGroupAccessStatus accessStatus;
@property(nonatomic, readonly) NSArray<MWMBookmark *> *bookmarks;
@property(nonatomic, readonly) NSArray<MWMTrack *> *tracks;
@property(nonatomic, readonly) NSArray<MWMBookmarkGroup *> *collections;
@property(nonatomic, readonly) NSArray<MWMBookmarkGroup *> *categories;
@property(nonatomic, readonly) MWMBookmarkGroupType type;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,91 @@
#import "MWMBookmarkGroup.h"
#import "MWMBookmarksManager.h"
@interface MWMBookmarkGroup ()
@property(weak, nonatomic) MWMBookmarksManager *manager;
@end
@implementation MWMBookmarkGroup
- (instancetype)initWithCategoryId:(MWMMarkGroupID)categoryId
bookmarksManager:(MWMBookmarksManager *)manager {
self = [super init];
if (self) {
_manager = manager;
_categoryId = categoryId;
}
return self;
}
- (NSString *)title {
return [self.manager getCategoryName:self.categoryId];
}
- (NSString *)author {
return [self.manager getCategoryAuthorName:self.categoryId];
}
- (NSString *)annotation {
return [self.manager getCategoryAnnotation:self.categoryId];
}
- (NSString *)detailedAnnotation {
return [self.manager getCategoryDescription:self.categoryId];
}
- (NSURL *)imageUrl {
return [self.manager getCategoryImageUrl:self.categoryId];
}
- (NSInteger)bookmarksCount {
return [self.manager getCategoryMarksCount:self.categoryId];
}
- (NSInteger)trackCount {
return [self.manager getCategoryTracksCount:self.categoryId];
}
- (BOOL)isVisible {
return [self.manager isCategoryVisible:self.categoryId];
}
- (BOOL)isEmpty {
return [self.manager isCategoryEmpty:self.categoryId];
}
- (BOOL)hasDescription {
return [self.manager hasExtraInfo:self.categoryId];
}
- (MWMBookmarkGroupAccessStatus)accessStatus {
return [self.manager getCategoryAccessStatus:self.categoryId];
}
- (NSArray<MWMBookmark *> *)bookmarks {
return [self.manager bookmarksForGroup:self.categoryId];
}
- (NSArray<MWMTrack *> *)tracks {
return [self.manager tracksForGroup:self.categoryId];
}
- (NSArray<MWMBookmarkGroup *> *)collections {
return [self.manager collectionsForGroup:self.categoryId];
}
- (NSArray<MWMBookmarkGroup *> *)categories {
return [self.manager categoriesForGroup:self.categoryId];
}
- (MWMBookmarkGroupType)type {
return [self.manager getCategoryGroupType:self.categoryId];
}
- (BOOL)isHtmlDescription {
return [self.manager isHtmlDescription:self.categoryId];
}
@end

View file

@ -0,0 +1,176 @@
#import <CoreLocation/CoreLocation.h>
#import "MWMTypes.h"
#import "MWMBookmarksObserver.h"
#import "PlacePageBookmarkData.h"
@class MWMBookmark;
@class MWMBookmarkGroup;
@class MWMBookmarksSection;
@class MWMCarPlayBookmarkObject;
@class MWMTrack;
@class RecentlyDeletedCategory;
@class UIColor;
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, MWMBookmarksSortingType) {
MWMBookmarksSortingTypeByType,
MWMBookmarksSortingTypeByDistance,
MWMBookmarksSortingTypeByTime,
MWMBookmarksSortingTypeByName
} NS_SWIFT_NAME(BookmarksSortingType);
typedef void (^PingCompletionBlock)(BOOL success);
typedef void (^ElevationPointChangedBlock)(double distance);
typedef void (^SearchBookmarksCompletionBlock)(NSArray<MWMBookmark *> *bookmarks);
typedef void (^SortBookmarksCompletionBlock)(NSArray<MWMBookmarksSection *> * _Nullable sortedSections);
typedef void (^SharingResultCompletionHandler)(MWMBookmarksShareStatus status, NSURL * _Nullable urlToALocalFile);
@protocol RecentlyDeletedCategoriesManager <NSObject>
- (uint64_t)recentlyDeletedCategoriesCount;
- (NSArray<RecentlyDeletedCategory *> *)getRecentlyDeletedCategories;
- (void)deleteRecentlyDeletedCategoryAtURLs:(NSArray<NSURL *> *)urls;
- (void)recoverRecentlyDeletedCategoriesAtURLs:(NSArray<NSURL *> *)urls;
@end
NS_SWIFT_NAME(BookmarksManager)
@interface MWMBookmarksManager : NSObject<BookmarksObservable, RecentlyDeletedCategoriesManager>
+ (MWMBookmarksManager *)sharedManager;
- (BOOL)areBookmarksLoaded;
- (void)loadBookmarks;
- (void)loadBookmarkFile:(NSURL *)url;
- (void)reloadCategoryAtFilePath:(NSString *)filePath;
- (void)deleteCategoryAtFilePath:(NSString *)filePath;
- (BOOL)areAllCategoriesEmpty;
- (BOOL)isCategoryEmpty:(MWMMarkGroupID)groupId;
- (void)prepareForSearch:(MWMMarkGroupID)groupId;
- (NSString *)getCategoryName:(MWMMarkGroupID)groupId;
- (uint64_t)getCategoryMarksCount:(MWMMarkGroupID)groupId;
- (uint64_t)getCategoryTracksCount:(MWMMarkGroupID)groupId;
- (MWMBookmarkGroupAccessStatus)getCategoryAccessStatus:(MWMMarkGroupID)groupId;
- (NSString *)getCategoryAnnotation:(MWMMarkGroupID)groupId;
- (NSString *)getCategoryDescription:(MWMMarkGroupID)groupId;
- (NSString *)getCategoryAuthorName:(MWMMarkGroupID)groupId;
- (NSString *)getCategoryAuthorId:(MWMMarkGroupID)groupId;
- (MWMBookmarkGroupType)getCategoryGroupType:(MWMMarkGroupID)groupId;
- (nullable NSURL *)getCategoryImageUrl:(MWMMarkGroupID)groupId;
- (BOOL)hasExtraInfo:(MWMMarkGroupID)groupId;
- (BOOL)isHtmlDescription:(MWMMarkGroupID)groupId;
- (MWMMarkGroupID)createCategoryWithName:(NSString *)name;
- (void)setCategory:(MWMMarkGroupID)groupId name:(NSString *)name;
- (void)setCategory:(MWMMarkGroupID)groupId description:(NSString *)name;
- (BOOL)isCategoryVisible:(MWMMarkGroupID)groupId;
- (void)setCategory:(MWMMarkGroupID)groupId isVisible:(BOOL)isVisible;
- (void)setUserCategoriesVisible:(BOOL)isVisible;
- (void)deleteCategory:(MWMMarkGroupID)groupId;
- (BOOL)checkCategoryName:(NSString *)name;
- (BOOL)hasCategory:(MWMMarkGroupID)groupId;
- (BOOL)hasBookmark:(MWMMarkID)bookmarkId;
- (BOOL)hasTrack:(MWMTrackID)trackId;
- (NSArray<NSNumber *> *)availableSortingTypes:(MWMMarkGroupID)groupId hasMyPosition:(BOOL)hasMyPosition;
- (void)sortBookmarks:(MWMMarkGroupID)groupId
sortingType:(MWMBookmarksSortingType)sortingType
location:(CLLocation * _Nullable)location
completion:(SortBookmarksCompletionBlock)completionBlock;
- (BOOL)hasLastSortingType:(MWMMarkGroupID)groupId;
- (MWMBookmarksSortingType)lastSortingType:(MWMMarkGroupID)groupId;
- (void)resetLastSortingType:(MWMMarkGroupID)groupId;
- (NSArray<MWMCarPlayBookmarkObject *> *)bookmarksForCategory:(MWMMarkGroupID)categoryId;
- (MWMMarkIDCollection)bookmarkIdsForCategory:(MWMMarkGroupID)categoryId;
- (void)deleteBookmark:(MWMMarkID)bookmarkId;
- (void)deleteTrack:(MWMTrackID)trackId;
- (MWMBookmark *)bookmarkWithId:(MWMMarkID)bookmarkId;
- (MWMTrack *)trackWithId:(MWMTrackID)trackId;
- (NSArray<MWMBookmark *> *)bookmarksForGroup:(MWMMarkGroupID)groupId;
- (NSArray<MWMTrack *> *)tracksForGroup:(MWMMarkGroupID)groupId;
- (NSArray<MWMBookmarkGroup *> *)collectionsForGroup:(MWMMarkGroupID)groupId;
- (NSArray<MWMBookmarkGroup *> *)categoriesForGroup:(MWMMarkGroupID)groupId;
- (void)searchBookmarksGroup:(MWMMarkGroupID)groupId
text:(NSString *)text
completion:(SearchBookmarksCompletionBlock)completion;
- (MWMTrackIDCollection)trackIdsForCategory:(MWMMarkGroupID)categoryId;
/**
Shares a specific category with the given group ID.
@param groupId The identifier for the category to be shared.
@param fileType Text/Binary/GPX
@param completion A block that handles the result of the share operation and takes two parameters:
- status: The status of the share operation, of type `MWMBookmarksShareStatus`.
- urlToALocalFile: The local file URL containing the shared data. This parameter is guaranteed to be non-nil only if `status` is `MWMBookmarksShareStatusSuccess`. In other cases, it will be nil.
*/
- (void)shareCategory:(MWMMarkGroupID)groupId fileType:(MWMKmlFileType)fileType completion:(SharingResultCompletionHandler)completion;
/**
Shares all categories.
@param completion A block that handles the result of the share operation and takes two parameters:
- status: The status of the share operation, of type `MWMBookmarksShareStatus`.
- urlToALocalFile: The local file URL containing the shared data. This parameter is guaranteed to be non-nil only if `status` is `MWMBookmarksShareStatusSuccess`. In other cases, it will be nil.
*/
- (void)shareAllCategoriesWithCompletion:(SharingResultCompletionHandler)completion;
/**
Shares a specific track with the given track ID.
@param trackId The identifier for the track to be shared.
@param fileType Text/Binary/GPX
*/
- (void)shareTrack:(MWMTrackID)trackId fileType:(MWMKmlFileType)fileType completion:(SharingResultCompletionHandler)completion;
- (void)finishSharing;
- (void)setNotificationsEnabled:(BOOL)enabled;
- (BOOL)areNotificationsEnabled;
- (NSArray<MWMBookmarkGroup *> *)sortedUserCategories;
- (size_t)userCategoriesCount;
- (MWMBookmarkGroup *)categoryWithId:(MWMMarkGroupID)groupId;
- (MWMBookmarkGroup *)categoryForBookmarkId:(MWMMarkID)bookmarkId;
- (MWMBookmarkGroup *)categoryForTrackId:(MWMTrackID)trackId;
- (NSString *)descriptionForBookmarkId:(MWMMarkID)bookmarkId;
- (void)updateBookmark:(MWMMarkID)bookmarkId
setGroupId:(MWMMarkGroupID)groupId
title:(NSString *)title
color:(MWMBookmarkColor)color
description:(NSString *)description;
- (void)updateBookmark:(MWMMarkID)bookmarkId
setColor:(MWMBookmarkColor)color;
- (void)moveBookmark:(MWMMarkID)bookmarkId
toGroupId:(MWMMarkGroupID)groupId;
- (void)updateTrack:(MWMTrackID)trackId
setGroupId:(MWMMarkGroupID)groupId
color:(UIColor *)color
title:(NSString *)title;
- (void)updateTrack:(MWMTrackID)trackId
setColor:(UIColor *)color;
- (void)moveTrack:(MWMTrackID)trackId
toGroupId:(MWMMarkGroupID)groupId;
- (BOOL)hasRecentlyDeletedBookmark;
- (instancetype)init __attribute__((unavailable("call +manager instead")));
- (instancetype)copy __attribute__((unavailable("call +manager instead")));
- (instancetype)copyWithZone:(NSZone *)zone __attribute__((unavailable("call +manager instead")));
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable("call +manager instead")));
+ (instancetype) new __attribute__((unavailable("call +manager instead")));
- (void)setElevationActivePoint:(CLLocationCoordinate2D)point distance:(double)distance trackId:(uint64_t)trackId;
- (void)setElevationActivePointChanged:(uint64_t)trackId callback:(ElevationPointChangedBlock)callback;
- (void)resetElevationActivePointChanged;
- (void)setElevationMyPositionChanged:(uint64_t)trackId callback:(ElevationPointChangedBlock)callback;
- (void)resetElevationMyPositionChanged;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,884 @@
#import "MWMBookmarksManager.h"
#import "MWMBookmark+Core.h"
#import "MWMBookmarksSection.h"
#import "MWMBookmarkGroup.h"
#import "MWMCarPlayBookmarkObject.h"
#import "MWMTrack+Core.h"
#import "RecentlyDeletedCategory+Core.h"
#include "Framework.h"
#include "map/bookmarks_search_params.hpp"
#include "coding/internal/file_data.hpp"
#include "base/stl_helpers.hpp"
#include "base/string_utils.hpp"
#include <utility>
static kml::PredefinedColor kmlColorFromBookmarkColor(MWMBookmarkColor bookmarkColor) {
switch (bookmarkColor) {
case MWMBookmarkColorNone:
return kml::PredefinedColor::None;
case MWMBookmarkColorRed:
return kml::PredefinedColor::Red;
case MWMBookmarkColorBlue:
return kml::PredefinedColor::Blue;
case MWMBookmarkColorPurple:
return kml::PredefinedColor::Purple;
case MWMBookmarkColorYellow:
return kml::PredefinedColor::Yellow;
case MWMBookmarkColorPink:
return kml::PredefinedColor::Pink;
case MWMBookmarkColorBrown:
return kml::PredefinedColor::Brown;
case MWMBookmarkColorGreen:
return kml::PredefinedColor::Green;
case MWMBookmarkColorOrange:
return kml::PredefinedColor::Orange;
case MWMBookmarkColorDeepPurple:
return kml::PredefinedColor::DeepPurple;
case MWMBookmarkColorLightBlue:
return kml::PredefinedColor::LightBlue;
case MWMBookmarkColorCyan:
return kml::PredefinedColor::Cyan;
case MWMBookmarkColorTeal:
return kml::PredefinedColor::Teal;
case MWMBookmarkColorLime:
return kml::PredefinedColor::Lime;
case MWMBookmarkColorDeepOrange:
return kml::PredefinedColor::DeepOrange;
case MWMBookmarkColorGray:
return kml::PredefinedColor::Gray;
case MWMBookmarkColorBlueGray:
return kml::PredefinedColor::BlueGray;
case MWMBookmarkColorCount:
return kml::PredefinedColor::Count;
}
}
static MWMBookmarksSortingType convertSortingType(BookmarkManager::SortingType const &sortingType) {
switch (sortingType) {
case BookmarkManager::SortingType::ByType:
return MWMBookmarksSortingTypeByType;
case BookmarkManager::SortingType::ByDistance:
return MWMBookmarksSortingTypeByDistance;
case BookmarkManager::SortingType::ByTime:
return MWMBookmarksSortingTypeByTime;
case BookmarkManager::SortingType::ByName:
return MWMBookmarksSortingTypeByName;
}
}
static BookmarkManager::SortingType convertSortingTypeToCore(MWMBookmarksSortingType sortingType) {
switch (sortingType) {
case MWMBookmarksSortingTypeByType:
return BookmarkManager::SortingType::ByType;
case MWMBookmarksSortingTypeByDistance:
return BookmarkManager::SortingType::ByDistance;
case MWMBookmarksSortingTypeByTime:
return BookmarkManager::SortingType::ByTime;
case MWMBookmarksSortingTypeByName:
return BookmarkManager::SortingType::ByName;
}
}
static KmlFileType convertFileTypeToCore(MWMKmlFileType fileType) {
switch (fileType) {
case MWMKmlFileTypeText:
return KmlFileType::Text;
case MWMKmlFileTypeBinary:
return KmlFileType::Binary;
case MWMKmlFileTypeGpx:
return KmlFileType::Gpx;
}
}
@interface MWMBookmarksManager ()
@property(nonatomic, readonly) BookmarkManager & bm;
@property(nonatomic) NSHashTable<id<MWMBookmarksObserver>> * observers;
@property(nonatomic) BOOL areBookmarksLoaded;
@property(nonatomic) NSURL * shareCategoryURL;
@property(nonatomic) NSInteger lastSearchId;
@property(nonatomic) NSInteger lastSortId;
@end
@implementation MWMBookmarksManager
+ (instancetype)sharedManager
{
static MWMBookmarksManager * manager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc] initManager];
});
return manager;
}
- (BookmarkManager &)bm
{
return GetFramework().GetBookmarkManager();
}
- (void)addObserver:(id<MWMBookmarksObserver>)observer
{
[self.observers addObject:observer];
}
- (void)removeObserver:(id<MWMBookmarksObserver>)observer
{
[self.observers removeObject:observer];
}
- (instancetype)initManager
{
self = [super init];
if (self)
{
_observers = [NSHashTable<id<MWMBookmarksObserver>> weakObjectsHashTable];
[self registerBookmarksObserver];
}
return self;
}
- (void)registerBookmarksObserver
{
BookmarkManager::AsyncLoadingCallbacks bookmarkCallbacks;
{
__weak auto wSelf = self;
bookmarkCallbacks.m_onStarted = [wSelf]() {
wSelf.areBookmarksLoaded = NO;
};
}
{
__weak auto wSelf = self;
bookmarkCallbacks.m_onFinished = [wSelf]() {
__strong auto self = wSelf;
if (!self)
return;
self.areBookmarksLoaded = YES;
[self loopObservers:^(id<MWMBookmarksObserver> observer) {
if ([observer respondsToSelector:@selector(onBookmarksLoadFinished)])
[observer onBookmarksLoadFinished];
}];
};
}
{
__weak auto wSelf = self;
bookmarkCallbacks.m_onFileSuccess = [wSelf](std::string const & filePath,
bool isTemporaryFile) {
__strong __typeof(self) self = wSelf;
[self loopObservers:^(id<MWMBookmarksObserver> observer) {
if ([observer respondsToSelector:@selector(onBookmarksFileLoadSuccess)])
[observer onBookmarksFileLoadSuccess];
}];
};
}
{
__weak auto wSelf = self;
bookmarkCallbacks.m_onFileError = [wSelf](std::string const & filePath, bool isTemporaryFile) {
__strong __typeof(self) self = wSelf;
[self loopObservers:^(id<MWMBookmarksObserver> observer) {
if ([observer respondsToSelector:@selector(onBookmarksFileLoadError)])
[observer onBookmarksFileLoadError];
}];
};
}
self.bm.SetAsyncLoadingCallbacks(std::move(bookmarkCallbacks));
}
#pragma mark - Bookmarks loading
- (BOOL)areBookmarksLoaded
{
return _areBookmarksLoaded;
}
- (void)loadBookmarks
{
self.bm.LoadBookmarks();
}
- (void)loadBookmarkFile:(NSURL *)url
{
self.bm.LoadBookmark(url.path.UTF8String, false /* isTemporaryFile */);
}
- (void)reloadCategoryAtFilePath:(NSString *)filePath
{
self.bm.ReloadBookmark(filePath.UTF8String);
}
- (void)deleteCategoryAtFilePath:(NSString *)filePath
{
auto const groupId = self.bm.GetCategoryByFileName(filePath.UTF8String);
if (groupId)
[self deleteCategory:groupId];
}
#pragma mark - Categories
- (BOOL)areAllCategoriesEmpty
{
return self.bm.AreAllCategoriesEmpty();
}
- (BOOL)isCategoryEmpty:(MWMMarkGroupID)groupId {
return self.bm.HasBmCategory(groupId) && self.bm.IsCategoryEmpty(groupId);
}
- (void)prepareForSearch:(MWMMarkGroupID)groupId {
self.bm.PrepareForSearch(groupId);
}
- (NSString *)getCategoryName:(MWMMarkGroupID)groupId
{
return @(self.bm.GetCategoryName(groupId).c_str());
}
- (uint64_t)getCategoryMarksCount:(MWMMarkGroupID)groupId
{
return self.bm.GetUserMarkIds(groupId).size();
}
- (uint64_t)getCategoryTracksCount:(MWMMarkGroupID)groupId
{
return self.bm.GetTrackIds(groupId).size();
}
- (MWMBookmarkGroupAccessStatus)getCategoryAccessStatus:(MWMMarkGroupID)groupId
{
switch (self.bm.GetCategoryData(groupId).m_accessRules)
{
case kml::AccessRules::Local:
return MWMBookmarkGroupAccessStatusLocal;
case kml::AccessRules::Public:
return MWMBookmarkGroupAccessStatusPublic;
case kml::AccessRules::DirectLink:
return MWMBookmarkGroupAccessStatusPrivate;
case kml::AccessRules::AuthorOnly:
return MWMBookmarkGroupAccessStatusAuthorOnly;
case kml::AccessRules::P2P:
case kml::AccessRules::Paid:
case kml::AccessRules::Count:
return MWMBookmarkGroupAccessStatusOther;
}
}
- (NSString *)getCategoryAnnotation:(MWMMarkGroupID)groupId {
return @(GetPreferredBookmarkStr(self.bm.GetCategoryData(groupId).m_annotation).c_str());
}
- (NSString *)getCategoryDescription:(MWMMarkGroupID)groupId
{
return @(GetPreferredBookmarkStr(self.bm.GetCategoryData(groupId).m_description).c_str());
}
- (NSString *)getCategoryAuthorName:(MWMMarkGroupID)groupId
{
return @(self.bm.GetCategoryData(groupId).m_authorName.c_str());
}
- (NSString *)getCategoryAuthorId:(MWMMarkGroupID)groupId
{
return @(self.bm.GetCategoryData(groupId).m_authorId.c_str());
}
- (MWMBookmarkGroupType)getCategoryGroupType:(MWMMarkGroupID)groupId {
if (self.bm.IsCompilation(groupId) == false) {
return MWMBookmarkGroupTypeRoot;
}
switch (self.bm.GetCompilationType(groupId)) {
case kml::CompilationType::Category:
return MWMBookmarkGroupTypeCategory;
case kml::CompilationType::Collection:
return MWMBookmarkGroupTypeCollection;
case kml::CompilationType::Day:
return MWMBookmarkGroupTypeDay;
}
return MWMBookmarkGroupTypeRoot;
}
- (nullable NSURL *)getCategoryImageUrl:(MWMMarkGroupID)groupId {
NSString *urlString = @(self.bm.GetCategoryData(groupId).m_imageUrl.c_str());
return [NSURL URLWithString:urlString];
}
- (BOOL)hasExtraInfo:(MWMMarkGroupID)groupId {
auto data = self.bm.GetCategoryData(groupId);
return !data.m_description.empty() || !data.m_annotation.empty();
}
- (BOOL)isHtmlDescription:(MWMMarkGroupID)groupId {
auto const description = GetPreferredBookmarkStr(self.bm.GetCategoryData(groupId).m_description);
return strings::IsHTML(description);
}
- (MWMMarkGroupID)createCategoryWithName:(NSString *)name
{
auto groupId = self.bm.CreateBookmarkCategory(name.UTF8String);
self.bm.SetLastEditedBmCategory(groupId);
return groupId;
}
- (void)setCategory:(MWMMarkGroupID)groupId name:(NSString *)name
{
self.bm.GetEditSession().SetCategoryName(groupId, name.UTF8String);
}
- (void)setCategory:(MWMMarkGroupID)groupId description:(NSString *)name
{
self.bm.GetEditSession().SetCategoryDescription(groupId, name.UTF8String);
}
- (BOOL)isCategoryVisible:(MWMMarkGroupID)groupId
{
return self.bm.IsVisible(groupId);
}
- (void)setCategory:(MWMMarkGroupID)groupId isVisible:(BOOL)isVisible
{
self.bm.GetEditSession().SetIsVisible(groupId, isVisible);
}
- (void)setUserCategoriesVisible:(BOOL)isVisible {
self.bm.SetAllCategoriesVisibility(isVisible);
}
- (void)setCatalogCategoriesVisible:(BOOL)isVisible {
self.bm.SetAllCategoriesVisibility(isVisible);
}
- (void)deleteCategory:(MWMMarkGroupID)groupId
{
self.bm.GetEditSession().DeleteBmCategory(groupId, false /* move to the Trash */);
[self loopObservers:^(id<MWMBookmarksObserver> observer) {
if ([observer respondsToSelector:@selector(onBookmarksCategoryDeleted:)])
[observer onBookmarksCategoryDeleted:groupId];
}];
}
- (BOOL)checkCategoryName:(NSString *)name
{
return !self.bm.IsUsedCategoryName(name.UTF8String);
}
- (BOOL)hasCategory:(MWMMarkGroupID)groupId
{
return self.bm.HasBmCategory(groupId);
}
- (BOOL)hasBookmark:(MWMMarkID)bookmarkId
{
return self.bm.HasBookmark(bookmarkId);
}
- (BOOL)hasTrack:(MWMTrackID)trackId
{
return self.bm.HasTrack(trackId);
}
- (NSArray<NSNumber *> *)availableSortingTypes:(MWMMarkGroupID)groupId hasMyPosition:(BOOL)hasMyPosition{
auto const availableTypes = self.bm.GetAvailableSortingTypes(groupId, hasMyPosition);
NSMutableArray *result = [NSMutableArray array];
for (auto const &sortingType : availableTypes) {
[result addObject:[NSNumber numberWithInteger:convertSortingType(sortingType)]];
}
return [result copy];
}
- (void)sortBookmarks:(MWMMarkGroupID)groupId
sortingType:(MWMBookmarksSortingType)sortingType
location:(CLLocation *)location
completion:(SortBookmarksCompletionBlock)completion {
self.bm.SetLastSortingType(groupId, convertSortingTypeToCore(sortingType));
m2::PointD myPosition = m2::PointD::Zero();
if (sortingType == MWMBookmarksSortingTypeByDistance) {
if (!location) {
completion(nil);
return;
}
myPosition = mercator::FromLatLon(location.coordinate.latitude, location.coordinate.longitude);
}
auto const sortId = ++self.lastSortId;
__weak auto weakSelf = self;
BookmarkManager::SortParams sortParams;
sortParams.m_groupId = groupId;
sortParams.m_sortingType = convertSortingTypeToCore(sortingType);
sortParams.m_hasMyPosition = location != nil;
sortParams.m_myPosition = myPosition;
sortParams.m_onResults = [weakSelf, sortId, completion] (BookmarkManager::SortedBlocksCollection &&sortedBlocks,
BookmarkManager::SortParams::Status status) {
__strong auto self = weakSelf;
if (!self || sortId != self.lastSortId)
return;
switch (status) {
case BookmarkManager::SortParams::Status::Completed: {
NSMutableArray *result = [NSMutableArray array];
for (auto const &sortedBlock : sortedBlocks) {
NSMutableArray *bookmarks = nil;
if (sortedBlock.m_markIds.size() > 0) {
bookmarks = [NSMutableArray array];
for (auto const &markId : sortedBlock.m_markIds) {
[bookmarks addObject:[[MWMBookmark alloc] initWithMarkId:markId
bookmarkData:self.bm.GetBookmark(markId)]];
}
}
NSMutableArray *tracks = nil;
if (sortedBlock.m_trackIds.size() > 0) {
tracks = [NSMutableArray array];
for (auto const &trackId : sortedBlock.m_trackIds) {
[tracks addObject:[[MWMTrack alloc] initWithTrackId:trackId trackData:self.bm.GetTrack(trackId)]];
}
}
[result addObject:[[MWMBookmarksSection alloc] initWithTitle:@(sortedBlock.m_blockName.c_str())
bookmarks:bookmarks
tracks:tracks]];
}
completion([result copy]);
break;
}
case BookmarkManager::SortParams::Status::Cancelled:
completion(nil);
break;
}
};
self.bm.GetSortedCategory(sortParams);
}
- (BOOL)hasLastSortingType:(MWMMarkGroupID)groupId {
BookmarkManager::SortingType st;
return self.bm.GetLastSortingType(groupId, st);
}
- (MWMBookmarksSortingType)lastSortingType:(MWMMarkGroupID)groupId {
BookmarkManager::SortingType st;
self.bm.GetLastSortingType(groupId, st);
return convertSortingType(st);
}
- (void)resetLastSortingType:(MWMMarkGroupID)groupId {
self.bm.ResetLastSortingType(groupId);
}
#pragma mark - Bookmarks
- (NSArray<MWMCarPlayBookmarkObject *> *)bookmarksForCategory:(MWMMarkGroupID)categoryId
{
NSMutableArray<MWMCarPlayBookmarkObject *> * result = [NSMutableArray array];
auto const & bookmarkIds = self.bm.GetUserMarkIds(categoryId);
for (auto bookmarkId : bookmarkIds)
{
MWMCarPlayBookmarkObject *bookmark = [[MWMCarPlayBookmarkObject alloc] initWithBookmarkId:bookmarkId];
[result addObject:bookmark];
}
return [result copy];
}
- (MWMMarkIDCollection)bookmarkIdsForCategory:(MWMMarkGroupID)categoryId {
auto const &bookmarkIds = self.bm.GetUserMarkIds(categoryId);
NSMutableArray<NSNumber *> *collection = [[NSMutableArray alloc] initWithCapacity:bookmarkIds.size()];
for (auto bookmarkId : bookmarkIds)
[collection addObject:@(bookmarkId)];
return [collection copy];
}
- (void)deleteBookmark:(MWMMarkID)bookmarkId
{
self.bm.GetEditSession().DeleteBookmark(bookmarkId);
[self loopObservers:^(id<MWMBookmarksObserver> observer) {
if ([observer respondsToSelector:@selector(onBookmarkDeleted:)])
[observer onBookmarkDeleted:bookmarkId];
}];
}
- (void)deleteTrack:(MWMTrackID)trackId {
self.bm.GetEditSession().DeleteTrack(trackId);
}
- (MWMBookmark *)bookmarkWithId:(MWMMarkID)bookmarkId {
return [[MWMBookmark alloc] initWithMarkId:bookmarkId bookmarkData:self.bm.GetBookmark(bookmarkId)];
}
- (MWMTrack *)trackWithId:(MWMTrackID)trackId {
return [[MWMTrack alloc] initWithTrackId:trackId trackData:self.bm.GetTrack(trackId)];
}
- (MWMBookmarkGroup *)categoryForBookmarkId:(MWMMarkID)bookmarkId {
auto const groupId = self.bm.GetBookmark(bookmarkId)->GetGroupId();
return [self categoryWithId:groupId];
}
- (MWMBookmarkGroup *)categoryForTrackId:(MWMTrackID)trackId {
auto const groupId = self.bm.GetTrack(trackId)->GetGroupId();
return [self categoryWithId:groupId];
}
- (NSString *)descriptionForBookmarkId:(MWMMarkID)bookmarkId {
auto const description = self.bm.GetBookmark(bookmarkId)->GetDescription();
return [NSString stringWithUTF8String:description.c_str()];
}
- (NSArray<MWMBookmark *> *)bookmarksForGroup:(MWMMarkGroupID)groupId {
auto const &bookmarkIds = self.bm.GetUserMarkIds(groupId);
NSMutableArray *result = [NSMutableArray array];
for (auto bookmarkId : bookmarkIds) {
[result addObject:[[MWMBookmark alloc] initWithMarkId:bookmarkId bookmarkData:self.bm.GetBookmark(bookmarkId)]];
}
return [result copy];
}
- (void)searchBookmarksGroup:(MWMMarkGroupID)groupId
text:(NSString *)text
completion:(SearchBookmarksCompletionBlock)completion
{
auto const searchId = ++self.lastSearchId;
__weak auto weakSelf = self;
using search::BookmarksSearchParams;
BookmarksSearchParams params{
text.UTF8String,
groupId,
// m_onResults
[weakSelf, searchId, completion](BookmarksSearchParams::Results results, BookmarksSearchParams::Status status)
{
__strong auto self = weakSelf;
if (!self || searchId != self.lastSearchId)
return;
self.bm.FilterInvalidBookmarks(results);
NSMutableArray *result = [NSMutableArray array];
for (auto bookmarkId : results)
[result addObject:[[MWMBookmark alloc] initWithMarkId:bookmarkId bookmarkData:self.bm.GetBookmark(bookmarkId)]];
completion(result);
}
};
GetFramework().GetSearchAPI().SearchInBookmarks(std::move(params));
}
#pragma mark - Tracks
- (MWMTrackIDCollection)trackIdsForCategory:(MWMMarkGroupID)categoryId {
auto const & trackIds = self.bm.GetTrackIds(categoryId);
NSMutableArray<NSNumber *> * collection = [[NSMutableArray alloc] initWithCapacity:trackIds.size()];
for (auto trackId : trackIds)
[collection addObject:@(trackId)];
return collection;
}
- (NSArray<MWMTrack *> *)tracksForGroup:(MWMMarkGroupID)groupId {
auto const & trackIds = self.bm.GetTrackIds(groupId);
NSMutableArray * result = [[NSMutableArray alloc] initWithCapacity:trackIds.size()];
for (auto trackId : trackIds)
[result addObject:[[MWMTrack alloc] initWithTrackId:trackId trackData:self.bm.GetTrack(trackId)]];
return result;
}
- (NSArray<MWMBookmarkGroup *> *)collectionsForGroup:(MWMMarkGroupID)groupId {
auto const & collectionIds = self.bm.GetChildrenCollections(groupId);
NSMutableArray * result = [[NSMutableArray alloc] initWithCapacity:collectionIds.size()];
for (auto collectionId : collectionIds)
[result addObject:[[MWMBookmarkGroup alloc] initWithCategoryId:collectionId bookmarksManager:self]];
return result;
}
- (NSArray<MWMBookmarkGroup *> *)categoriesForGroup:(MWMMarkGroupID)groupId {
auto const & categoryIds = self.bm.GetChildrenCategories(groupId);
NSMutableArray * result = [[NSMutableArray alloc] initWithCapacity:categoryIds.size()];
for (auto categoryId : categoryIds)
[result addObject:[[MWMBookmarkGroup alloc] initWithCategoryId:categoryId bookmarksManager:self]];
return result;
}
#pragma mark - Category sharing
- (void)shareCategory:(MWMMarkGroupID)groupId fileType:(MWMKmlFileType)fileType completion:(SharingResultCompletionHandler)completion {
self.bm.PrepareFileForSharing({groupId}, [self, completion](auto sharingResult) {
[self handleSharingResult:sharingResult completion:completion];
}, convertFileTypeToCore(fileType));
}
- (void)shareAllCategoriesWithCompletion:(SharingResultCompletionHandler)completion {
self.bm.PrepareAllFilesForSharing([self, completion](auto sharingResult) {
[self handleSharingResult:sharingResult completion:completion];
});
}
- (void)shareTrack:(MWMTrackID)trackId fileType:(MWMKmlFileType)fileType completion:(SharingResultCompletionHandler)completion {
self.bm.PrepareTrackFileForSharing(trackId, [self, completion](auto sharingResult) {
[self handleSharingResult:sharingResult completion:completion];
}, convertFileTypeToCore(fileType));
}
- (void)handleSharingResult:(BookmarkManager::SharingResult)sharingResult completion:(SharingResultCompletionHandler)completion {
NSURL *urlToALocalFile = nil;
MWMBookmarksShareStatus status;
switch (sharingResult.m_code) {
case BookmarkManager::SharingResult::Code::Success:
urlToALocalFile = [NSURL fileURLWithPath:@(sharingResult.m_sharingPath.c_str()) isDirectory:NO];
ASSERT(urlToALocalFile, ("Invalid share category URL"));
self.shareCategoryURL = urlToALocalFile;
status = MWMBookmarksShareStatusSuccess;
break;
case BookmarkManager::SharingResult::Code::EmptyCategory:
status = MWMBookmarksShareStatusEmptyCategory;
break;
case BookmarkManager::SharingResult::Code::ArchiveError:
status = MWMBookmarksShareStatusArchiveError;
break;
case BookmarkManager::SharingResult::Code::FileError:
status = MWMBookmarksShareStatusFileError;
break;
}
completion(status, urlToALocalFile);
}
- (void)finishSharing {
if (!self.shareCategoryURL)
return;
base::DeleteFileX(self.shareCategoryURL.path.UTF8String);
self.shareCategoryURL = nil;
}
#pragma mark - Notifications
- (void)setNotificationsEnabled:(BOOL)enabled {
self.bm.SetNotificationsEnabled(enabled);
}
- (BOOL)areNotificationsEnabled {
return self.bm.AreNotificationsEnabled();
}
#pragma mark - Catalog
- (NSArray<MWMBookmarkGroup *> *)sortedUserCategories {
auto const & list = self.bm.GetSortedBmGroupIdList();
NSMutableArray<MWMBookmarkGroup *> * result = [[NSMutableArray alloc] initWithCapacity:list.size()];
for (auto const & groupId : list)
[result addObject:[self categoryWithId:groupId]];
return result;
}
- (MWMBookmarkGroup *)categoryWithId:(MWMMarkGroupID)groupId {
return [[MWMBookmarkGroup alloc] initWithCategoryId:groupId bookmarksManager:self];
}
- (size_t)userCategoriesCount {
return self.bm.GetBmGroupsCount();
}
- (void)updateBookmark:(MWMMarkID)bookmarkId
setGroupId:(MWMMarkGroupID)groupId
title:(NSString *)title
color:(MWMBookmarkColor)color
description:(NSString *)description {
ASSERT_NOT_EQUAL(groupId, kml::kInvalidMarkGroupId, ());
auto const currentGroupId = self.bm.GetBookmark(bookmarkId)->GetGroupId();
auto editSession = self.bm.GetEditSession();
if (currentGroupId != groupId)
editSession.MoveBookmark(bookmarkId, currentGroupId, groupId);
auto bookmark = editSession.GetBookmarkForEdit(bookmarkId);
ASSERT(bookmark, ("Invalid bookmark id:", bookmarkId));
auto kmlColor = kmlColorFromBookmarkColor(color);
if (kmlColor != bookmark->GetColor())
self.bm.SetLastEditedBmColor(kmlColor);
bookmark->SetColor(kmlColor);
bookmark->SetDescription(description.UTF8String);
if (title.UTF8String != bookmark->GetPreferredName())
bookmark->SetCustomName(title.UTF8String);
}
- (void)updateBookmark:(MWMMarkID)bookmarkId setColor:(MWMBookmarkColor)color {
auto editSession = self.bm.GetEditSession();
auto bookmark = editSession.GetBookmarkForEdit(bookmarkId);
ASSERT(bookmark, ("Invalid bookmark id:", bookmarkId));
auto kmlColor = kmlColorFromBookmarkColor(color);
if (kmlColor != bookmark->GetColor())
self.bm.SetLastEditedBmColor(kmlColor);
bookmark->SetColor(kmlColor);
}
- (void)moveBookmark:(MWMMarkID)bookmarkId toGroupId:(MWMMarkGroupID)groupId {
ASSERT_NOT_EQUAL(groupId, kml::kInvalidMarkGroupId, ());
auto const currentGroupId = self.bm.GetBookmark(bookmarkId)->GetGroupId();
if (currentGroupId != groupId) {
auto editSession = self.bm.GetEditSession();
editSession.MoveBookmark(bookmarkId, currentGroupId, groupId);
}
}
- (void)updateTrack:(MWMTrackID)trackId
setGroupId:(MWMMarkGroupID)groupId
color:(UIColor *)color
title:(NSString *)title {
ASSERT_NOT_EQUAL(groupId, kml::kInvalidMarkGroupId, ());
auto const currentGroupId = self.bm.GetTrack(trackId)->GetGroupId();
auto editSession = self.bm.GetEditSession();
if (currentGroupId != groupId)
editSession.MoveTrack(trackId, currentGroupId, groupId);
auto track = editSession.GetTrackForEdit(trackId);
ASSERT(track, ("Invalid track id:", trackId));
auto const currentColor = track->GetColor(0);
auto const newColor = [MWMBookmarksManager getColorFromUIColor:color];
if (newColor != currentColor)
track->SetColor(newColor);
track->SetName(title.UTF8String);
}
- (void)updateTrack:(MWMTrackID)trackId setColor:(UIColor *)color {
auto editSession = self.bm.GetEditSession();
auto track = editSession.GetTrackForEdit(trackId);
ASSERT(track, ("Invalid track id:", trackId));
auto const currentColor = track->GetColor(0);
auto const newColor = [MWMBookmarksManager getColorFromUIColor:color];
if (newColor != currentColor)
track->SetColor(newColor);
}
- (void)moveTrack:(MWMTrackID)trackId toGroupId:(MWMMarkGroupID)groupId {
ASSERT_NOT_EQUAL(groupId, kml::kInvalidMarkGroupId, ());
auto const currentGroupId = self.bm.GetTrack(trackId)->GetGroupId();
if (currentGroupId != groupId) {
auto editSession = self.bm.GetEditSession();
editSession.MoveTrack(trackId, currentGroupId, groupId);
}
}
- (BOOL)hasRecentlyDeletedBookmark {
return self.bm.HasRecentlyDeletedBookmark();
}
- (void)setCategory:(MWMMarkGroupID)groupId authorType:(MWMBookmarkGroupAuthorType)author
{
switch (author)
{
case MWMBookmarkGroupAuthorTypeLocal:
self.bm.GetEditSession().SetCategoryCustomProperty(groupId, @"author_type".UTF8String, @"local".UTF8String);
break;
case MWMBookmarkGroupAuthorTypeTraveler:
self.bm.GetEditSession().SetCategoryCustomProperty(groupId, @"author_type".UTF8String, @"tourist".UTF8String);
}
}
// MARK: - RecentlyDeletedCategoriesManager
- (uint64_t)recentlyDeletedCategoriesCount {
return self.bm.GetRecentlyDeletedCategoriesCount();
}
- (NSArray<RecentlyDeletedCategory *> *)getRecentlyDeletedCategories {
auto const categoriesCollection = self.bm.GetRecentlyDeletedCategories();
NSMutableArray<RecentlyDeletedCategory *> * recentlyDeletedCategories = [[NSMutableArray alloc] initWithCapacity:categoriesCollection->size()];
for (auto const & [filePath, categoryPtr] : * categoriesCollection) {
ASSERT(categoryPtr, ("Recently deleted category shouldn't be nil."));
RecentlyDeletedCategory * category = [[RecentlyDeletedCategory alloc] initWithCategoryData:categoryPtr->m_categoryData filePath:filePath];
[recentlyDeletedCategories addObject:category];
}
return recentlyDeletedCategories;
}
- (void)deleteRecentlyDeletedCategoryAtURLs:(NSArray<NSURL *> *)urls {
std::vector<std::string> filePaths;
for (NSURL * url in urls)
filePaths.push_back(url.filePathURL.path.UTF8String);
self.bm.DeleteRecentlyDeletedCategoriesAtPaths(filePaths);
}
- (void)recoverRecentlyDeletedCategoriesAtURLs:(NSArray<NSURL *> *)urls {
std::vector<std::string> filePaths;
for (NSURL * url in urls)
filePaths.push_back(url.filePathURL.path.UTF8String);
self.bm.RecoverRecentlyDeletedCategoriesAtPaths(filePaths);
}
#pragma mark - Helpers
- (void)loopObservers:(void (^)(id<MWMBookmarksObserver> observer))block
{
for (id<MWMBookmarksObserver> observer in [self.observers copy])
{
if (observer)
block(observer);
}
}
- (void)setElevationActivePoint:(CLLocationCoordinate2D)point distance:(double)distance trackId:(uint64_t)trackId {
self.bm.SetElevationActivePoint(trackId, mercator::FromLatLon(point.latitude, point.longitude), distance);
}
- (void)setElevationActivePointChanged:(uint64_t)trackId callback:(ElevationPointChangedBlock)callback {
__weak __typeof(self) ws = self;
self.bm.SetElevationActivePointChangedCallback([callback, trackId, ws] () {
callback(ws.bm.GetElevationActivePoint(trackId));
});
}
- (void)resetElevationActivePointChanged {
self.bm.SetElevationActivePointChangedCallback(nullptr);
}
- (void)setElevationMyPositionChanged:(uint64_t)trackId callback:(ElevationPointChangedBlock)callback {
__weak __typeof(self) ws = self;
self.bm.SetElevationMyPositionChangedCallback([callback, trackId, ws] () {
callback(ws.bm.GetElevationMyPosition(trackId));
});
}
- (void)resetElevationMyPositionChanged {
self.bm.SetElevationMyPositionChangedCallback(nullptr);
}
+ (dp::Color)getColorFromUIColor:(UIColor *)color {
CGFloat fRed, fGreen, fBlue, fAlpha;
[color getRed:&fRed green:&fGreen blue:&fBlue alpha:&fAlpha];
const uint8_t red = [self convertColorComponentToHex:fRed];
const uint8_t green = [self convertColorComponentToHex:fGreen];
const uint8_t blue = [self convertColorComponentToHex:fBlue];
const uint8_t alpha = [self convertColorComponentToHex:fAlpha];
return dp::Color(red, green, blue, alpha);
}
+ (uint8_t)convertColorComponentToHex:(CGFloat)color {
ASSERT_LESS_OR_EQUAL(color, 1.f, ("Extended sRGB color space is not supported"));
ASSERT_GREATER_OR_EQUAL(color, 0.f, ("Extended sRGB color space is not supported"));
static constexpr uint8_t kMaxChannelValue = 255;
return color * kMaxChannelValue;
}
@end

View file

@ -0,0 +1,22 @@
#import <Foundation/Foundation.h>
#import "MWMTypes.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(BookmarksObserver)
@protocol MWMBookmarksObserver<NSObject>
@optional
- (void)onBookmarksLoadFinished;
- (void)onBookmarksFileLoadSuccess;
- (void)onBookmarksFileLoadError;
- (void)onBookmarksCategoryDeleted:(MWMMarkGroupID)groupId;
- (void)onRecentlyDeletedBookmarksCategoriesChanged;
- (void)onBookmarkDeleted:(MWMMarkID)bookmarkId;
@end
@protocol BookmarksObservable<NSObject>
- (void)addObserver:(id<MWMBookmarksObserver>)observer;
- (void)removeObserver:(id<MWMBookmarksObserver>)observer;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,21 @@
#import <Foundation/Foundation.h>
@class MWMBookmark;
@class MWMTrack;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(BookmarksSection)
@interface MWMBookmarksSection : NSObject
@property(nonatomic, readonly) NSString *sectionName;
@property(nonatomic, readonly, nullable) NSArray<MWMBookmark *> *bookmarks;
@property(nonatomic, readonly, nullable) NSArray<MWMTrack *> *tracks;
- (instancetype)initWithTitle:(NSString *)title
bookmarks:(nullable NSArray<MWMBookmark *> *)bookmarks
tracks:(nullable NSArray<MWMTrack *> *)tracks;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,19 @@
#import "MWMBookmarksSection.h"
@implementation MWMBookmarksSection
- (instancetype)initWithTitle:(NSString *)title
bookmarks:(NSArray<MWMBookmark *> *)bookmarks
tracks:(NSArray<MWMTrack *> *)tracks {
self = [super init];
if (self) {
_sectionName = title;
_bookmarks = bookmarks;
_tracks = tracks;
}
return self;
}
@end

View file

@ -0,0 +1,18 @@
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import "MWMTypes.h"
NS_ASSUME_NONNULL_BEGIN
@interface MWMCarPlayBookmarkObject : NSObject
@property(assign, nonatomic, readonly) MWMMarkID bookmarkId;
@property(strong, nonatomic, readonly) NSString *prefferedName;
@property(strong, nonatomic, readonly) NSString *address;
@property(assign, nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property(assign, nonatomic, readonly) CGPoint mercatorPoint;
- (instancetype)initWithBookmarkId:(MWMMarkID)bookmarkId;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,31 @@
#import "MWMCarPlayBookmarkObject.h"
#include "Framework.h"
#include "geometry/mercator.hpp"
@interface MWMCarPlayBookmarkObject()
@property(assign, nonatomic, readwrite) MWMMarkID bookmarkId;
@property(strong, nonatomic, readwrite) NSString *prefferedName;
@property(strong, nonatomic, readwrite) NSString *address;
@property(assign, nonatomic, readwrite) CLLocationCoordinate2D coordinate;
@property(assign, nonatomic, readwrite) CGPoint mercatorPoint;
@end
@implementation MWMCarPlayBookmarkObject
- (instancetype)initWithBookmarkId:(MWMMarkID)bookmarkId {
self = [super init];
if (self) {
self.bookmarkId = bookmarkId;
auto const & bm = GetFramework().GetBookmarkManager();
Bookmark const * bookmark = bm.GetBookmark(bookmarkId);
self.prefferedName = @(bookmark->GetPreferredName().c_str());
auto const pivot = bookmark->GetPivot();
self.mercatorPoint = CGPointMake(pivot.x, pivot.y);
auto const & address = GetFramework().GetAddressAtPoint(pivot);
self.address = @(address.FormatAddress().c_str());
auto const location = mercator::ToLatLon(pivot);
self.coordinate = CLLocationCoordinate2DMake(location.m_lat, location.m_lon);
}
return self;
}
@end

View file

@ -0,0 +1,9 @@
#import "MWMTrack.h"
#include <CoreApi/Framework.h>
@interface MWMTrack (Core)
- (instancetype)initWithTrackId:(MWMMarkID)markId trackData:(Track const *)track;
@end

View file

@ -0,0 +1,17 @@
#import <UIKit/UIKit.h>
#import "MWMTypes.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(Track)
@interface MWMTrack : NSObject
@property(nonatomic, readonly) MWMTrackID trackId;
@property(nonatomic, readonly) NSString *trackName;
@property(nonatomic, readonly) NSInteger trackLengthMeters;
@property(nonatomic, readonly) UIColor *trackColor;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,21 @@
#import "MWMTrack+Core.h"
@implementation MWMTrack
@end
@implementation MWMTrack (Core)
- (instancetype)initWithTrackId:(MWMTrackID)trackId trackData:(Track const *)track {
self = [super init];
if (self) {
_trackId = trackId;
_trackName = @(track->GetName().c_str());
_trackLengthMeters = track->GetLengthMeters();
auto const color = track->GetColor(0);
_trackColor = [UIColor colorWithRed:color.GetRedF() green:color.GetGreenF() blue:color.GetBlueF() alpha:1.f];
}
return self;
}
@end

View file

@ -0,0 +1,9 @@
#import "RecentlyDeletedCategory.h"
#include "kml/types.hpp"
@interface RecentlyDeletedCategory (Core)
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath;
@end

View file

@ -0,0 +1,15 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface RecentlyDeletedCategory : NSObject
@property(nonatomic, readonly) NSString * title;
@property(nonatomic, readonly) NSURL * fileURL;
@property(nonatomic, readonly) NSDate * deletionDate;
- (instancetype)initTitle:(NSString *)title fileURL:(NSURL *)fileURL deletionDate:(NSDate *)deletionDate;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,35 @@
#import "RecentlyDeletedCategory+Core.h"
#include <map/bookmark_helpers.hpp>
#include <platform/platform_ios.h>
@implementation RecentlyDeletedCategory
- (instancetype)initTitle:(NSString *)title fileURL:(NSURL *)fileURL deletionDate:(NSDate *)deletionDate {
self = [super init];
if (self) {
_title = title;
_fileURL = fileURL;
_deletionDate = deletionDate;
}
return self;
}
@end
@implementation RecentlyDeletedCategory (Core)
- (instancetype)initWithCategoryData:(kml::CategoryData)data filePath:(std::string const &)filePath {
self = [super init];
if (self) {
auto const name = GetPreferredBookmarkStr(data.m_name);
_title = [NSString stringWithCString:name.c_str() encoding:NSUTF8StringEncoding];
auto const pathString = [NSString stringWithCString:filePath.c_str() encoding:NSUTF8StringEncoding];
_fileURL = [NSURL fileURLWithPath:pathString];
NSTimeInterval creationTime = Platform::GetFileCreationTime(filePath);
_deletionDate = [NSDate dateWithTimeIntervalSince1970:creationTime];
}
return self;
}
@end

View file

@ -0,0 +1,10 @@
#import "TrackInfo.h"
#include <CoreApi/Framework.h>
#include "map/track_statistics.hpp"
@interface TrackInfo (Core)
- (instancetype)initWithTrackStatistics:(TrackStatistics const &)statistics;
@end

View file

@ -0,0 +1,20 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface TrackInfo : NSObject
@property (nonatomic, readonly) NSString * distance;
@property (nonatomic, readonly) NSString * duration;
@property (nonatomic, readonly) NSString * ascent;
@property (nonatomic, readonly) NSString * descent;
@property (nonatomic, readonly) NSString * maxElevation;
@property (nonatomic, readonly) NSString * minElevation;
@property (nonatomic, readonly) BOOL hasElevationInfo;
+ (TrackInfo *)emptyInfo;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,30 @@
#import "TrackInfo+Core.h"
#import "StringUtils.h"
@implementation TrackInfo
+ (TrackInfo *)emptyInfo {
return [[TrackInfo alloc] initWithTrackStatistics:TrackStatistics()];
}
@end
@implementation TrackInfo (Core)
- (instancetype)initWithTrackStatistics:(TrackStatistics const &)statistics {
if (self = [super init]) {
_distance = ToNSString(statistics.GetFormattedLength());
_duration = ToNSString(statistics.GetFormattedDuration());
_ascent = ToNSString(statistics.GetFormattedAscent());
_descent = ToNSString(statistics.GetFormattedDescent());
_maxElevation = ToNSString(statistics.GetFormattedMaxElevation());
_minElevation = ToNSString(statistics.GetFormattedMinElevation());
_hasElevationInfo = statistics.m_ascent != 0 ||
statistics.m_descent != 0 ||
statistics.m_maxElevation != 0 ||
statistics.m_minElevation != 0;
}
return self;
}
@end

View file

@ -0,0 +1,26 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, MWMOpenGLDriver) {
MWMOpenGLDriverRegular,
MWMOpenGLDriverMetalPre103, // iOS 10..10.3
MWMOpenGLDriverMetal
};
NS_ASSUME_NONNULL_BEGIN
@interface AppInfo : NSObject
+ (instancetype)sharedInfo;
- (instancetype)init __attribute__((unavailable("init is not available")));
@property(nonatomic, readonly) NSString * bundleVersion;
@property(nonatomic, readonly) NSString * buildNumber;
@property(nonatomic, readonly) NSString * languageId;
@property(nonatomic, readonly) NSString * twoLetterLanguageId;
@property(nonatomic, readonly) NSString * deviceModel;
@property(nonatomic, readonly) MWMOpenGLDriver openGLDriver;
@property(nonatomic, readonly) BOOL canMakeCalls;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,107 @@
#import "AppInfo.h"
#import "MWMCommon.h"
#include "platform/platform_ios.h"
#include "platform/preferred_languages.hpp"
#include "platform/settings.hpp"
#include <sys/utsname.h>
#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
@interface AppInfo ()
@property(nonatomic) NSString * bundleVersion;
@property(nonatomic) NSString * buildNumber;
@property(nonatomic) NSString * deviceModel;
@end
@implementation AppInfo
+ (instancetype)sharedInfo
{
static AppInfo * appInfo;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
appInfo = [[self alloc] init];
});
return appInfo;
}
- (instancetype)init
{
self = [super init];
return self;
}
#pragma mark - Properties
- (NSString *)bundleVersion
{
if (!_bundleVersion)
_bundleVersion = NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"];
return _bundleVersion;
}
- (NSString *)buildNumber
{
if (!_buildNumber)
_buildNumber = NSBundle.mainBundle.infoDictionary[@"CFBundleVersion"];
return _buildNumber;
}
- (NSString *)languageId
{
return NSLocale.preferredLanguages.firstObject;
}
- (NSString *)twoLetterLanguageId
{
auto languageId = self.languageId;
return languageId ? @(languages::Normalize(languageId.UTF8String).c_str()) : @"en";
}
- (NSString *)deviceModel
{
if (!_deviceModel)
_deviceModel = @(GetPlatform().DeviceModel().c_str());
return _deviceModel;
}
- (MWMOpenGLDriver)openGLDriver
{
utsname systemInfo;
uname(&systemInfo);
NSString * machine = @(systemInfo.machine);
if (platform::kDeviceModelsBeforeMetalDriver[machine] != nil)
return MWMOpenGLDriverRegular;
if (platform::kDeviceModelsWithiOS10MetalDriver[machine] != nil)
{
if (isIOSVersionLessThan(10))
return MWMOpenGLDriverRegular;
else if (isIOSVersionLessThanString(@"10.3"))
return MWMOpenGLDriverMetalPre103;
}
return MWMOpenGLDriverMetal;
}
- (BOOL)canMakeCalls
{
if (UIDevice.currentDevice.userInterfaceIdiom != UIUserInterfaceIdiomPhone)
return NO;
NSURL * telURL = [NSURL URLWithString:@"tel://"];
if (![UIApplication.sharedApplication canOpenURL:telURL])
return NO;
NSDictionary<NSString *,CTCarrier *> * dict = [[CTTelephonyNetworkInfo alloc] init].serviceSubscriberCellularProviders;
for (id key in dict)
{
NSString * networkCode = [dict objectForKey:key].mobileNetworkCode;
if (networkCode != nil && networkCode.length > 0 && ![networkCode isEqualToString:@"65535"])
return YES;
}
return NO;
}
@end

View file

@ -0,0 +1,61 @@
#import <UIKit/UIKit.h>
#import "MWMTypes.h"
NS_ASSUME_NONNULL_BEGIN
static inline BOOL firstVersionIsLessThanSecond(NSString * first, NSString * second)
{
NSArray<NSString *> * f = [first componentsSeparatedByString:@"."];
NSArray<NSString *> * s = [second componentsSeparatedByString:@"."];
NSUInteger iter = 0;
while (f.count > iter && s.count > iter)
{
NSInteger fiv = f[iter].integerValue;
NSInteger siv = s[iter].integerValue;
if (fiv == siv)
iter++;
else
return fiv < siv;
}
return f.count < s.count;
}
static inline BOOL isIOSVersionLessThanString(NSString * version)
{
return firstVersionIsLessThanSecond(UIDevice.currentDevice.systemVersion, version);
}
static inline BOOL isIOSVersionLessThan(NSUInteger version)
{
return isIOSVersionLessThanString([NSString stringWithFormat:@"%@", @(version)]);
}
static inline BOOL isInterfaceRightToLeft(void) NS_EXTENSION_UNAVAILABLE_IOS("Not available in extensions")
{
return UIApplication.sharedApplication.userInterfaceLayoutDirection ==
UIUserInterfaceLayoutDirectionRightToLeft;
}
static inline NSString * formattedSize(uint64_t size)
{
return [NSByteCountFormatter stringFromByteCount:size
countStyle:NSByteCountFormatterCountStyleFile];
}
// Use only for screen dimensions CGFloat comparison
static inline BOOL equalScreenDimensions(CGFloat left, CGFloat right)
{
return fabs(left - right) < 0.5;
}
static inline void performOnce(MWMVoidBlock block, NSString *key) {
BOOL performed = [[NSUserDefaults standardUserDefaults] boolForKey:key];
if (!performed) {
block();
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
}
}
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,25 @@
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Measure : NSObject
@property(nonatomic, readonly) double value;
@property(nonatomic, readonly) NSString* valueAsString;
@property(nonatomic, readonly) NSString* unit;
- (instancetype) initAsSpeed:(double) mps;
@end
NS_SWIFT_NAME(GeoUtil)
@interface MWMGeoUtil : NSObject
+ (float)angleAtPoint:(CLLocationCoordinate2D)p1 toPoint:(CLLocationCoordinate2D)p2;
+ (NSString *)formattedOsmLinkForCoordinate:(CLLocationCoordinate2D)coordinate zoomLevel:(int)zoomLevel;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,66 @@
#import "MWMGeoUtil.h"
#include "geometry/mercator.hpp"
#include "geometry/angles.hpp"
#include "platform/locale.hpp"
#include "platform/localization.hpp"
#include "platform/settings.hpp"
#include "platform/measurement_utils.hpp"
@implementation Measure
// Alternative native implementation.
// It has the issue: some localized unit are too long even in .short style. E.g. speed for RU.
/*
let imperial = Settings.measurementUnits() == .imperial
var speedMeasurement = Measurement(value: speed, unit: UnitSpeed.metersPerSecond)
speedMeasurement.convert(to: imperial ? UnitSpeed.milesPerHour : UnitSpeed.kilometersPerHour)
let formatter = MeasurementFormatter()
formatter.unitOptions = .providedUnit
formatter.numberFormatter.maximumFractionDigits = 0
formatter.unitStyle = .short
if speedMeasurement.value < 10
{
formatter.numberFormatter.minimumFractionDigits = 1
formatter.numberFormatter.maximumFractionDigits = 1
}
let speedString = formatter.string(from: speedMeasurement)
*/
- (NSString*) valueAsString {
auto const outString = measurement_utils::ToStringPrecision(self.value, self.value >= 10.0 ? 0 : 1);
return [NSString stringWithUTF8String:outString.c_str()];
}
- (instancetype)initAsSpeed:(double) mps {
self = [super init];
if (self) {
auto units = measurement_utils::Units::Metric;
settings::TryGet(settings::kMeasurementUnits, units);
_value = measurement_utils::MpsToUnits(mps, units);
_unit = @(platform::GetLocalizedSpeedUnits(units).c_str());
}
return self;
}
@end
@implementation MWMGeoUtil
+ (float)angleAtPoint:(CLLocationCoordinate2D)p1 toPoint:(CLLocationCoordinate2D)p2 {
auto mp1 = mercator::FromLatLon(p1.latitude, p1.longitude);
auto mp2 = mercator::FromLatLon(p2.latitude, p2.longitude);
return ang::AngleTo(mp1, mp2);
}
+ (NSString *)formattedOsmLinkForCoordinate:(CLLocationCoordinate2D)coordinate zoomLevel:(int)zoomLevel {
auto const link = measurement_utils::FormatOsmLink(coordinate.latitude, coordinate.longitude, zoomLevel);
return [NSString stringWithCString:link.c_str() encoding:NSUTF8StringEncoding];
}
@end

View file

@ -0,0 +1,63 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void (^MWMVoidBlock)(void);
typedef void (^MWMStringBlock)(NSString *);
typedef void (^MWMURLBlock)(NSURL *);
typedef BOOL (^MWMCheckStringBlock)(NSString *);
typedef void (^MWMBoolBlock)(BOOL);
typedef NS_ENUM(NSUInteger, MWMDayTime) { MWMDayTimeDay, MWMDayTimeNight } NS_SWIFT_NAME(DayTime);
typedef NS_ENUM(NSUInteger, MWMUnits) { MWMUnitsMetric, MWMUnitsImperial } NS_SWIFT_NAME(Units);
typedef NS_ENUM(NSUInteger, MWMTheme) {
MWMThemeDay,
MWMThemeNight,
MWMThemeVehicleDay,
MWMThemeVehicleNight,
MWMThemeAuto
};
typedef NS_ENUM(NSUInteger, MWMKmlFileType) {
MWMKmlFileTypeText,
MWMKmlFileTypeBinary,
MWMKmlFileTypeGpx
} NS_SWIFT_NAME(KmlFileType);
typedef uint64_t MWMMarkID;
typedef uint64_t MWMTrackID;
typedef uint64_t MWMMarkGroupID;
typedef NSArray<NSNumber *> *MWMMarkIDCollection;
typedef NSArray<NSNumber *> *MWMTrackIDCollection;
typedef NSArray<NSNumber *> *MWMGroupIDCollection;
typedef NS_CLOSED_ENUM(NSUInteger, MWMBookmarksShareStatus) {
MWMBookmarksShareStatusSuccess,
MWMBookmarksShareStatusEmptyCategory,
MWMBookmarksShareStatusArchiveError,
MWMBookmarksShareStatusFileError
} NS_SWIFT_NAME(BookmarksShareStatus);
typedef NS_ENUM(NSUInteger, MWMBookmarkGroupAccessStatus) {
MWMBookmarkGroupAccessStatusLocal,
MWMBookmarkGroupAccessStatusPublic,
MWMBookmarkGroupAccessStatusPrivate,
MWMBookmarkGroupAccessStatusAuthorOnly,
MWMBookmarkGroupAccessStatusOther
} NS_SWIFT_NAME(BookmarkGroupAccessStatus);
typedef NS_ENUM(NSUInteger, MWMBookmarkGroupAuthorType) {
MWMBookmarkGroupAuthorTypeLocal,
MWMBookmarkGroupAuthorTypeTraveler
} NS_SWIFT_NAME(BookmarkGroupAuthorType);
typedef NS_ENUM(NSInteger, MWMBookmarkGroupType) {
MWMBookmarkGroupTypeRoot,
MWMBookmarkGroupTypeCategory,
MWMBookmarkGroupTypeCollection,
MWMBookmarkGroupTypeDay
} NS_SWIFT_NAME(BookmarkGroupType);
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,9 @@
#pragma once
#import <Foundation/Foundation.h>
#include <string_view>
inline NSString * ToNSString(std::string_view sv)
{
return [[NSString alloc] initWithBytes:sv.data() length:sv.size() encoding:NSUTF8StringEncoding];
}

View file

@ -0,0 +1,47 @@
//! Project version number for CoreApi.
FOUNDATION_EXPORT double CoreApiVersionNumber;
//! Project version string for CoreApi.
FOUNDATION_EXPORT const unsigned char CoreApiVersionString[];
#import <CoreApi/AppInfo.h>
#import <CoreApi/DeepLinkParser.h>
#import <CoreApi/DeepLinkSearchData.h>
#import <CoreApi/DeepLinkInAppFeatureHighlightData.h>
#import <CoreApi/Logger.h>
#import <CoreApi/MWMBookmark.h>
#import <CoreApi/MWMBookmarkGroup.h>
#import <CoreApi/MWMBookmarksSection.h>
#import <CoreApi/MWMBookmarksManager.h>
#import <CoreApi/MWMBookmarksObserver.h>
#import <CoreApi/MWMCarPlayBookmarkObject.h>
#import <CoreApi/MWMCommon.h>
#import <CoreApi/MWMFrameworkHelper.h>
#import <CoreApi/MWMGeoUtil.h>
#import <CoreApi/MWMMapNodeAttributes.h>
#import <CoreApi/MWMMapOverlayManager.h>
#import <CoreApi/MWMMapSearchResult.h>
#import <CoreApi/MWMMapUpdateInfo.h>
#import <CoreApi/MWMNetworkPolicy.h>
#import <CoreApi/MWMSearchFrameworkHelper.h>
#import <CoreApi/MWMStorage.h>
#import <CoreApi/MWMTrack.h>
#import <CoreApi/MWMTypes.h>
#import <CoreApi/RecentlyDeletedCategory.h>
#import "CoreApi/DistanceFormatter.h"
#import "CoreApi/AltitudeFormatter.h"
#import "CoreApi/DurationFormatter.h"
#import "CoreApi/TrackInfo.h"
#pragma mark - Place Page
#import <CoreApi/ElevationProfileData.h>
#import <CoreApi/IOpeningHoursLocalization.h>
#import <CoreApi/OpeningHours.h>
#import <CoreApi/PlacePageBookmarkData.h>
#import <CoreApi/PlacePageTrackData.h>
#import <CoreApi/PlacePageButtonsData.h>
#import <CoreApi/PlacePageData.h>
#import <CoreApi/PlacePageInfoData.h>
#import <CoreApi/PlacePagePreviewData.h>
#import <CoreApi/PlacePagePhone.h>

View file

@ -0,0 +1,21 @@
#import <Foundation/Foundation.h>
#import "DeepLinkParser.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, InAppFeatureHighlightType) {
InAppFeatureHighlightTypeNone,
InAppFeatureHighlightTypeTrackRecorder,
InAppFeatureHighlightTypeICloud,
};
@interface DeepLinkInAppFeatureHighlightData : NSObject
@property(nonatomic, readonly) DeeplinkUrlType urlType;
@property(nonatomic, readonly) InAppFeatureHighlightType feature;
- (instancetype)init:(DeeplinkUrlType)urlType;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,26 @@
#import "DeepLinkInAppFeatureHighlightData.h"
#import <CoreApi/Framework.h>
static inline InAppFeatureHighlightType FeatureTypeFrom(url_scheme::InAppFeatureHighlightRequest::InAppFeatureType type)
{
using namespace url_scheme;
switch (type)
{
case InAppFeatureHighlightRequest::InAppFeatureType::None: return InAppFeatureHighlightTypeNone;
case InAppFeatureHighlightRequest::InAppFeatureType::TrackRecorder: return InAppFeatureHighlightTypeTrackRecorder;
case InAppFeatureHighlightRequest::InAppFeatureType::iCloud: return InAppFeatureHighlightTypeICloud;
}
}
@implementation DeepLinkInAppFeatureHighlightData
- (instancetype)init:(DeeplinkUrlType)urlType {
self = [super init];
if (self) {
_urlType = urlType;
_feature = FeatureTypeFrom(GetFramework().GetInAppFeatureHighlightRequest().m_feature);
}
return self;
}
@end

View file

@ -0,0 +1,23 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, DeeplinkUrlType) {
DeeplinkUrlTypeIncorrect = 0,
DeeplinkUrlTypeMap,
DeeplinkUrlTypeRoute,
DeeplinkUrlTypeSearch,
DeeplinkUrlTypeCrosshair,
DeeplinkUrlTypeOAuth2,
DeeplinkUrlTypeMenu,
DeeplinkUrlTypeSettings
};
@interface DeepLinkParser : NSObject
+ (DeeplinkUrlType)parseAndSetApiURL:(NSURL *)url;
+ (void)executeMapApiRequest;
+ (void)addBookmarksFile:(NSURL*)url;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,39 @@
#import "DeepLinkParser.h"
#include <CoreApi/Framework.h>
#import "DeepLinkSearchData.h"
#import "map/mwm_url.hpp"
static inline DeeplinkUrlType deeplinkUrlType(url_scheme::ParsedMapApi::UrlType type)
{
using namespace url_scheme;
switch (type)
{
case ParsedMapApi::UrlType::Incorrect: return DeeplinkUrlTypeIncorrect;
case ParsedMapApi::UrlType::Map: return DeeplinkUrlTypeMap;
case ParsedMapApi::UrlType::Route: return DeeplinkUrlTypeRoute;
case ParsedMapApi::UrlType::Search: return DeeplinkUrlTypeSearch;
case ParsedMapApi::UrlType::Crosshair: return DeeplinkUrlTypeCrosshair;
case ParsedMapApi::UrlType::OAuth2: return DeeplinkUrlTypeOAuth2;
case ParsedMapApi::UrlType::Menu: return DeeplinkUrlTypeMenu;
case ParsedMapApi::UrlType::Settings: return DeeplinkUrlTypeSettings;
}
}
@implementation DeepLinkParser
+ (DeeplinkUrlType)parseAndSetApiURL:(NSURL *)url {
Framework &f = GetFramework();
return deeplinkUrlType(f.ParseAndSetApiURL(url.absoluteString.UTF8String));
}
+ (void)executeMapApiRequest {
GetFramework().ExecuteMapApiRequest();
}
+ (void)addBookmarksFile:(NSURL *)url {
// iOS doesn't create temporary files on import at least in Safari and Files.
GetFramework().AddBookmarksFile(url.path.UTF8String, false /* isTemporaryFile */);
}
@end

View file

@ -0,0 +1,18 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface DeepLinkSearchData : NSObject
@property(nonatomic, readonly) NSString *query;
@property(nonatomic, readonly) NSString *locale;
@property(nonatomic, readonly) double centerLat;
@property(nonatomic, readonly) double centerLon;
@property(nonatomic, readonly) BOOL isSearchOnMap;
- (instancetype)init;
- (BOOL)hasValidCenterLatLon;
- (void)onViewportChanged:(int)zoomLevel;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,31 @@
#import "DeepLinkSearchData.h"
#import <CoreApi/Framework.h>
#include "drape_frontend/visual_params.hpp"
#include "geometry/mercator.hpp"
#include "geometry/latlon.hpp"
@implementation DeepLinkSearchData
- (instancetype)init {
self = [super init];
if (self) {
auto const &request = GetFramework().GetParsedSearchRequest();
ms::LatLon const center = GetFramework().GetParsedCenterLatLon();
_query = [@((request.m_query + " ").c_str()) stringByRemovingPercentEncoding];
_locale = @(request.m_locale.c_str());
_centerLat = center.m_lat;
_centerLon = center.m_lon;
_isSearchOnMap = request.m_isSearchOnMap;
}
return self;
}
- (BOOL)hasValidCenterLatLon {
return _centerLat != ms::LatLon::kInvalid && _centerLon != ms::LatLon::kInvalid;
}
- (void)onViewportChanged:(int)zoomLevel {
auto const center = mercator::FromLatLon(_centerLat, _centerLon);
auto const rect = df::GetRectForDrawScale(zoomLevel, center);
GetFramework().GetSearchAPI().OnViewportChanged(rect);
}
@end

View file

@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface AltitudeFormatter : NSObject
+ (NSString *)altitudeStringFromMeters:(double)meters;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,12 @@
#import "AltitudeFormatter.h"
#include "platform/distance.hpp"
@implementation AltitudeFormatter
+ (NSString *)altitudeStringFromMeters:(double)meters {
auto const altitude = platform::Distance::FormatAltitude(meters);
return [NSString stringWithUTF8String:altitude.c_str()];
}
@end

View file

@ -0,0 +1,16 @@
import Foundation
@objcMembers
public final class DateTimeFormatter: NSObject {
private static let dateFormatter = DateFormatter()
public static func dateString(from date: Date, dateStyle: DateFormatter.Style, timeStyle: DateFormatter.Style) -> String {
DateFormatter.localizedString(from: date, dateStyle: dateStyle, timeStyle: timeStyle)
}
public static func dateString(from date: Date, locale: Locale = .current, format: String) -> String {
dateFormatter.locale = locale
dateFormatter.dateFormat = format
return dateFormatter.string(from: date)
}
}

View file

@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface DistanceFormatter : NSObject
+ (NSString *)distanceStringFromMeters:(double)meters;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,12 @@
#import "DistanceFormatter.h"
#include "platform/distance.hpp"
@implementation DistanceFormatter
+ (NSString *)distanceStringFromMeters:(double)meters {
auto const coreDistance = platform::Distance::CreateFormatted(meters);
return [NSString stringWithUTF8String:coreDistance.ToString().c_str()];
}
@end

View file

@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface DurationFormatter: NSObject
+ (NSString *)durationStringFromTimeInterval:(NSTimeInterval)timeInterval NS_SWIFT_NAME(durationString(from:));
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,12 @@
#import "DurationFormatter.h"
#include "platform/duration.hpp"
@implementation DurationFormatter
+ (NSString *)durationStringFromTimeInterval:(NSTimeInterval)timeInterval {
auto const duration = platform::Duration(static_cast<int>(timeInterval));
return [NSString stringWithCString:duration.GetPlatformLocalizedString().c_str() encoding:NSUTF8StringEncoding];
}
@end

View file

@ -0,0 +1,21 @@
#include "Framework.h"
#include "base/assert.hpp"
static Framework * g_framework = 0;
bool g_wasDeleted = false;
Framework & GetFramework()
{
CHECK(!g_wasDeleted, ());
if (g_framework == 0)
g_framework = new Framework();
return *g_framework;
}
void DeleteFramework()
{
g_wasDeleted = true;
delete g_framework;
g_framework = nullptr;
}

View file

@ -0,0 +1,9 @@
// Wraps framework access
#pragma once
#include "map/framework.hpp"
/// Creates framework at first access
Framework & GetFramework();
/// Releases framework resources
void DeleteFramework();

View file

@ -0,0 +1,65 @@
#import <CoreLocation/CoreLocation.h>
#import <UIKit/UIKit.h>
#import "MWMTypes.h"
@class MWMMapSearchResult;
@class TrackInfo;
@class ElevationProfileData;
typedef NS_ENUM(NSUInteger, MWMZoomMode) { MWMZoomModeIn = 0, MWMZoomModeOut };
NS_ASSUME_NONNULL_BEGIN
typedef void (^SearchInDownloaderCompletions)(NSArray<MWMMapSearchResult *> *results, BOOL finished);
typedef void (^TrackRecordingUpdatedHandler)(TrackInfo * _Nonnull trackInfo);
@protocol TrackRecorder
+ (void)startTrackRecording;
+ (void)setTrackRecordingUpdateHandler:(TrackRecordingUpdatedHandler _Nullable)trackRecordingDidUpdate;
+ (void)stopTrackRecording;
+ (void)saveTrackRecordingWithName:(nonnull NSString *)name;
+ (BOOL)isTrackRecordingEnabled;
+ (BOOL)isTrackRecordingEmpty;
/// Returns current track recording elevation info.
/// If the track recording is not in progress, returns empty ElevationProfileData.
+ (ElevationProfileData * _Nonnull)trackRecordingElevationInfo;
@end
NS_SWIFT_NAME(FrameworkHelper)
@interface MWMFrameworkHelper : NSObject<TrackRecorder>
+ (void)processFirstLaunch:(BOOL)hasLocation;
+ (void)setVisibleViewport:(CGRect)rect scaleFactor:(CGFloat)scale;
+ (void)setTheme:(MWMTheme)theme;
+ (MWMDayTime)daytimeAtLocation:(nullable CLLocation *)location;
+ (void)createFramework;
+ (MWMMarkID)invalidBookmarkId;
+ (MWMMarkGroupID)invalidCategoryId;
+ (void)zoomMap:(MWMZoomMode)mode;
+ (void)moveMap:(UIOffset)offset;
+ (void)scrollMapToDistanceX:(double)x andY:(double)y;
+ (void)deactivateMapSelection;
+ (void)switchMyPositionMode;
+ (void)stopLocationFollow;
+ (NSArray<NSString *> *)obtainLastSearchQueries;
+ (void)rotateMap:(double)azimuth animated:(BOOL)isAnimated;
+ (void)updatePositionArrowOffset:(BOOL)useDefault offset:(int)offsetY;
+ (int64_t)dataVersion;
+ (void)searchInDownloader:(NSString *)query
inputLocale:(NSString *)locale
completion:(SearchInDownloaderCompletions)completion;
+ (BOOL)canEditMapAtViewportCenter;
+ (void)showOnMap:(MWMMarkGroupID)categoryId;
+ (void)showBookmark:(MWMMarkID)bookmarkId;
+ (void)showTrack:(MWMTrackID)trackId;
+ (void)saveRouteAsTrack;
+ (void)updatePlacePageData;
+ (void)updateAfterDeleteBookmark;
+ (int)currentZoomLevel;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,244 @@
#import "MWMFrameworkHelper.h"
#import "MWMMapSearchResult+Core.h"
#import "TrackInfo+Core.h"
#import "ElevationProfileData+Core.h"
#include "Framework.h"
#include "base/sunrise_sunset.hpp"
#include "platform/local_country_file_utils.hpp"
#include "platform/network_policy_ios.h"
@implementation MWMFrameworkHelper
+ (void)processFirstLaunch:(BOOL)hasLocation {
auto &f = GetFramework();
if (!hasLocation)
f.SwitchMyPositionNextMode();
else
f.RunFirstLaunchAnimation();
}
+ (void)setVisibleViewport:(CGRect)rect scaleFactor:(CGFloat)scale {
CGFloat const x0 = rect.origin.x * scale;
CGFloat const y0 = rect.origin.y * scale;
CGFloat const x1 = x0 + rect.size.width * scale;
CGFloat const y1 = y0 + rect.size.height * scale;
GetFramework().SetVisibleViewport(m2::RectD(x0, y0, x1, y1));
}
+ (void)setTheme:(MWMTheme)theme {
auto &f = GetFramework();
auto const style = f.GetMapStyle();
auto const isOutdoor = ^BOOL(MapStyle style) {
switch (style) {
case MapStyleOutdoorsLight:
case MapStyleOutdoorsDark:
return YES;
default:
return NO;
}
}(style);
auto const newStyle = ^MapStyle(MWMTheme theme) {
switch (theme) {
case MWMThemeDay:
return isOutdoor ? MapStyleOutdoorsLight : MapStyleDefaultLight;
case MWMThemeVehicleDay:
return MapStyleVehicleLight;
case MWMThemeNight:
return isOutdoor ? MapStyleOutdoorsDark : MapStyleDefaultDark;
case MWMThemeVehicleNight:
return MapStyleVehicleDark;
case MWMThemeAuto:
NSAssert(NO, @"Invalid theme");
return MapStyleDefaultLight;
}
}(theme);
if (style != newStyle)
f.SetMapStyle(newStyle);
}
+ (MWMDayTime)daytimeAtLocation:(CLLocation *)location {
if (!location)
return MWMDayTimeDay;
DayTimeType dayTime =
GetDayTime(NSDate.date.timeIntervalSince1970, location.coordinate.latitude, location.coordinate.longitude);
switch (dayTime) {
case DayTimeType::Day:
case DayTimeType::PolarDay:
return MWMDayTimeDay;
case DayTimeType::Night:
case DayTimeType::PolarNight:
return MWMDayTimeNight;
}
}
+ (void)createFramework {
UNUSED_VALUE(GetFramework());
}
+ (MWMMarkID)invalidBookmarkId {
return kml::kInvalidMarkId;
}
+ (MWMMarkGroupID)invalidCategoryId {
return kml::kInvalidMarkGroupId;
}
+ (NSArray<NSString *> *)obtainLastSearchQueries {
NSMutableArray *result = [NSMutableArray array];
auto const &queries = GetFramework().GetSearchAPI().GetLastSearchQueries();
for (auto const &item : queries) {
[result addObject:@(item.second.c_str())];
}
return [result copy];
}
#pragma mark - Map Interaction
+ (void)zoomMap:(MWMZoomMode)mode {
switch (mode) {
case MWMZoomModeIn:
GetFramework().Scale(Framework::SCALE_MAG, true);
break;
case MWMZoomModeOut:
GetFramework().Scale(Framework::SCALE_MIN, true);
break;
}
}
+ (void)moveMap:(UIOffset)offset {
GetFramework().Move(offset.horizontal, offset.vertical, true);
}
+ (void)scrollMapToDistanceX:(double)x andY:(double)y
{
GetFramework().Scroll(x, y);
}
+ (void)deactivateMapSelection {
GetFramework().DeactivateMapSelection();
}
+ (void)switchMyPositionMode {
GetFramework().SwitchMyPositionNextMode();
}
+ (void)stopLocationFollow {
GetFramework().StopLocationFollow();
}
+ (void)rotateMap:(double)azimuth animated:(BOOL)isAnimated {
GetFramework().Rotate(azimuth, isAnimated);
}
+ (void)updatePositionArrowOffset:(BOOL)useDefault offset:(int)offsetY {
GetFramework().UpdateMyPositionRoutingOffset(useDefault, offsetY);
}
+ (int64_t)dataVersion {
return GetFramework().GetCurrentDataVersion();
}
+ (void)searchInDownloader:(NSString *)query
inputLocale:(NSString *)locale
completion:(SearchInDownloaderCompletions)completion {
storage::DownloaderSearchParams params{
query.UTF8String,
locale.precomposedStringWithCompatibilityMapping.UTF8String,
// m_onResults
[completion](storage::DownloaderSearchResults const & results)
{
NSMutableArray *resultsArray = [NSMutableArray arrayWithCapacity:results.m_results.size()];
for (auto const & res : results.m_results)
{
MWMMapSearchResult *result = [[MWMMapSearchResult alloc] initWithSearchResult:res];
[resultsArray addObject:result];
}
completion(resultsArray, results.m_endMarker);
}
};
GetFramework().GetSearchAPI().SearchInDownloader(std::move(params));
}
+ (BOOL)canEditMapAtViewportCenter {
auto const &f = GetFramework();
return f.CanEditMapForPosition(f.GetViewportCenter());
}
+ (void)showOnMap:(MWMMarkGroupID)categoryId {
GetFramework().ShowBookmarkCategory(categoryId);
}
+ (void)showBookmark:(MWMMarkID)bookmarkId {
GetFramework().ShowBookmark(bookmarkId);
}
+ (void)showTrack:(MWMTrackID)trackId {
GetFramework().ShowTrack(trackId);
}
+ (void)saveRouteAsTrack {
GetFramework().SaveRoute();
}
+ (void)updatePlacePageData {
GetFramework().UpdatePlacePageInfoForCurrentSelection();
}
+ (void)updateAfterDeleteBookmark {
auto & frm = GetFramework();
auto buildInfo = frm.GetCurrentPlacePageInfo().GetBuildInfo();
buildInfo.m_match = place_page::BuildInfo::Match::FeatureOnly;
buildInfo.m_userMarkId = kml::kInvalidMarkId;
buildInfo.m_source = place_page::BuildInfo::Source::Other;
frm.UpdatePlacePageInfoForCurrentSelection(buildInfo);
}
+ (int)currentZoomLevel {
return GetFramework().GetDrawScale();
}
// MARK: - TrackRecorder
+ (void)startTrackRecording {
GetFramework().StartTrackRecording();
}
+ (void)setTrackRecordingUpdateHandler:(TrackRecordingUpdatedHandler _Nullable)trackRecordingDidUpdate {
if (!trackRecordingDidUpdate)
{
GetFramework().SetTrackRecordingUpdateHandler(nullptr);
return;
}
GetFramework().SetTrackRecordingUpdateHandler([trackRecordingDidUpdate](TrackStatistics const & statistics) {
TrackInfo * info = [[TrackInfo alloc] initWithTrackStatistics:statistics];
trackRecordingDidUpdate(info);
});
}
+ (void)stopTrackRecording {
GetFramework().StopTrackRecording();
}
+ (void)saveTrackRecordingWithName:(nonnull NSString *)name {
GetFramework().SaveTrackRecordingWithName(name.UTF8String);
}
+ (BOOL)isTrackRecordingEnabled {
return GetFramework().IsTrackRecordingEnabled();
}
+ (BOOL)isTrackRecordingEmpty {
return GetFramework().IsTrackRecordingEmpty();
}
+ (ElevationProfileData * _Nonnull)trackRecordingElevationInfo {
return [[ElevationProfileData alloc] initWithElevationInfo:GetFramework().GetTrackRecordingElevationInfo()];
}
@end

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>

View file

@ -0,0 +1,23 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, LogLevel) {
LogLevelDebug = 0,
LogLevelInfo,
LogLevelWarning,
LogLevelError,
LogLevelCritical
};
@interface Logger : NSObject
+ (void)log:(LogLevel)level message:(NSString *)message;
+ (BOOL)canLog:(LogLevel)level;
+ (void)setFileLoggingEnabled:(BOOL)fileLoggingEnabled;
+ (nullable NSURL *)getLogFileURL;
+ (uint64_t)getLogFileSize;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,258 @@
#import "Logger.h"
#import <OSLog/OSLog.h>
#include "base/logging.hpp"
#include "coding/zip_creator.hpp"
@interface Logger ()
@property (nullable, nonatomic) NSFileHandle * fileHandle;
@property (nonnull, nonatomic) os_log_t osLogger;
/// This property is introduced to avoid the CoreApi => Maps target dependency and stores the MWMSettings.isFileLoggingEnabled value.
@property (class, nonatomic) BOOL fileLoggingEnabled;
@property (class, readonly, nonatomic) dispatch_queue_t fileLoggingQueue;
+ (Logger *)logger;
+ (void)enableFileLogging;
+ (void)disableFileLogging;
+ (void)logMessageWithLevel:(base::LogLevel)level src:(base::SrcPoint const &)src message:(std::string const &)message;
+ (void)tryWriteToFile:(std::string const &)logString;
+ (NSURL *)getZippedLogFile:(NSString *)logFilePath;
+ (void)removeFileAtPath:(NSString *)filePath;
+ (base::LogLevel)baseLevel:(LogLevel)level;
@end
// Subsystem and category are used for the OSLog.
NSString * const kLoggerSubsystem = [[NSBundle mainBundle] bundleIdentifier];
NSString * const kLoggerCategory = @"OM";
NSString * const kLogFileName = @"log.txt";
NSString * const kZipLogFileExtension = @"zip";
NSString * const kLogFilePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
stringByAppendingPathComponent:kLogFileName];
// TODO: (KK) Review and change this limit after some testing.
NSUInteger const kMaxLogFileSize = 1024 * 1024 * 100; // 100 MB;
@implementation Logger
static BOOL _fileLoggingEnabled = NO;
+ (void)initialize
{
if (self == [Logger class]) {
SetLogMessageFn(&LogMessage);
SetAssertFunction(&AssertMessage);
}
}
+ (Logger *)logger {
static Logger * logger = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
logger = [[self alloc] init];
});
return logger;
}
+ (dispatch_queue_t)fileLoggingQueue {
static dispatch_queue_t fileLoggingQueue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
fileLoggingQueue = dispatch_queue_create("app.comaps.fileLoggingQueue", attributes);
});
return fileLoggingQueue;
}
- (instancetype)init {
self = [super init];
if (self) {
_osLogger = os_log_create(kLoggerSubsystem.UTF8String, kLoggerCategory.UTF8String);
}
return self;
}
// MARK: - Public
+ (void)setFileLoggingEnabled:(BOOL)fileLoggingEnabled {
fileLoggingEnabled ? [self enableFileLogging] : [self disableFileLogging];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
LOG_SHORT(LINFO, ("Local time:", NSDate.date.description.UTF8String, ", Time Zone:", NSTimeZone.defaultTimeZone.abbreviation.UTF8String));
});
LOG(LINFO, ("File logging is enabled:", _fileLoggingEnabled ? "YES" : "NO"));
}
+ (BOOL)fileLoggingEnabled {
return _fileLoggingEnabled;
}
+ (void)log:(LogLevel)level message:(NSString *)message {
LOG_SHORT([self baseLevel:level], (message.UTF8String));
}
+ (BOOL)canLog:(LogLevel)level {
return [Logger baseLevel:level] >= base::g_LogLevel;
}
+ (nullable NSURL *)getLogFileURL {
if ([self fileLoggingEnabled]) {
if (![NSFileManager.defaultManager fileExistsAtPath:kLogFilePath]) {
LOG(LERROR, ("Log file doesn't exist while file logging is enabled:", kLogFilePath.UTF8String));
return nil;
}
return [self getZippedLogFile:kLogFilePath];
} else {
// Fetch logs from the OSLog store.
if (@available(iOS 15.0, *)) {
NSError * error;
OSLogStore * store = [OSLogStore storeWithScope:OSLogStoreCurrentProcessIdentifier error:&error];
if (error) {
LOG(LERROR, (error.localizedDescription.UTF8String));
return nil;
}
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"subsystem == %@", kLoggerSubsystem];
OSLogEnumerator * enumerator = [store entriesEnumeratorWithOptions:{} position:nil predicate:predicate error:&error];
if (error) {
LOG(LERROR, (error.localizedDescription.UTF8String));
return nil;
}
NSMutableString * logString = [NSMutableString string];
NSString * kNewLineStr = @"\n";
id object;
while (object = [enumerator nextObject]) {
if ([object isMemberOfClass:[OSLogEntryLog class]]) {
[logString appendString:[object composedMessage]];
[logString appendString:kNewLineStr];
}
}
if (logString.length == 0) {
LOG(LINFO, ("OSLog entry is empty."));
return nil;
}
[NSFileManager.defaultManager createFileAtPath:kLogFilePath contents:[logString dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
return [self getZippedLogFile:kLogFilePath];
} else {
return nil;
}
}
}
+ (uint64_t)getLogFileSize {
Logger * logger = [self logger];
return logger.fileHandle != nil ? [logger.fileHandle offsetInFile] : 0;
}
// MARK: - C++ injection
void LogMessage(base::LogLevel level, base::SrcPoint const & src, std::string const & message)
{
[Logger logMessageWithLevel:level src:src message:message];
CHECK_LESS(level, base::g_LogAbortLevel, ("Abort. Log level is too serious", level));
}
bool AssertMessage(base::SrcPoint const & src, std::string const & message)
{
[Logger logMessageWithLevel:base::LCRITICAL src:src message:message];
return true;
}
// MARK: - Private
+ (void)enableFileLogging {
Logger * logger = [self logger];
NSFileManager * fileManager = [NSFileManager defaultManager];
// Create a log file if it doesn't exist and setup file handle for writing.
if (![fileManager fileExistsAtPath:kLogFilePath])
[fileManager createFileAtPath:kLogFilePath contents:nil attributes:nil];
NSFileHandle * fileHandle = [NSFileHandle fileHandleForWritingAtPath:kLogFilePath];
if (fileHandle == nil) {
LOG(LERROR, ("Failed to open log file for writing", kLogFilePath.UTF8String));
[self disableFileLogging];
return;
}
// Clean up the file if it exceeds the maximum size.
if ([fileManager contentsAtPath:kLogFilePath].length > kMaxLogFileSize)
[fileHandle truncateFileAtOffset:0];
logger.fileHandle = fileHandle;
_fileLoggingEnabled = YES;
}
+ (void)disableFileLogging {
Logger * logger = [self logger];
[logger.fileHandle closeFile];
logger.fileHandle = nil;
[self removeFileAtPath:kLogFilePath];
_fileLoggingEnabled = NO;
}
+ (void)logMessageWithLevel:(base::LogLevel)level src:(base::SrcPoint const &)src message:(std::string const &)message {
// Build the log message string.
auto & logHelper = base::LogHelper::Instance();
std::ostringstream output;
// TODO: (KK) Either guard this call, or refactor thread ids in logHelper.
logHelper.WriteProlog(output, level);
logHelper.WriteLog(output, src, message);
auto const logString = output.str();
// Log the message into the system log.
os_log([self logger].osLogger, "%{public}s", logString.c_str());
if (level < base::GetDefaultLogAbortLevel())
dispatch_async([self fileLoggingQueue], ^{ [self tryWriteToFile:logString]; });
else
[self tryWriteToFile:logString];
}
+ (void)tryWriteToFile:(std::string const &)logString {
NSFileHandle * fileHandle = [self logger].fileHandle;
if (fileHandle != nil) {
[fileHandle seekToEndOfFile];
[fileHandle writeData:[NSData dataWithBytes:logString.c_str() length:logString.length()]];
}
}
+ (NSURL *)getZippedLogFile:(NSString *)logFilePath {
NSString * zipFileName = [[logFilePath.lastPathComponent stringByDeletingPathExtension] stringByAppendingPathExtension:kZipLogFileExtension];
NSString * zipFilePath = [[NSFileManager.defaultManager temporaryDirectory] URLByAppendingPathComponent:zipFileName].path;
auto const success = CreateZipFromFiles({logFilePath.UTF8String}, zipFilePath.UTF8String);
if (!success) {
LOG(LERROR, ("Failed to zip log file:", kLogFilePath.UTF8String, ". The original file will be returned."));
return [NSURL fileURLWithPath:logFilePath];
}
return [NSURL fileURLWithPath:zipFilePath];
}
+ (void)removeFileAtPath:(NSString *)filePath {
if ([NSFileManager.defaultManager fileExistsAtPath:filePath]) {
NSError * error;
[NSFileManager.defaultManager removeItemAtPath:filePath error:&error];
if (error)
LOG(LERROR, (error.localizedDescription.UTF8String));
}
}
+ (base::LogLevel)baseLevel:(LogLevel)level {
switch (level) {
case LogLevelDebug: return LDEBUG;
case LogLevelInfo: return LINFO;
case LogLevelWarning: return LWARNING;
case LogLevelError: return LERROR;
case LogLevelCritical: return LCRITICAL;
}
}
@end

View file

@ -0,0 +1,31 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, MWMNetworkPolicyPermission) {
MWMNetworkPolicyPermissionAsk,
MWMNetworkPolicyPermissionAlways,
MWMNetworkPolicyPermissionNever,
MWMNetworkPolicyPermissionToday,
MWMNetworkPolicyPermissionNotToday
};
typedef NS_ENUM(NSInteger, MWMConnectionType) {
MWMConnectionTypeNone,
MWMConnectionTypeWifi,
MWMConnectionTypeCellular
} NS_SWIFT_NAME(ConnectionType);
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(NetworkPolicy)
@interface MWMNetworkPolicy: NSObject
@property(nonatomic) MWMNetworkPolicyPermission permission;
@property(nonatomic, readonly) NSDate *permissionExpirationDate;
@property(nonatomic, readonly) BOOL canUseNetwork;
@property(nonatomic, readonly) MWMConnectionType connectionType;
+ (MWMNetworkPolicy *)sharedPolicy;
@end
NS_ASSUME_NONNULL_END

Some files were not shown because too many files have changed in this diff Show more