Files
tripz-llc/apps/rider_new/ios/Runner/AppDelegate.swift
T
Hamza-AyedandClaude Opus 5 69da4abc01 feat(apps): سقالة rider_new و driver_new على Cubit — المرحلة 1
بناء من الصفر على باك إند NestJS، بقرار المالك 2026-08-04 الذي يعكس قرارَي
نقل GetX (2026-07-21) واعتماد باك إند PHP (2026-07-27). لا يُورَّث سطر دارت
من apps/*-archive-cubit، وسيرو مرجع بصري وسلوكي لا مصدر نسخ.

الوثائق:
- docs/37: الخطة الكاملة بستّ مراحل وبواباتها
- docs/38: عقد الـAPI من 37 controller، معظمه متحقَّق حيّاً من السيرفر المنشور
- docs/39: جرد 202 شاشة في تطبيقَي سيرو، مصنّفة داخل/خارج النطاق

الطبقة الأصلية منقولة من *-archive-cubit وحدها لأنها هوية النشر:
- rider_new  → com.mobileapp.store.ride · shorebird 496cb3ac
- driver_new → com.sefer_driver (أندرويد) · com.sefer.driver (iOS) · 68cc9345
- أُصلح تعارض: هدف RunnerTests في driver_new كان يحمل bundle الراكب

الأصول مصدرها *-archive-cubit لا سيرو: أصول الأرشيف مجموعة أشمل (كل صور
سيرو + صور تريبز) وخطوطها هي خطوط docs/26. استُكمل السائق بعشرة ملفات
ناقصة من سيرو (شعارات مزوّدي الدفع + صوتان).

lib/ مكتوب من الصفر (11 ملف لكل تطبيق):
- AppConfig بأعلام const — أساس نموذج lite/pro/max
- TokenStore على التخزين الآمن، يقرأ exp محليّاً بلا حزمة خارجية
- AuthInterceptor بتجديد استباقي وطلقة واحدة — عمر التوكن 15 دقيقة فقط
- ApiClient و ApiException يفهم مصفوفة message في NestJS

flutter analyze نظيف في التطبيقين. البناء الفعلي لم يُجرَّب بعد: بوابة
المرحلة 1 تتطلّب البناء على السيرفر، والوصول إليه غير متاح حالياً.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 21:21:26 +03:00

140 lines
5.4 KiB
Swift

import UIKit
import Flutter
import FirebaseCore
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
GeneratedPluginRegistrant.register(with: self)
// Perform security checks *before* showing the main UI
if SecurityChecks.isDeviceCompromised() { // Call Objective-C method
showSecurityAlert()
return false // Prevent app from launching further
}
//setup method channel
setupMethodChannel()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func setupMethodChannel(){
guard let controller = window?.rootViewController as? FlutterViewController else {
return
}
let channel = FlutterMethodChannel(name: "com.mobileapp.store.ride/security",
binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler { call, result in
switch call.method {
case "isNativeRooted":
// We already check this in isDeviceCompromised,
// so we can just return the result directly.
result(SecurityChecks.isDeviceCompromised())
default:
result(FlutterMethodNotImplemented)
}
}
}
func showSecurityAlert() {
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else {
// Handle the case where rootVC is nil (extremely unlikely, but good practice)
exit(0)
// return
}
let alert = UIAlertController(
title: "Security Warning".localized, // Use localized strings!
message: "Compromised device detected. Exiting.".localized,
preferredStyle: .alert
)
// Add an "OK" button with an obfuscated action
alert.addAction(UIAlertAction(title: "OK".localized, style: .destructive) { _ in
// More robust exit (see explanation below)
self.obfuscatedExit()
})
// Use a presentationDetents to prevent dismissing
if #available(iOS 15.0, *) {
alert.popoverPresentationController?.sourceView = rootVC.view
alert.popoverPresentationController?.sourceRect = CGRect(x: rootVC.view.bounds.midX, y: rootVC.view.bounds.midY, width: 0, height: 0)
alert.popoverPresentationController?.permittedArrowDirections = []
alert.presentationController?.delegate = self
if let presentationController = alert.presentationController as? UISheetPresentationController {
presentationController.detents = [.medium()] // Set a fixed height if desired
presentationController.preferredCornerRadius = 20;
presentationController.prefersEdgeAttachedInCompactHeight = true;// Set corner radius
presentationController.prefersGrabberVisible = false // Optional: show a grabber
}
} else {
// Fallback on earlier versions
}
rootVC.present(alert, animated: true, completion: nil)
}
// Obfuscated Exit (Example - you'll need a more robust solution)
func obfuscatedExit() {
// This is a *very* basic example. A real obfuscation would be MUCH more complex.
let selector = NSSelectorFromString(String(format: "%@%@", "ex", "it:"))
if responds(to: selector) {
perform(selector, with: 0) // Call exit(0) indirectly
}
}
// MARK: - Secure API Key Storage (Example)
func getAPIKey() -> String {
// DO NOT store API keys directly in your code!
// Load it from a more secure location, such as:
// 1. A configuration file that is *not* checked into source control.
// 2. Keychain Services (recommended for iOS).
// 3. A backend server (best option).
// This is just a placeholder. Replace with your actual key loading.
// In a real app, you'd fetch this from a secure source.
guard let filePath = Bundle.main.path(forResource: "keys", ofType: "plist"),
let plist = NSDictionary(contentsOfFile: filePath),
let apiKey = plist["google_maps_api_key"] as? String else {
fatalError("Couldn't find keys or API Key") // Ensure a crash if not found
}
return apiKey
}
func loadConfig() -> [String: Any]? {
guard let path = Bundle.main.path(forResource: "Config", ofType: "plist"),
let config = NSDictionary(contentsOfFile: path) as? [String: Any] else {
return nil // Or handle the error appropriately
}
return config
}
}
// Add for prevent dismissing
extension AppDelegate: UIAdaptivePresentationControllerDelegate {
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
return false
}
func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
}
}
// Add String extension
extension String {
var localized: String {
return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
}
}