KOTLIN MULTIPLATFORM 路 END-TO-END EXAMPLES

ADict on every platform

Selektywne modu艂y, natywne adaptery i jawne integracje aplikacji dla Androida, Apple, JVM/Desktop oraz Web/Wasm.

Selective modules, native adapters and explicit application integrations for Android, Apple, JVM/Desktop and Web/Wasm.

Stable 3.0.0Next 3.0.1-SNAPSHOT

1. Selektywno艣膰 KMP1. KMP module selection

Do commonMain dodaj tylko potrzebne logiczne modu艂y. Gradle sam wybierze wariant Android, JVM, iOS lub Wasm. Nie wpisuj r臋cznie wsp贸艂rz臋dnych z sufiksem platformy.

Add only the required logical modules to commonMain. Gradle selects Android, JVM, iOS or Wasm variants automatically. Never declare platform-suffixed coordinates directly.

kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation("rip.nerd.adictlibrary:adict-core:3.0.0")
            implementation("rip.nerd.adictlibrary:adict-foundation:3.0.0")
            implementation("rip.nerd.adictlibrary:adict-billing:3.0.0")
            implementation("rip.nerd.adictlibrary:adict-ads-core:3.0.0")
            implementation("rip.nerd.adictlibrary:adict-ads-homeads:3.0.0")
        }
        androidMain.dependencies {
            // Only for ADict.init(), manifest discovery and legacy Android UI.
            implementation("rip.nerd.adictlibrary:adict-runtime:3.0.0")
        }
    }
}
Ka偶dy modu艂 KMP ma w艂asne metadane i nie pobiera ca艂ego adict-runtime. Pe艂na lista 16 logicznych artefakt贸w, funkcji i platform znajduje si臋 w katalogu instalacyjnym.Every KMP module has its own metadata and does not pull the complete adict-runtime. The installation catalog lists all 16 logical artifacts, their purpose and platforms.

2. Kod wsp贸lny i reklamy bez SDK2. Shared code and SDK-free ads

class AppFeatures(services: ADictPlatformServices) : AutoCloseable {
    private val foundation = ADictFoundation(services)
    val billing = foundation.services?.billing?.let(::ADictBilling)

    override fun close() = foundation.close()
}

val houseAds = HouseAdsGateway(
    campaigns = listOf(
        HouseAdCampaign(
            id = "upgrade",
            placements = setOf("home"),
            formats = setOf(AdFormat.BANNER),
            payload = "app://premium"
        )
    ),
    presenter = { ad -> showHouseCreative(ad.payload) }
)
val ads = ADictAdsEngine(listOf(houseAds, customNetworkGateway))

customNetworkGateway implementuje publiczny AdGateway. Bez 偶adnego gatewaya aplikacja ukrywa placement; biblioteka nie raportuje fikcyjnego wy艣wietlenia.

customNetworkGateway implements the public AdGateway. With no gateway, the application hides the placement; the library never reports a fake impression.

3. Android

val playBilling = AndroidPlayBillingGateway(applicationContext) { foregroundActivity }
val services = ProviderADictServices(
    platform = ADictPlatform.ANDROID,
    billing = playBilling,
    secureStorage = appEncryptedStorage,
    biometrics = appBiometricGateway,
    notifications = appNotificationGateway
)

foregroundActivity zwraca bie偶膮c膮 Activity lub null. Dow贸d zakupu Google Play musi trafi膰 do zaufanego backendu przed trwa艂ym nadaniem uprawnienia. Dla starszej fasady u偶yj ADictPrivacy.update(...), a potem ADict.init(application, BuildConfig.DEBUG); manifest discovery uruchomi tylko zainstalowane modu艂y.

foregroundActivity returns the current Activity or null. Send Google Play purchase evidence to a trusted backend before granting a durable entitlement. For the legacy facade, call ADictPrivacy.update(...) and then ADict.init(application, BuildConfig.DEBUG); manifest discovery starts installed modules only.

4. iOS / Apple

val native = AppleADictServices(
    AppleADictConfiguration(
        keychainService = "rip.nerd.app",
        billingEnabled = true,
        trackingConsentEnabled = true,
        notificationsEnabled = true
    )
)
val services = OverlayADictServices(
    overrides = ProviderADictServices(
        platform = ADictPlatform.APPLE,
        billing = appStoreBillingOverride,
        sharing = appSharePresenter
    ),
    defaults = native
)

