4. تهيئة SDK
4.1 AppDelegate
أنشئ صنفًا فرعيًا من AKSBaseAppDelegate — وهو صنف أساسي يعتمد نمط أسلوب القالب (Template Method)، ويتولى إعداد Firebase وتهيئة AdMob وتهيئة SDK والإعداد الأولي للإعدادات عن بُعد، مع مجموعة من نقاط التخصيص المعلنة بالكلمة open لربط منطق تطبيقك بها.
import UIKit
import AppsKit
import AKSKit
class AppDelegate: AKSBaseAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
AppsKitSDKAdsManagerIOS.instance.initialize(userHasGivenConsent: true, isCCPAConsent: true)
// super.application(...) does, in order:
// FirebaseApp.configure() -> MobileAds.shared.start() -> setupAdBridge()
// -> AppsKitSDK init/setDevMode/setTestMode/saveDefaultConfigJson
// -> configureSDK() -> manageRemoteConfigs(...)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
/// Called by the base class during the sequence above. Optional — only needed if your
/// app has its own ad-loading abstraction layer that needs to be wired up to AKS. If you
/// call `AppsKitSDKAdsManagerIOS.instance` directly from your own Swift code (§5), leave
/// this empty.
override func setupAdBridge() {}
override func isDevMode() -> Bool { return false } // wire to your own debug flag
override func isTestMode() -> Bool { return false }
override func defaultConfigJson() -> String? {
return "<encrypted-config-json-from-aks-portal>"
}
/// Called after dev/test mode flags are applied — use this for anything that needs
/// to happen after the base setup but before remote config is ready (e.g. initializing
/// AppLovin MAX directly).
override func configureSDK() {}
override func onConfigsReadyToUse() {
// optional — fires once Firebase Remote Config has been fetched/activated
}
}القائمة الكاملة لنقاط التخصيص المعلنة بالكلمة open في AKSBaseAppDelegate: setupAdBridge()، isDevMode() -> Bool (القيمة الافتراضية false)، isTestMode() -> Bool (القيمة الافتراضية false)، defaultConfigJson() -> String? (القيمة الافتراضية nil)، timeToFetchRemoteConfigInSeconds() -> Int (القيمة الافتراضية 3600)، onConfigsReadyToUse()، configureSDK().
4.2 التهيئة دون الوراثة من AKSBaseAppDelegate
الوراثة من AKSBaseAppDelegate ليست إلزامية — فهو مجرد غلاف لتسهيل التكامل. إذا كان تطبيقك يملك بالفعل تنفيذًا خاصًا لـ UIApplicationDelegate لا يمكنك الاستغناء عنه، فاستدعِ أساليب AppsKitSDK/AKSKit نفسها التي يستدعيها داخليًا، مباشرةً من تنفيذك الخاص لـ application(_:didFinishLaunchingWithOptions:):
import UIKit
import AppsKit // for makeDefaultIOSAdHandler() — see below
import AKSKit
import FirebaseCore
import FirebaseRemoteConfig
import GoogleMobileAds
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
MobileAds.shared.start()
// If your app has its own ad-loading abstraction layer to wire up to AKS, do it
// around here — most apps don't need this; see §5 to call ad formats directly.
let sdkInstance = AppsKitSDK.shared
sdkInstance.setIOSAdHandler(iosAdHandler: makeDefaultIOSAdHandler())
sdkInstance.initialize(
context: PlatformContext(delegate: nil),
application: PlatformApplication(application: UIApplication.shared)
)
sdkInstance.setDevMode(value: false) // wire to your own debug flag
sdkInstance.setTestMode(value: false)
sdkInstance.setTimeToFetchRemoteConfigInSeconds(intervalToFetchRemoteConfigInSeconds: 3600) // optional, defaults to 3600
sdkInstance.saveDefaultConfigJson(encryptedJson: "<encrypted-config-json-from-aks-portal>")
sdkInstance.manageRemoteConfigs(onRemoteConfigReady: RemoteConfigReadyBridge {
// optional — your own hook into the fetched remote config
})
AdsManager.shared.setAppExitTime(appExitTime: 0)
AKSUtils.shared.cleanCachedData()
return true
}
}
// manageRemoteConfigs(onRemoteConfigReady:) takes an OnRemoteConfigAvailable, not a closure —
// AKSBaseAppDelegate has a private adapter for this internally; outside it, write your own:
private class RemoteConfigReadyBridge: NSObject, OnRemoteConfigAvailable {
private let onReady: () -> Void
init(_ onReady: @escaping () -> Void) { self.onReady = onReady }
func onRemoteConfigReadyToFetch(remoteConfig: RemoteConfig) {
DispatchQueue.main.async { self.onReady() }
}
}يتطلب setIOSAdHandler(iosAdHandler:) تنفيذًا فعليًا لـ IOSPlatformAdHandler — والتنفيذ الذي يستخدمه AKSBaseAppDelegate داخليًا (لتهيئة وساطة AdMob/IronSource/MAX وإرسال التقارير إلى AppsFlyer/Adjust) هو نوع داخلي في SDK، لذا أُتيح لهذه الحالة تحديدًا عبر makeDefaultIOSAdHandler().
ما تفقده عند عدم الوراثة من AKSBaseAppDelegate: التدفق التلقائي لـ «إعلان فتح التطبيق على مستوى التطبيق» (§4.6) — فالأساليب requestSplashAppOpenAd/disableAppOpenSplashCallback/showAppOpenAdIfAvailable هي أساليب مثيل تابعة لـ AKSBaseAppDelegate نفسه، ولا تتوفر على كائن AppsKitSDK وحده. كما أن تتبّع الانتقال بين الواجهة الأمامية والخلفية الذي يشغّلها موجود في عمليات إعادة تعريف applicationWillEnterForeground/applicationDidBecomeActive/applicationWillResignActive داخل AKSBaseAppDelegate. إذا كنت لا تزال تريد عرض إعلان فتح التطبيق، فاستدعِ AppsKitSDKAdsManagerIOS.instance.loadAppOpen/showAppOpen بنفسك (§5، إعلانات فتح التطبيق) في الموضع الذي تختاره ضمن تدفق بدء تشغيل تطبيقك.
4.3 نقطة دخول التطبيق في SwiftUI
إذا كان تطبيقك يستخدم بروتوكول App في SwiftUI بدلًا من لوحة العمل (storyboard)، فاربطه بـ AppDelegate باستخدام @UIApplicationDelegateAdaptor — فهذا ما يؤدي فعليًا إلى تشغيل application(_:didFinishLaunchingWithOptions:):
import SwiftUI
@main
struct YourApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}يُنفَّذ تسلسل التهيئة الكامل الوارد في §4.1 بشكل متزامن داخل didFinishLaunchingWithOptions، قبل عرض WindowGroup — لذا يكون جسر الإعلانات وSDK جاهزين قبل ظهور أي واجهة مستخدم.
4.4 الموافقة وتهيئة الإعلانات
AppsKitSDKAdsManagerIOS.instance.initialize(userHasGivenConsent: true, isCCPAConsent: true)يضبط التكامل المرجعي كلا العَلَمين على القيمة الثابتة
true— ولا يتضمن أي ربط بتدفق موافقة UMP/CMP. هذا نقص في التكامل، وليس توصية: إذا كان تطبيقك خاضعًا لـ GDPR/CCPA، فنفّذ تدفق الموافقة الخاص بك (تتوفر UMP SDK من Google بالفعل كتبعية غير مباشرة عبرGoogle-Mobile-Ads-SDK، §1.2 — ما عليك سوى استدعائها) أو استخدم CMP مخصصة، ومرّر قرار المستخدم الفعلي إلى هذا الاستدعاء بدلًا من القيمة الثابتةtrue. استخدم ذلك مع مطالبة ATT الواردة في §3.3 إذا كنت تحتاج أيضًا إلى IDFA.
4.5 إزالة الإعلانات / التحكم حسب عمليات الشراء داخل التطبيق (IAP)
AppsKitSDK.shared.setRemoveAdsStatus(status: true) // call after a successful purchase/restore
AppsKitSDK.shared.getRemoveAdsStatus() // check current statusيتحقق كل استدعاء تحميل/عرض من هذه الحالة داخليًا بالفعل — فلا تضف شروط تحقق بنفسك. لاحظ نمط الوصول في Swift: تظهر جميع أنواع المديرين هذه كأصناف تتيح الوصول عبر الخاصية الثابتة .shared (AppsKitSDK.shared وAKSLogManager.shared وPreferencesManager.shared وغيرها) — وستجد هذا النمط في مختلف أجزاء SDK.
4.6 إعلانات فتح التطبيق على مستوى التطبيق
يتتبّع AKSBaseAppDelegate تلقائيًا الانتقال بين الواجهة الأمامية والخلفية (applicationWillEnterForeground/applicationDidBecomeActive/applicationWillResignActive)، ويعرض إعلان فتح التطبيق عند استئناف التطبيق إذا كان isAppOpenAtApplicationLvl مفعّلًا في إعدادات بوابة AKS. تتوفر ثلاثة أساليب لتدفق شاشة البداية:
// Call from your splash screen — requests and shows an app-open ad
(appDelegate as? AKSBaseAppDelegate)?.requestSplashAppOpenAd(callbacks: myCallbacks, waitTimeInSeconds: 5)
// Stop the splash screen's callback from firing again on subsequent app-open events
(appDelegate as? AKSBaseAppDelegate)?.disableAppOpenSplashCallback()
// Manually trigger "show if one's already loaded and ready" — used internally on resume
(appDelegate as? AKSBaseAppDelegate)?.showAppOpenAdIfAvailable()