# اتصال iOS · Swift — نسخه ۱ ## مسیر کوتاه ۱. رسانه و جایگاه را در پنل بسازید و تأیید کنید. ۲. همین SDK را نصب کنید. ۳. شناسه UNT_ نمونه را با جایگاه همان فرمت جایگزین کنید. اپ شما به سرور، دامنه اختصاصی، JWT یا کلید API نیاز ندارد. نشانی HTTPS مثال، سرور خود دیدنکس است. پس از تغییر دامنه پلتفرم، نشانی جدید را در همان یک تنظیم جایگزین کنید. شناسه بسته واقعی اپ باید با رسانه تأییدشده برابر باشد. ## نصب و پیش‌نیاز پوشه ZIP را به Xcode به‌صورت Local Swift Package اضافه و محصول DidnexAds را به Target وصل کنید. import DidnexAds را در UIViewController بگذارید. روش جایگزین: فایل‌های DidnexAdView.swift، DidnexAudience.swift و DidnexIntegrity.swift را در Target کپی و import ماژول را از مثال حذف کنید. PrivacyInfo.xcprivacy را در Copy Bundle Resources بگذارید. iOS ≥15، Swift 5.9 و Xcode دارای iOS SDK. UI را روی MainActor فراخوانی کنید. برای تبلیغ پایه وابستگی شخص ثالث لازم نیست. ``` import UIKit import DidnexAds let ad=DidnexAdView(frame:container.bounds) container.addSubview(ad) ad.loadDirect(serviceOrigin:URL(string:"https://didnex.com")!,unitID:"UNT_BANNER") ``` ## Manifest و تنظیمات سیستم‌عامل فایل `sdk/ios/Examples/Integration.plist` کنار مثال است. قطعه‌ها را با تنظیمات موجود ادغام کنید. مجوز اینترنت در Android باید در manifest اصلی نسخه Release هم باشد. تبلیغ پایه به مجوز مکان، شناسه تبلیغاتی، دسترسی حافظه یا اعلان نیاز ندارد. ``` UIBackgroundModesremote-notification ``` برای Flutter/Unity در iOS، حداقل iOS 15 و PrivacyInfo.xcprivacy بسته را رعایت کنید. ATS را برای نشانی HTTPS باز نکنید. پوش پس‌زمینه تنها پس از رضایت و تنظیم سرویس سیستم‌عامل فعال می‌شود؛ Inbox به Firebase یا مجوز اعلان نیاز ندارد. ## فونت، ظاهر و کنترل سروری ظاهر کارت در هر دو روش مستقیم و پراکسی از پاسخ همان تبلیغ خوانده می‌شود. مدیر در تنظیمات ← ظاهر تبلیغ و SDK، رنگ، زمینه، فونت، گردی کارت، جای نشان و پخش خودکار ویدیوی معمولی را تعیین می‌کند. ابتدا یک‌بار SDK و اپ را به‌روزرسانی کنید؛ سپس تغییرات پشتیبانی‌شده در درخواست بعدی تبلیغ اعمال می‌شوند. فونت Noto Sans Arabic فارسی و لوگو از سرور خود دیدنکس کش می‌شوند؛ خطای این دارایی‌های اختیاری باعث noFill یا خطای تبلیغ نمی‌شود. Unity برای UGUI یک فونت کوچک فارسی همراه و شکل‌دهی حروف در مرز نمایش دارد. سرور کد اجرایی جدید به اپ نمی‌فرستد. فونت حدود ۲۴۵ کیبی‌بایت است و پس از کش برای هر تبلیغ دوباره دریافت نمی‌شود؛ پاک‌شدن کش می‌تواند دریافت دوباره را لازم کند. پخش، لمس و تشخیص دیدپذیری روی دستگاه باقی می‌مانند. توقف دستی، پس‌زمینه و پایان ویدیو رعایت می‌شوند. نشان شبکه و دکمه‌های صدا، توقف و بستن، کلیک تبلیغ‌دهنده ثبت نمی‌کنند. بدنه و دکمه مقصد امضاشده مشترک دارند. لمس بازی تعاملی به خود بازی می‌رسد. در وب مبدأ دیدنکس را در `font-src` سیاست CSP مجاز کنید. ## همه فرمت‌ها فرمت در پنل و روی جایگاه تعیین می‌شود؛ نوع متد را مطابق جدول انتخاب کنید. `playable` از مسیر تمام‌صفحه و `audio` از مسیر درون صفحه استفاده می‌کند. ویدیوی معمولی فقط هنگام فعال‌بودن اپ و حداقل ۵۰٪ دیده‌شدن، خودکار و بی‌صداست؛ صوت و جایزه نیازمند انتخاب کاربرند. محدودیت مرورگر ممکن است پخش دستی را لازم کند. | فرمت | شناسه قراردادی | روش نمایش | |---|---|---| | بنر | `banner` | درون صفحه | | بنر تطبیقی | `adaptive_banner` | درون صفحه | | همسان | `native` | درون صفحه | | ویدیوی همسان | `native_video` | درون صفحه | | ویدیوی درون محتوا | `video` | درون صفحه | | بینابینی | `interstitial` | تمام‌صفحه | | ویدیوی جایزه‌دار | `rewarded` | جایزه‌دار | | بینابینی جایزه‌دار | `rewarded_interstitial` | جایزه‌دار | | آغاز اپ | `app_open` | تمام‌صفحه | | بازی تعاملی | `playable` | تمام‌صفحه | | صوتی | `audio` | درون صفحه | | کاروسلی | `carousel` | درون صفحه | | نظرسنجی | `poll` | درون صفحه | ### بنر ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("banner","UNT_BANNER")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### بنر تطبیقی ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("adaptive_banner","UNT_ADAPTIVE_BANNER")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### همسان ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("native","UNT_NATIVE")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### ویدیوی همسان ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("native_video","UNT_NATIVE_VIDEO")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### ویدیوی درون محتوا ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("video","UNT_VIDEO")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### بینابینی ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("interstitial","UNT_INTERSTITIAL")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### ویدیوی جایزه‌دار ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("rewarded","UNT_REWARDED")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### بینابینی جایزه‌دار ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("rewarded_interstitial","UNT_REWARDED_INTERSTITIAL")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### آغاز اپ ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("app_open","UNT_APP_OPEN")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### بازی تعاملی ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("playable","UNT_PLAYABLE")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### صوتی ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("audio","UNT_AUDIO")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### کاروسلی ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("carousel","UNT_CAROUSEL")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### نظرسنجی ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("poll","UNT_POLL")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · بنر برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("banner","UNT_BANNER")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · بنر تطبیقی برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("adaptive_banner","UNT_ADAPTIVE_BANNER")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · همسان برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("native","UNT_NATIVE")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · ویدیوی همسان برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("native_video","UNT_NATIVE_VIDEO")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · ویدیوی درون محتوا برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("video","UNT_VIDEO")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · بینابینی برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("interstitial","UNT_INTERSTITIAL")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · ویدیوی جایزه‌دار برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("rewarded","UNT_REWARDED")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · بینابینی جایزه‌دار برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("rewarded_interstitial","UNT_REWARDED_INTERSTITIAL")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · آغاز اپ برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("app_open","UNT_APP_OPEN")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · بازی تعاملی برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("playable","UNT_PLAYABLE")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · صوتی برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("audio","UNT_AUDIO")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · کاروسلی برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("carousel","UNT_CAROUSEL")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ### سرور اختیاری · نظرسنجی برای این روش، پراکسی خودتان و callback نشست فعلی اپ را تکمیل کنید؛ تابع خالی نمونه اتصال واقعی ایجاد نمی‌کند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let diagnosticToken:String? = "".isEmpty ? nil : "" private let proxy=URL(string:"https://YOUR-BACKEND.example/ads")! // Replace with YOUR current authenticated app session. private func appSessionToken()->String { "" } private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[("poll","UNT_POLL")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewarded(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitial(from:self,proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.load(proxyURL:proxy,appAccessToken:appSessionToken(),unitID:unit,diagnosticToken:diagnosticToken)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ## نمونه کامل قابل استفاده فایل `sdk/ios/Examples/DidnexViewController.swift` در همین بسته قرار دارد. نام package/namespace مثال متعلق به اپ نمونه است؛ آن را با پروژه میزبان هماهنگ کنید. شناسه‌های UNT_ جایگزین‌اند و برای دریافت تبلیغ واقعی باید عوض شوند. ``` import UIKit import DidnexAds // Omit this import only when copying all SDK Swift files into the app target. @MainActor final class DidnexViewController: UIViewController { private let origin=URL(string:"https://didnex.com")! private let units:[(String,String)]=[ ("banner","UNT_BANNER"),("adaptive_banner","UNT_ADAPTIVE_BANNER"), ("native","UNT_NATIVE"),("native_video","UNT_NATIVE_VIDEO"),("video","UNT_VIDEO"), ("interstitial","UNT_INTERSTITIAL"),("rewarded","UNT_REWARDED"), ("rewarded_interstitial","UNT_REWARDED_INTERSTITIAL"),("app_open","UNT_APP_OPEN"), ("playable","UNT_PLAYABLE"),("audio","UNT_AUDIO"),("carousel","UNT_CAROUSEL"),("poll","UNT_POLL")] private let stack=UIStackView(), slot=UIView(), status=UILabel() private var inline:DidnexAdView?, full:DidnexAdView? override func viewDidLoad(){ super.viewDidLoad();view.backgroundColor = .systemBackground let scroll=UIScrollView();scroll.translatesAutoresizingMaskIntoConstraints=false;view.addSubview(scroll) stack.axis = .vertical;stack.spacing=8;stack.translatesAutoresizingMaskIntoConstraints=false;scroll.addSubview(stack) NSLayoutConstraint.activate([ scroll.topAnchor.constraint(equalTo:view.safeAreaLayoutGuide.topAnchor),scroll.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor), scroll.leadingAnchor.constraint(equalTo:view.leadingAnchor),scroll.trailingAnchor.constraint(equalTo:view.trailingAnchor), stack.topAnchor.constraint(equalTo:scroll.contentLayoutGuide.topAnchor),stack.bottomAnchor.constraint(equalTo:scroll.contentLayoutGuide.bottomAnchor), stack.leadingAnchor.constraint(equalTo:scroll.contentLayoutGuide.leadingAnchor),stack.trailingAnchor.constraint(equalTo:scroll.contentLayoutGuide.trailingAnchor), stack.widthAnchor.constraint(equalTo:scroll.frameLayoutGuide.widthAnchor)]) status.numberOfLines=0;stack.addArrangedSubview(status) for (format,unit) in units { let b=UIButton(type:.system);b.setTitle(format,for:.normal);b.addAction(UIAction{[weak self] _ in self?.show(format,unit:unit)},for:.touchUpInside);stack.addArrangedSubview(b) } slot.heightAnchor.constraint(equalToConstant:360).isActive=true;stack.addArrangedSubview(slot) } private func show(_ format:String,unit:String){ let ad=DidnexAdView();ad.onLoaded={[weak self] _ in self?.status.text="تبلیغ آماده شد"} ad.onNoFill={[weak self] _ in self?.slot.isHidden=true;self?.status.text="فعلاً تبلیغی موجود نیست"} ad.onError={[weak self] _ in self?.slot.isHidden=true;self?.status.text="تبلیغ در دسترس نیست؛ استفاده از اپ ادامه دارد"} if ["interstitial","app_open","playable","rewarded","rewarded_interstitial"].contains(format){ guard presentedViewController==nil else{return};full?.destroy();full=ad if format.hasPrefix("rewarded") {ad.showRewardedDirect(from:self,serviceOrigin:origin,unitID:unit){[weak self] _ in self?.status.text="جایزه در کیف نصب دیدنکس ثبت شد" // Do not credit twice. }}else{ad.showInterstitialDirect(from:self,serviceOrigin:origin,unitID:unit)} }else{slot.isHidden=false;inline?.destroy();inline=ad;slot.addSubview(ad);ad.frame=slot.bounds;ad.autoresizingMask=[.flexibleWidth,.flexibleHeight];ad.loadDirect(serviceOrigin:origin,unitID:unit)} } override func viewDidDisappear(_ animated:Bool){super.viewDidDisappear(animated);if isMovingFromParent || isBeingDismissed {inline?.destroy();full?.destroy()}} } ``` ## اعتبارسنجی اختیاری اپ برای Flutter فقط در صورت استفاده از Play Integrity، در android/gradle.properties مقدار `didnex.enablePlayIntegrity=true` و در client گزینه enableIntegrity را فعال کنید. اتصال پایه بدون این وابستگی کار می‌کند. برای Android بومی، DidnexIntegrity.kt و وابستگی Google Play را اضافه کنید؛ provider باید با سیاست پنل هماهنگ باشد. این قابلیت برای توزیع خارج Google Play الزام عمومی نیست. ## خطا، چرخه عمر و جایزه نداشتن تبلیغ، قطع اینترنت یا خطای سرور باید فقط فضای تبلیغ را جمع کند؛ شروع اپ را به تبلیغ وابسته نکنید. بی‌وقفه retry نکنید؛ درخواست بعدی را به فرصت طبیعی بعدی اپ موکول کنید. برای تمام‌صفحه یک نمونه تازه، صفحه قابل نمایش و اقدام مناسب کاربر لازم است؛ از resume برای نمایش خودکار استفاده نکنید. تست اتصال را با diagnosticToken از پنل انجام دهید. رسانهٔ تست متناسب با فرمت بارگذاری می‌شود؛ هزینه، درآمد یا جایزه ندارد. بارگذاری گزارش‌شده تأیید کامپایل یا پخش روی همهٔ دستگاه‌ها نیست. برای آزمون رسانه و درآمد، کمپین و رسانه تأییدشده و بودجه واقعی لازم است. هیچ جایزه‌ای با صرف loaded، click یا نمایش ناقص ایجاد نکنید. فقط رسید `verified=true` و `applied=true` از سمت سرور معتبر است؛ SDK جایزه را پیش‌تر در کیف نصب ثبت کرده، دوباره اعتبار ندهید. برای مصرف، externalId را پیش از ارسال ذخیره کنید و در retry عوض نکنید. ## کیف، پوش و سرور اختیاری - کیف نصب و محدودیت بازیابی: https://didnex.com/docs/publisher/wallet - پوش اختصاصی همین پلتفرم، Manifest، FCM/APNs و نمونه پس‌زمینه: https://didnex.com/docs/publisher/push-ios - روش سروری برای اتصال جایزه به حساب موجود اپ: https://didnex.com/docs/publisher/server همه ۱۳ فرمت در اتصال مستقیم و پراکسی اختیاری موجودند. پراکسی فقط روی سرور خودتان کلید خصوصی md_ را نگه می‌دارد. هیچ JWT یا کلید را در فایل نمونه یا پنل کپی کد ثابت نکنید. برای APIهای کیف، رضایت و پوش، شکست واقعی را کنترل کنید؛ موجودی یا موفقیت جعلی نشان ندهید. ## تشخیص خطا | وضعیت | بررسی | |---|---| | noFill | رسانه و جایگاه فعال، کمپین سازگار، بودجه، زمان و فیلتر دستگاه/لوکیشن | | package mismatch | شناسه واقعی خروجی اپ و رسانه ثبت‌شده؛ debug flavor ممکن است پسوند داشته باشد | | 403 در وب | Origin دقیق سایت تأییدشده و HTTPS | | شبکه/مهلت | دسترسی دامنه دیدنکس از دستگاه، INTERNET نسخه Release و تنظیم TLS | | تصویر تست فقط | diagnosticToken را پس از پایان تست بردارید؛ کارت تست کمپین زنده نیست | | جایزه ثبت نشد | رضایت، تماشای کامل، ترتیب رویدادها و رسید سرور؛ موجودی را محلی تغییر ندهید | | پوش دیده نشد | ابتدا Inbox را بررسی کنید؛ سپس رضایت، توکن تازه، تنظیم provider و مجوز سیستم‌عامل | ## وضعیت آزمون آزمون‌های قرارداد سرور، وب و بسته‌بندی در مخزن اجرا می‌شوند. وضعیت کامپایل منطبق با هش منبع در SDK Lab و releases.json ثبت می‌شود؛ اجرای دستگاه فقط با رسید مستقل device_playback قابل تأیید است. قبل از انتشار اپ: ساخت Release، اینترنت قطع، DNS نامعتبر، پاسخ ۵۰۰/JSON خراب، رسانه خراب، خروج صفحه حین درخواست، pause/resume و تکرار claim را بررسی کنید. فایل‌های tool/verify یا scripts/sdk-build.mjs مسیر تکرار آزمون را مشخص می‌کنند. هیچ انتشار pub.dev/Maven عمومی در این بسته ادعا نمی‌شود. ## تبلیغات و پوش در یک بسته کنترلر پوش از همان کلاینت تبلیغات ساخته می‌شود؛ دانلود یا نصب بسته دوم لازم نیست. راهنمای `PUSH.md` داخل همین بسته شامل نمونه اتصال مشترک، رضایت، دریافت، گزارش و چرخه عمر است. روش‌های قدیمی Initialize همچنان سازگارند؛ فایل‌های SDK را از دو ZIP هم‌زمان به پروژه کپی نکنید. ## شناسه جایگاه چیست و از کجا بگیرم؟ پوش در دیدنکس از **شناسه عمومی یکی از جایگاه‌های فعال همان رسانه** برای پیدا کردن اپ یا سایت شما استفاده می‌کند. پیشوند واقعی این شناسه `UNT_` است. این مقدار نام اپ، شناسه بسته، شناسه رسانه با پیشوند `AST_`، توکن FCM یا کلید API نیست و محرمانه نیست. در پنل ناشر، «جایگاه‌های تبلیغ» را باز کنید؛ شناسه زیر نام جایگاه دیده می‌شود. همان شناسه کامل را کپی کنید. راه ساده‌تر: در «پوش ← اتصال رسانه‌ها»، برای رسانه خود «کد پوش» را بزنید و پلتفرم را انتخاب کنید؛ کد با شناسه واقعی شما ساخته می‌شود و نیازی به حدس یا جایگزینی دستی ندارد. در مثال عمومی زیر، `placementId` یا `PlacementId` یا `PLACEMENT_ID` متغیری برای نگهداری همین شناسه است. متن `PASTE_YOUR_PLACEMENT_ID_HERE` فقط محل جایگزینی است و شناسه قابل استفاده نیست. آن را با شناسه کامل جایگاه خودتان که با `UNT_` شروع می‌شود عوض کنید. یک جایگاه فعال کافی است؛ جایگاه مخصوص پوش و نمایش تبلیغ برای عضوکردن مخاطب لازم نیست. رسانه نیز باید فعال و تأییدشده باشد و پوش آن در پنل فعال شده باشد. | مقدار | محل دریافت / کاربرد | | --- | --- | | شناسه جایگاه (`UNT_…`) | پنل ناشر ← جایگاه‌های تبلیغ؛ ورودی ایجاد کنترلر پوش | | شناسه بسته | تنظیمات پروژه اپ؛ باید با رسانه اپ ثبت‌شده یکسان باشد | | دامنه دیدنکس | نشانی همین سرویس؛ سرور اختصاصی ناشر لازم نیست | | کلید خصوصی API | داخل اپ یا مرورگر قرار نمی‌گیرد | اگر `inactive_inventory` دیدید، فعال و تأییدبودن رسانه و فعال‌بودن جایگاه همان رسانه را بررسی کنید. کپی‌کردن شناسه از یک حساب یا رسانه دیگر اتصال را درست نمی‌کند. ## اتصال ساده با شناسه روشن — ۸.۱۷ یک کلاینت را در طول عمر اپ نگه دارید و همان را برای تبلیغ و پوش به کار ببرید. این فراخوانی فقط تنظیم محلی است؛ موفقیت اتصال شبکه با دریافت تبلیغ یا عضویت موفق مشخص می‌شود. init خودکار اجازه اعلان نمی‌گیرد و هیچ پیام واقعی ارسال نمی‌کند. برای تغییر دامنه، origin همان یک تنظیم را تغییر دهید. شناسه عمومی جایگاه (`UNT_…`) باید متعلق به رسانه فعال و تأییدشده خودتان باشد؛ کلید خصوصی وارد اپ نکنید. ``` let placementId = "PASTE_YOUR_PLACEMENT_ID_HERE" let client = try Didnex.initialize(origin: URL(string:"https://didnex.com")!) let ad = DidnexAdView() container.addSubview(ad) client.load(view: ad, unitID: placementId) // Set the view constraints in the host container. // Fullscreen: client.showInterstitial(view: ad, from: self, unitID: unit) // Push: client.createPush(unitID: placementId, display: { _ in false }, onError: { _ in }) ``` این قطعه، الگوی راه‌اندازی است؛ مثال کامل همه فرمت‌ها، مدیریت خطا و چرخه عمر در ادامه همین راهنما و پوشه Examples بسته قرار دارد. در Flutter کد بالا داخل `Future main() async` قرار می‌گیرد؛ `MyApp` ویجت واقعی اپ شماست. در B4A فایل جدید `Didnex.bas` هم به‌عنوان Class اضافه شود. برای دامنه اختصاصی B4A، initializer اصلی `DidnexAds.Initialize(Me,"Ads",origin,Application.PackageName)` همچنان معتبر است. روش اختیاری پراکسی همان قرارداد نمایش را دارد و برای اتصال مستقیم هیچ سرور ناشری لازم نیست. ### قرارداد اندازه `slot.version=1` و `slot.profile` از جایگاه می‌آیند. نسبت یا ارتفاع فایلِ دانلودشده اندازه بیرونی را تغییر نمی‌دهد. تصویر و ویدیو با contain داخل کادر قرار می‌گیرند؛ برای `noFill` و خطا کادر جمع می‌شود. پس از یک‌بار به‌روزرسانی SDK، تعویض فایل هم‌اندازه یا شروع پخش ویدیو باعث تغییر چیدمان نمی‌شود. برای پروفایل غیراستانداردِ پیش‌فرض، متادیتای جایگاه و اندازه میزبان را مطابق همان پروفایل تنظیم کنید؛ در Unity/Godot مستطیل میزبان تحت کنترل اپ است. | پروفایل | نسبت رسانه / اندازه مرجع | نوار ثابت زیر رسانه | |---|---|---| | بنر کوچک / بزرگ | ۳۲۰×۵۰ / ۳۲۰×۱۰۰ | ندارد | | بنر مستطیل / عریض | ۳۰۰×۲۵۰ / ۷۲۸×۹۰ | ندارد | | تطبیقی | نسبت ۳۲۰×۵۰ با عرض میزبان | ندارد | | همسان تصویری | ۱۶:۹ یا مربع | ۸۴ پیکسل منطقی | | همسان ویدیویی / کاروسل | ۱۶:۹ | ۸۴ پیکسل منطقی | | ویدیوی درون محتوا | ۱۶:۹ | ندارد | | صوتی | کادر ۹۶ پیکسل منطقی | ندارد | | نظرسنجی | کادر مرجع ۳۲۰×۳۰۰ | ۸۴ پیکسل منطقی | | بینابینی، جایزه، آغاز اپ و بازی | میزبان تمام‌صفحه با حاشیه امن | قالب ثابت میزبان | این‌ها پروفایل محصول دیدنکس‌اند؛ ادعای گواهی IAB یا ابعاد پیکسلی واحد جهانی برای تمام فرمت‌ها ندارند. فایل می‌تواند چند برابر اندازه مرجع باشد. تصویر کوچکِ خارج از حداقل یا نسبت نامطابق پیش از ثبت کمپین رد می‌شود؛ حداقل و نسبت دقیق در فرم کمپین و `GET /api/display-profiles` قابل دریافت‌اند. برای ویدیوی افقی حداقل ۳۲۰×۱۸۰ و پیشنهاد ۶۴۰×۳۶۰ است. فایل‌های کمپین قدیمی بدون پروفایل با حفظ نسبت داخل کادر ثابت قرار می‌گیرند. برای اعمال renderer جدید در SDK نصب‌شده، اپ باید یک بار به‌روزرسانی شود؛ تغییر سرور به‌تنهایی کد قدیمی را عوض نمی‌کند. ### حدود آزمون این نسخه آزمون قرارداد سرور، رسیدها، noFill، فایل خراب، قطع ارتباط و یکپارچگی بسته‌ها در مخزن وجود دارد. ساخت بومی با هش همین سورس فقط وقتی در `releases.json` مقدار `nativeBuild=passed` دارد تأیید شده است. نبود چنین رسیدی یعنی کامپایل بومی/پخش روی دستگاه تأیید نشده؛ موفقیت تست جاوااسکریپت جای آن را نمی‌گیرد. AARهای قدیمی داخل پوشه سازگاری را معادل ساخت جدید تلقی نکنید؛ برای Android و Godot از پروژه سورس و gradlew استفاده کنید.