AppleADictServices korzysta ze StoreKit, Keychain, LocalAuthentication, AVSpeechSynthesizer, UIKit, StoreKit Review i UserNotifications. Mo偶esz te偶 u偶y膰 pojedynczych fabryk: appleBillingGateway(), appleSecurity(...), appleTextToSpeech() i appleEngagement(...). Pomini臋ty override zachowuje implementacj臋 natywn膮.

AppleADictServices uses StoreKit, Keychain, LocalAuthentication, AVSpeechSynthesizer, UIKit, StoreKit Review and UserNotifications. Individual factories are also available: appleBillingGateway(), appleSecurity(...), appleTextToSpeech() and appleEngagement(...). An omitted override keeps the native implementation.

Kotlin/Native eksportuje API suspend do Swift jako wywo艂ania asynchroniczne. Weryfikacja zakupu nadal nale偶y do backendu.Kotlin/Native exports suspend APIs to Swift as asynchronous calls. Purchase verification still belongs on the backend.

5. JVM / Desktop

val billing = HostedCheckoutBillingGateway(
    products = catalog,
    checkout = CheckoutLauncher { productId ->
        Desktop.getDesktop().browse(URI("https://pay.example/checkout?product=$productId"))
        true
    },
    entitlements = EntitlementSource { api.loadVerifiedProductIds() }
)
val services = ProviderADictServices(ADictPlatform.JVM, billing = billing)

Otwarcie checkoutu zwraca PurchaseResult.Pending. Dopiero backend zasilany webhookiem operatora potwierdza dost臋p przez EntitlementSource.

Opening checkout returns PurchaseResult.Pending. Only the provider-webhook-backed server confirms access through EntitlementSource.

6. Web / Wasm

val webBilling = HostedCheckoutBillingGateway(
    products = catalog,
    checkout = CheckoutLauncher { productId -> openCheckoutWindow(productId) },
    entitlements = EntitlementSource { backend.verifiedEntitlements() }
)
val webServices = ProviderADictServices(
    platform = ADictPlatform.WEB,
    billing = webBilling,
    clipboard = browserClipboardGateway,
    sharing = browserShareGateway
)

CheckoutLauncher mo偶e otworzy膰 PayPal, Stripe lub inny checkout wybrany przez aplikacj臋. localStorage nie jest bezpiecznym magazynem. Odmowa przegl膮darki wy艂膮cza capability zamiast zwraca膰 sukces.

CheckoutLauncher may open PayPal, Stripe or another application-selected checkout. localStorage is not secure storage. Browser denial disables the capability instead of returning success.

7. W艂asny dostawca i bezpieczny fallback7. Custom provider and safe fallback

class PartnerAdsGateway(private val sdk: PartnerSdk) : AdGateway {
    override val provider = AdProvider(id = "partner", priority = 100)
    override suspend fun load(request: AdRequest): AdLoadResult = sdk.load(request)
    override suspend fun show(ad: LoadedAd): Boolean = sdk.show(ad)
}

val services = ProviderADictServices(
    platform = currentPlatform,
    billing = appBillingGateway,
    consent = appConsentGateway,
    secureStorage = appSecureStorage,
    textToSpeech = appSpeechEngine
)

Ten sam wzorzec obejmuje BillingGateway, SessionSink, DiagnosticProbe, TextToSpeechGateway i LocalNotificationGateway. Brak odpowiednika oznacza null i wy艂膮czon膮 funkcj臋 UI, nigdy atrap臋 sukcesu.

The same pattern covers BillingGateway, SessionSink, DiagnosticProbe, TextToSpeechGateway and LocalNotificationGateway. No equivalent means null and a disabled UI capability, never a fake success.

8. Macierz demonstracyjna8. Demo matrix

HostZakresCoverage
Android:app:assembleDebug: runtime, auto-discovery and selective modules
Android lite:smoke-consumer:assembleRelease -Plite=true: core, billing and house ads without runtime
JVMProvider bundle, billing, ads, sessions and diagnostics tests
iOSApple gateways, Swift API and three native framework variants
Web/WasmOpen providers and portable feature logic

Windows mo偶e kompilowa膰 targety iOS, lecz testy symulatora uruchamia runner macOS.

Windows can compile iOS targets, but simulator tests run on a macOS runner.