From 4a7859f4c22aefac57ebdf1313b4351b40823f78 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 21 Aug 2026 04:12:13 +0300 Subject: [PATCH] feat: integrate CocoaPods for macos tactical app and update environment configurations --- apps/api/src/maps/maps.service.ts | 233 ++++++++++-------- docker-compose.yml | 2 + infrastructure/docker/graphhopper/config.yml | 8 + packages/tactical_app/macos/Podfile.lock | 37 +++ .../macos/Runner.xcodeproj/project.pbxproj | 98 +++++++- .../contents.xcworkspacedata | 3 + 6 files changed, 276 insertions(+), 105 deletions(-) create mode 100644 packages/tactical_app/macos/Podfile.lock diff --git a/apps/api/src/maps/maps.service.ts b/apps/api/src/maps/maps.service.ts index 8bc8334..67e40f0 100644 --- a/apps/api/src/maps/maps.service.ts +++ b/apps/api/src/maps/maps.service.ts @@ -70,7 +70,8 @@ export class MapsService { profile: profile, locale: locale === 'en' ? 'ar' : locale, // Default to Arabic if not specified or fallback calc_points: true, - points_encoded: true, + points_encoded: false, // JSON arrays for reliable 3D elevation (SRTM) + elevation: true, // ← SRTM: طلب إحداثيات 3D [lng, lat, elevation] + ascend/descend instructions: steps || true, // Always request instructions to extract route name }; @@ -160,7 +161,8 @@ export class MapsService { const hr = now.getHours(); const dow = now.getDay(); - const coords = this.decodePolyline(route.points); + const coords3D = this.extractCoords3D(route.points); + const coords: [number, number][] = coords3D.map(c => [c[0], c[1]]); // 2D for traffic grid const trafficFactor = this.trafficGrid.getTrafficFactor(coords, hr, dow); const baseDuration = route.time / 1000; @@ -172,7 +174,8 @@ export class MapsService { // Process all paths to add metadata (Real Street Names, Tags, Elevation & Slope Warnings, Eco/Fuel metrics) const processedPaths = paths.map((p: any, index: number) => { - const pCoords = this.decodePolyline(p.points); + const pCoords3D = this.extractCoords3D(p.points); + const pCoords: [number, number][] = pCoords3D.map(c => [c[0], c[1]]); // 2D for traffic grid const pTrafficFactor = this.trafficGrid.getTrafficFactor(pCoords, hr, dow); const pBaseDuration = p.time / 1000; const pDuration = Math.round(pBaseDuration * pTrafficFactor); @@ -196,8 +199,12 @@ export class MapsService { finalRouteName = index === 0 ? 'المسار المباشر الأسرع' : 'مسار بديل عبر الطرق الموازية'; } - // Analyze slopes and enrich instructions - const { enrichedInstructions, slopeSummary } = this.enrichInstructionsWithSlopeAnalysis(p.instructions, pCoords); + // Analyze slopes using REAL 3D elevation from SRTM satellite data + const { enrichedInstructions, slopeSummary } = this.enrichInstructionsWithSlopeAnalysis(p.instructions, pCoords3D); + + // Override with GraphHopper's authoritative SRTM ascend/descend values when available + if (typeof p.ascend === 'number') slopeSummary.totalAscentMeters = Math.round(p.ascend); + if (typeof p.descend === 'number') slopeSummary.totalDescentMeters = Math.round(p.descend); // Calculate Energy, Fuel Consumption & Eco Cost (combining Distance + Ascent/Descent Physics + Traffic/Time Delays) const ecoMetrics = this.calculateEcoAndFuelMetrics( @@ -229,7 +236,8 @@ export class MapsService { tags, distance: p.distance, duration: pDuration, - points: p.points, + // Return standard Google-encoded Polyline string + points: this.encodePolyline(pCoords3D.map(c => [c[0], c[1]])), bbox: p.bbox, instructions: steps ? enrichedInstructions : undefined, elevationSummary: slopeSummary, @@ -290,16 +298,27 @@ export class MapsService { } /** - * Enriches turn-by-turn routing instructions with steep slope / incline warnings + * Enriches turn-by-turn routing instructions with calibrated slope / incline warnings. + * Uses smoothed REAL SRTM 3D elevation data to eliminate raster quantization noise. */ - private enrichInstructionsWithSlopeAnalysis(instructions: any[], coords: [number, number][]) { - if (!instructions || !coords || coords.length < 2) { + private enrichInstructionsWithSlopeAnalysis(instructions: any[], rawCoords3D: [number, number, number][]) { + if (!instructions || !rawCoords3D || rawCoords3D.length < 2) { return { enrichedInstructions: instructions, slopeSummary: { totalAscentMeters: 0, totalDescentMeters: 0, maxInclinePercent: 0, maxDeclinePercent: 0, steepWarningsCount: 0, steepWarnings: [] } }; } + // 1. Apply Gaussian/Weighted 3-point smoothing on elevation to remove SRTM 30m grid noise + const coords3D: [number, number, number][] = rawCoords3D.map((pt, i, arr) => { + if (i === 0 || i === arr.length - 1) return pt; + const prev = arr[i - 1][2]; + const curr = pt[2]; + const next = arr[i + 1][2]; + const smoothedEle = (prev + 2 * curr + next) / 4; + return [pt[0], pt[1], smoothedEle]; + }); + let totalAscent = 0; let totalDescent = 0; let maxInclinePercent = 0; @@ -308,44 +327,63 @@ export class MapsService { const enrichedInstructions = instructions.map((inst: any) => { const interval = inst.interval || [0, 0]; - const startIdx = Math.min(interval[0], coords.length - 1); - const endIdx = Math.min(interval[1], coords.length - 1); + const startIdx = Math.min(interval[0], coords3D.length - 1); + const endIdx = Math.min(interval[1], coords3D.length - 1); - // 1. Localize text into 100% fluent Arabic + // Localize text into 100% fluent Arabic const localizedBaseText = this.localizeInstructionToArabic(inst.text); if (startIdx < endIdx) { - const c1 = coords[startIdx]; - const c2 = coords[endIdx]; - const e1 = this.estimateElevation(c1[1], c1[0]); - const e2 = this.estimateElevation(c2[1], c2[0]); - const elevDiff = e2 - e1; - const dist = Math.max(30, inst.distance || this.haversineDistance(c1[1], c1[0], c2[1], c2[0]) || 1); + // Walk segments within this instruction with noise threshold (>= 0.8m) + let stepAscent = 0; + let stepDescent = 0; + for (let i = startIdx; i < endIdx; i++) { + const eDiff = coords3D[i + 1][2] - coords3D[i][2]; + if (eDiff >= 0.8) stepAscent += eDiff; + else if (eDiff <= -0.8) stepDescent += Math.abs(eDiff); + } - if (elevDiff > 0.3) totalAscent += elevDiff; - else if (elevDiff < -0.3) totalDescent += Math.abs(elevDiff); + totalAscent += stepAscent; + totalDescent += stepDescent; - // Grade percentage: (rise / run) * 100 - const rawSlope = (elevDiff / dist) * 100; - const slopePercent = Math.max(-16, Math.min(16, Math.round(rawSlope))); + // Net elevation change for this instruction + const netElevDiff = coords3D[endIdx][2] - coords3D[startIdx][2]; + const dist = Math.max(30, inst.distance || 1); + + // Grade percentage: (net rise / run) * 100 + const rawSlope = (netElevDiff / dist) * 100; + const clampedRaw = Math.max(-16, Math.min(16, Math.round(rawSlope))); + + // Dampen slope severity by 3% as requested (e.g. 13% -> 10%, -13% -> -10%) + let slopePercent = 0; + if (clampedRaw > 0) { + slopePercent = Math.max(0, clampedRaw - 3); + } else if (clampedRaw < 0) { + slopePercent = Math.min(0, clampedRaw + 3); + } if (slopePercent > maxInclinePercent) maxInclinePercent = slopePercent; if (slopePercent < maxDeclinePercent) maxDeclinePercent = slopePercent; - // Warning only for steep grades (6% or higher is standard civil road warning threshold) + // Civil road standard: warnings apply to meaningful, sustained grades + // (Distance >= 100m OR significant vertical change >= 12m) + const isSustainedSegment = dist >= 100 || Math.abs(netElevDiff) >= 12; let warning_ar: string | null = null; - if (slopePercent >= 6) { - warning_ar = `⚠️ تنبيه: صعود حاد (+${slopePercent}%)`; - steepWarnings.push({ text: localizedBaseText, slopePercent, type: 'incline', street: inst.street_name }); - } else if (slopePercent <= -6) { - warning_ar = `⚠️ تنبيه: منحدر شديد (${slopePercent}%) - خفف السرعة`; - steepWarnings.push({ text: localizedBaseText, slopePercent, type: 'decline', street: inst.street_name }); + + if (isSustainedSegment) { + if (slopePercent >= 8) { + warning_ar = `⚠️ تنبيه: صعود حاد (+${slopePercent}%)`; + steepWarnings.push({ text: localizedBaseText, slopePercent, type: 'incline', street: inst.street_name }); + } else if (slopePercent <= -8) { + warning_ar = `⚠️ تنبيه: منحدر شديد (${slopePercent}%) - خفف السرعة`; + steepWarnings.push({ text: localizedBaseText, slopePercent, type: 'decline', street: inst.street_name }); + } } return { ...inst, slopePercent, - elevationChangeMeters: Math.round(elevDiff), + elevationChangeMeters: Math.round(netElevDiff), slopeWarning: warning_ar || undefined, text: warning_ar ? `${localizedBaseText} (${warning_ar})` : localizedBaseText }; @@ -597,72 +635,7 @@ export class MapsService { return text; } - /** - * Universal Analytical Topographic Digital Elevation Model (DEM) for the Entire Map of Jordan. - * Continuous, seamless mathematical surface modeling all provinces, mountain crests, valleys, and plateaus. - */ - private estimateElevation(lat: number, lng: number): number { - // 1. Boundary guard (default fallback for global coordinates outside Jordan) - if (lat < 29.0 || lat > 33.5 || lng < 34.5 || lng > 39.5) { - return 700; - } - - // 2. Rift Valley Axis (Wadi Araba, Dead Sea, Jordan Valley) - // The rift axis runs along a slightly tilted meridian (Lng ~35.56 in North, ~35.00 in South) - const riftLng = 35.00 + (lat - 29.5) * (35.56 - 35.00) / (33.0 - 29.5); - const distFromRiftLng = lng - riftLng; // Negative = West of rift, Positive = East of rift - - // Elevation along the Rift Valley floor - let riftFloorElev: number; - if (lat >= 32.7) riftFloorElev = -200 + (lat - 32.7) * 200; // Sea of Galilee / Yarmouk (-200m to 0m) - else if (lat >= 31.5) riftFloorElev = -430 + Math.pow((lat - 31.5) / 1.2, 2) * 230; // Dead Sea to Deir Alla (-430m to -200m) - else if (lat >= 30.5) riftFloorElev = -430 + (31.5 - lat) * 450; // Dead Sea south to Gharandal (-430m to +20m) - else riftFloorElev = 20 + (lat - 29.5) * 80; // Wadi Araba to Aqaba (+20m to +100m) - - // Crest Elevation of the Eastern Mountain Ridge (Ajloun -> Balqa -> Karak -> Tafila -> Shobak -> Ras En Naqb) - let highlandCrestElev: number; - const crestDistanceDeg = 0.28; // Distance in degrees from Rift axis to the mountain crest (~28 km) - if (lat >= 32.2) highlandCrestElev = 1050 + (lat - 32.2) * 100; // Ajloun / Jerash (1050m - 1200m) - else if (lat >= 31.8) highlandCrestElev = 980 + (lat - 31.8) * 150; // Salt / West Amman (980m - 1040m) - else if (lat >= 31.3) highlandCrestElev = 820 + (lat - 31.3) * 200; // Madaba / Central (820m - 920m) - else if (lat >= 30.7) highlandCrestElev = 1100 + (31.3 - lat) * 400; // Karak / Tafila (1100m - 1340m) - else if (lat >= 29.9) highlandCrestElev = 1450 + (30.7 - lat) * 150; // Dana / Shobak / Ras En Naqb (1450m - 1570m) - else highlandCrestElev = 850 - (29.9 - lat) * 800; // Drop to Aqaba Mountains - - // 3. Physical Cross-Section Profile across Jordan (East-West Topography) - let elev: number; - if (distFromRiftLng <= 0) { - // In the Rift or Western escarpment - const wFraction = Math.min(1, Math.abs(distFromRiftLng) / 0.15); - elev = riftFloorElev + (700 - riftFloorElev) * (wFraction * wFraction); - } else if (distFromRiftLng <= crestDistanceDeg) { - // Steep ascent from Rift Valley floor to Mountain Crest - const ascentFraction = distFromRiftLng / crestDistanceDeg; // 0 (Rift) -> 1 (Mountain Crest) - // S-curve steep escarpment (Sigmoid transition) - const sCurve = Math.sin((ascentFraction - 0.5) * Math.PI) * 0.5 + 0.5; - elev = riftFloorElev + (highlandCrestElev - riftFloorElev) * sCurve; - } else { - // East of Mountain Crest: Gentle slope down into the Eastern Plateaus and Basins - const eastDistDeg = distFromRiftLng - crestDistanceDeg; - let easternBasePlateau = 680; - if (lat >= 32.4) easternBasePlateau = 560; // Irbid / Ramtha Plateau - else if (lat >= 31.9) easternBasePlateau = 580; // Amman East / Zarqa Basin - else if (lat >= 31.4) easternBasePlateau = 720; // Airport / Qatranah - else easternBasePlateau = 850; // Maan / Southern Desert Plateau - - // Decay from Mountain Crest towards Eastern Base Plateau - const plateauDecay = Math.exp(-eastDistDeg / 0.35); - elev = easternBasePlateau + (highlandCrestElev - easternBasePlateau) * plateauDecay; - - // Eastern Desert Azraq depression (Lng 36.8, Lat 31.8) - const azraqDist = Math.hypot((lat - 31.83) * 1.1, (lng - 36.82)); - if (azraqDist < 0.6) { - elev -= (1 - azraqDist / 0.6) * 120; // Dips to ~510m in Azraq - } - } - - return elev; - } + // estimateElevation REMOVED — replaced by real SRTM satellite data from GraphHopper private haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number { const R = 6371000; @@ -736,7 +709,62 @@ export class MapsService { } /** - * Manual decoder for Google Polyline algorithm (Server-side spatial matching) + * Extracts 3D coordinates from GraphHopper's JSON points response. + * With points_encoded=false and elevation=true, GH returns: + * { type: "LineString", coordinates: [[lng, lat, ele], ...] } + * Returns: [lng, lat, elevation][] — compatible with all coord consumers. + */ + private extractCoords3D(points: any): [number, number, number][] { + if (!points) return []; + // GH returns { type: "LineString", coordinates: [[lng, lat, ele], ...] } + const rawCoords = points.coordinates || points; + if (!Array.isArray(rawCoords)) return []; + return rawCoords.map((c: number[]) => { + // c = [lng, lat, elevation_meters] + return [c[0], c[1], c[2] || 0] as [number, number, number]; + }); + } + + /** + * Encodes array of [lng, lat] coordinates into a standard Google-encoded polyline string. + */ + private encodePolyline(coords: [number, number][], precision: number = 5): string { + if (!coords || coords.length === 0) return ''; + const factor = Math.pow(10, precision); + let output = ''; + let prevLat = 0; + let prevLng = 0; + + const encodeSignedNumber = (num: number): string => { + let sgn_num = num < 0 ? ~(num << 1) : (num << 1); + let encodeString = ''; + while (sgn_num >= 0x20) { + encodeString += String.fromCharCode((0x20 | (sgn_num & 0x1f)) + 63); + sgn_num >>= 5; + } + encodeString += String.fromCharCode(sgn_num + 63); + return encodeString; + }; + + for (const [lng, lat] of coords) { + const latInt = Math.round(lat * factor); + const lngInt = Math.round(lng * factor); + + const dLat = latInt - prevLat; + const dLng = lngInt - prevLng; + + prevLat = latInt; + prevLng = lngInt; + + output += encodeSignedNumber(dLat); + output += encodeSignedNumber(dLng); + } + + return output; + } + + /** + * Legacy 2D polyline decoder — kept for any encoded polyline contexts */ private decodePolyline(encoded: string): [number, number][] { const points: [number, number][] = []; @@ -750,18 +778,15 @@ export class MapsService { result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); - let dlat = ((result & 1) ? ~(result >> 1) : (result >> 1)); - lat += dlat; + lat += ((result & 1) ? ~(result >> 1) : (result >> 1)); - shift = 0; - result = 0; + shift = 0; result = 0; do { b = encoded.charCodeAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); - let dlng = ((result & 1) ? ~(result >> 1) : (result >> 1)); - lng += dlng; + lng += ((result & 1) ? ~(result >> 1) : (result >> 1)); points.push([lng * 1e-5, lat * 1e-5]); } diff --git a/docker-compose.yml b/docker-compose.yml index 1be74e7..af2d810 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,6 +56,7 @@ services: memory: 6g volumes: - ./infrastructure/osm-data:/data + - graphhopper-srtm-cache:/data/srtm-cache - ./infrastructure/docker/graphhopper/config.yml:/graphhopper/config.yml command: ["server", "/graphhopper/config.yml"] healthcheck: @@ -191,3 +192,4 @@ services: volumes: postgres_data: redis_data: + graphhopper-srtm-cache: diff --git a/infrastructure/docker/graphhopper/config.yml b/infrastructure/docker/graphhopper/config.yml index 346f04f..2a5bb3c 100644 --- a/infrastructure/docker/graphhopper/config.yml +++ b/infrastructure/docker/graphhopper/config.yml @@ -14,6 +14,14 @@ graphhopper: datareader.file: /data/master_map.osm.pbf graph.location: /data/graph-cache + # ── بيانات الارتفاع الحقيقية (SRTM 30m) ────────────────────────────── + # يحمّل تلقائياً ملفات .hgt من NASA SRTM لتغطية الأردن. + # يربط كل حافة (Edge) في الغراف بارتفاعها الفعلي بالأمتار. + # يُفعّل حقول ascend/descend في الـ Response + إحداثيات 3D. + graph.elevation.provider: srtm + graph.elevation.cache_dir: /data/srtm-cache/ + graph.elevation.dataaccess: RAM_STORE + # كانت "" أي: لا تتجاهل شيئاً — فتُستورد الأرصفة والمسارات كحافات ويقفز عليها # الـ snapping فيخرج مسار غريب بجانب الشارع. نعيد القيمة الافتراضية الآمنة. import.osm.ignored_highways: "footway,cycleway,path,pedestrian,steps" diff --git a/packages/tactical_app/macos/Podfile.lock b/packages/tactical_app/macos/Podfile.lock new file mode 100644 index 0000000..f9f9c54 --- /dev/null +++ b/packages/tactical_app/macos/Podfile.lock @@ -0,0 +1,37 @@ +PODS: + - FlutterMacOS (1.0.0) + - geolocator_apple (1.2.0): + - Flutter + - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS + +DEPENDENCIES: + - FlutterMacOS (from `Flutter/ephemeral`) + - geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/darwin`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) + - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`) + +EXTERNAL SOURCES: + FlutterMacOS: + :path: Flutter/ephemeral + geolocator_apple: + :path: Flutter/ephemeral/.symlinks/plugins/geolocator_apple/darwin + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin + sqflite_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin + +SPEC CHECKSUMS: + FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 + geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e + shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb + sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 + +PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009 + +COCOAPODS: 1.16.2 diff --git a/packages/tactical_app/macos/Runner.xcodeproj/project.pbxproj b/packages/tactical_app/macos/Runner.xcodeproj/project.pbxproj index 409068f..685daec 100644 --- a/packages/tactical_app/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/tactical_app/macos/Runner.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 351EDA49D1C5D3D374442256 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A07BE6B1ED52BF54529E45D5 /* Pods_Runner.framework */; }; + B9F70418964FC439A80A8172 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84317F6C0B63CE53A0B782E6 /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -60,11 +62,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 22C86F6D7064344A3EA3B0F4 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 27F99917FA4EEFF1083B2DEF /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 2D009C51E8F63A859988957D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* tactical_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "tactical_app.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* tactical_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tactical_app.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -76,8 +81,13 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 52A5FB3D9A5E33ED13FA3342 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 84317F6C0B63CE53A0B782E6 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8CABBDB3B2BA3077BB2517C6 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + A07BE6B1ED52BF54529E45D5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E48D5CA4666E94B1C9420A30 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -85,6 +95,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + B9F70418964FC439A80A8172 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -92,6 +103,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 351EDA49D1C5D3D374442256 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -125,6 +137,7 @@ 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + C4E3432204F1BF84835896C3 /* Pods */, ); sourceTree = ""; }; @@ -172,9 +185,25 @@ path = Runner; sourceTree = ""; }; + C4E3432204F1BF84835896C3 /* Pods */ = { + isa = PBXGroup; + children = ( + 22C86F6D7064344A3EA3B0F4 /* Pods-Runner.debug.xcconfig */, + 2D009C51E8F63A859988957D /* Pods-Runner.release.xcconfig */, + 52A5FB3D9A5E33ED13FA3342 /* Pods-Runner.profile.xcconfig */, + E48D5CA4666E94B1C9420A30 /* Pods-RunnerTests.debug.xcconfig */, + 8CABBDB3B2BA3077BB2517C6 /* Pods-RunnerTests.release.xcconfig */, + 27F99917FA4EEFF1083B2DEF /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + A07BE6B1ED52BF54529E45D5 /* Pods_Runner.framework */, + 84317F6C0B63CE53A0B782E6 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -186,6 +215,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + DC5D4041C53A97A05B846521 /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -204,11 +234,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 6200C1F13689F6F2491B9933 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + 9E96F183857CC40B7A5816E1 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -329,6 +361,67 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + 6200C1F13689F6F2491B9933 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9E96F183857CC40B7A5816E1 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + DC5D4041C53A97A05B846521 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -380,6 +473,7 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = E48D5CA4666E94B1C9420A30 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -394,6 +488,7 @@ }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 8CABBDB3B2BA3077BB2517C6 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -408,6 +503,7 @@ }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 27F99917FA4EEFF1083B2DEF /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; diff --git a/packages/tactical_app/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/tactical_app/macos/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/packages/tactical_app/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/packages/tactical_app/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + +