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()