Repository navigation
feat(app-events): add Meta App Events and Objective-C facades (SDK 18.0.2) - #7
Merged
Merged
Conversation
…ive-C facades Meta now builds the Facebook iOS SDK binaries with Xcode releases newer than the ones available on CI runners, and a Swift facade can only import the SDK's Swift interfaces with the exact Swift compiler Meta used. - Port the Login and Share facades from Swift to Objective-C. They call the SDK through its Objective-C headers (including the generated -Swift.h headers), keep the same C entry points and keep the same behaviour. - Replace the SwiftPM dependency with release archives pinned by URL and SHA-256 in DependencyLocks/iOS/lockstate.txt (still SDK 18.0.2). The hashes equal the upstream Package.swift remoteChecksum values. - Build each facade with clang + libtool per slice, then wrap it in the same kfb/kfbshare xcframeworks as before. - Port the Share source invariants to the Objective-C implementation. - Cache the pinned archives in CI instead of the SwiftPM scratch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NativeFacebookShare binds its entry points with static __Internal imports, so the .NET iOS build passes them to the native linker as required symbols for every app. A Login-only build (the default selection) therefore failed to link with "Undefined symbols: _kfb_facebook_share_*" since 1.1.0. When Share is not selected, inject kfbsharestub.xcframework instead of kfbshare.xcframework. It defines the same three entry points on top of Foundation only: configuring logs an error, sharing completes with Failed/share_feature_not_selected and URL handling returns false. Share selections are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add kfbappevents.xcframework, an Objective-C facade over FBSDKAppEvents, FBSDKSettings and FBSDKApplicationDelegate exported as kfb_appevents_* C symbols: - SDK lifecycle: initialize, finished-launching hook, URL and user-activity forwarding (AEM / deep links), SDK version. - Settings: auto-log app events, advertiser ID collection, advertiser tracking (ignored by the SDK on iOS 17+), SKAdNetwork reporting, data processing options (LDU) and app-events debug logging. - Logging: activateApp, logEvent (optional valueToSum), logPurchase, flush and flush behaviour. Parameters travel as a JSON object of string or number values. - Identity: anonymous ID, user ID and advanced-matching user data. It only depends on FBSDKCoreKit, which every feature selection already links, so the package injects it for every selection: the managed App Events entry points will be static __Internal imports and must always resolve. The facade never changes a setting on its own. Values set through it are recorded and applied again after SDK initialization so the SDK persists them even when they were set before initialization. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Expose the App Events facade as Kapusch.Facebook.iOS.FacebookAppEvents, bound with static __Internal LibraryImport entry points: - InitializeSdk / FinishedLaunching, HandleOpenUrl and ContinueUserActivity forwarding for AEM and deep links, SdkVersion. - Set/Is pairs for automatic logging, advertiser ID collection, advertiser tracking (ignored by the SDK on iOS 17+) and SKAdNetwork reporting; SetDataProcessingOptions and SetDebugLoggingEnabled. - ActivateApp, LogEvent (parameters + optional valueToSum), LogPurchase, typed LogStartTrial / LogSubscribe / LogCompleteRegistration helpers, Flush and flush behaviour. - GetAnonymousId, Set/Clear/GetUserId and advanced-matching user data. Parameters accept strings, numbers and booleans (sent as "1"/"0") and are serialized with Utf8JsonWriter (trim and AOT safe). Add constants for every event name, parameter name, parameter value and user-data type declared by the bundled SDK headers, plus the Android-public activate/deactivate and _valueToSum names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Parse AppEvents in KapuschFacebookFeatures. App Events only needs FBSDKCoreKit, which every selection links, so its facade is always linked; selecting AppEvents alone produces a CoreKit-only build without LoginKit or ShareKit. The default selection stays Login. - Warn about unknown (or wrongly cased) feature names, which were silently ignored before. - Cover the AppEvents, Login;Share;AppEvents and default selections in the feature-selection test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NativeFacebookShare.ConfigureAndInitialize forces automatic app events logging off and advertiser ID collection / advertiser tracking to trackingAllowed. Once the app manages these settings through FacebookAppEvents (for example after a consent decision), Share must not silently override them. - The App Events facade records explicit values in the Objective-C class KFBAppEventsSettingsRegistry. Share and Login look it up through the Objective-C runtime, so there is no link-time dependency. - Share only applies its historical defaults to settings the app did not set explicitly. Apps that never call FacebookAppEvents keep the exact previous behaviour. - After Share or Login initializes the SDK, the recorded values are applied again so the SDK persists them (no-op when nothing was set). - Guard the behaviour in the Share source invariants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- scripts/validate-package.py replaces the inline layout check in CI and publish. On top of the layout (now including kfbsharestub and kfbappevents) it checks that both slices of each wrapper export every entry point of its header, that FacebookAppEvents binds every kfb_appevents_* symbol with a static __Internal import, that the managed constants cover every App Events name of the bundled SDK headers and that each constant value exists in the bundled FBSDKCoreKit binary. - scripts/verify-public.py fails on real-looking Meta app IDs, fb<appId> URL schemes, client tokens and email addresses in tracked files. Private terms are never tracked: they are read at run time from the KAPUSCH_PRIVATE_TERMS environment variable (a repository secret in CI) or the untracked .git/info/private-terms file, and failures only name the file and the term's position. The check is skipped with a notice when no term is configured. - Move FacebookDataProcessingOptions next to the other App Events types: it is a caller-supplied value, not an SDK constant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Integration.md: AppEvents feature selection; Info.plist switches with SDK defaults (FacebookAutoLogAppEventsEnabled, FacebookAdvertiserIDCollectionEnabled, FacebookSKAdNetworkReportEnabled) and the missing iOS AutoInit counterpart; a consent recipe (switches off in Info.plist, initialize after consent, manual ActivateApp, withdrawal steps); AEM URL / Universal Link forwarding; the double-counting rule for server-side purchase events; single SKAdNetwork owner; diagnostics and platform gaps. - README, NuGet readme, SourceMode and third-party notices: App Events, new xcframeworks, pinned archives and the CoreKit tracking manifest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Select Login;AppEvents in the sample (overridable with KapuschFacebookFeatures) so it links the App Events facade explicitly. - Ship the App Events switches off in Info.template.plist and add buttons for the consent recipe of Docs/Integration.md: grant consent (initialize, enable advertiser ID collection and debug logging, manual activation), log StartTrial and a custom event, flush and withdraw consent. Log activations from OnActivated after consent and forward Universal Links. - Without a FacebookAppID in the local, gitignored Info.plist the App Events buttons only print a message. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NativeFacebookLogin resolved kfb_facebook_* with a run-time lookup in the
main program. Nothing referenced those symbols at link time, so the native
linker dead-stripped them: Initialize and HandleOpenUrl silently did
nothing and SignInAsync could not reach the SDK. A Login;Share build of
master showed only the three Share symbols in the app executable.
- Bind the four Login entry points with [LibraryImport("__Internal")], like
Share and App Events. The public API and its best-effort semantics are
unchanged.
- When Login is not selected, link kfbloginstub.xcframework (Foundation
only), which defines the same entry points: SignInAsync completes with
Failed/login_feature_not_selected.
- Validate the static imports and package the placeholder.
Verified with nm on simulator builds for Login;AppEvents, AppEvents and
Share: every kfb_* entry point is present in the executable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before FBSDKApplicationDelegate configured it, the Facebook iOS SDK silently drops logged events (only a log line), ignores advanced-matching user data, overwrites a user ID set earlier with the persisted one, and answers setting getters with defaults. Some of those defaults are cached for the whole process: reading IsSKAdNetworkReportEnabled before initialization pinned it to true whatever FacebookSKAdNetworkReportEnabled said, and IsAdvertiserTrackingEnabled could pin "unspecified" on iOS 15-16. - The App Events registry now records whether a wrapper of this package initialized the SDK. App Events, Login and Share report it right after application:didFinishLaunchingWithOptions: (KFBNotifyAppEventsSdkInitialized, which also re-applies the recorded settings as before). - New FacebookAppEvents.IsSdkInitialized (kfb_appevents_is_sdk_initialized). - The event methods, the identity methods and the setting getters throw InvalidOperationException until then, like Kapusch.Facebook.Android. Setters, data processing options, flush behaviour, debug logging, the anonymous ID and the SDK version keep working at any time. - HandleOpenUrl and ContinueUserActivity return false without forwarding before initialization: forwarding stores campaign IDs and feeds AEM, which must wait for consent. - Guard the Login and Share notifications in the source invariants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Android Kapusch.Facebook.Android 1.2.0-rc.1 already ships the shared App Events API. A few iOS signatures and encodings still differed: - InitializeSdk(dataProcessingOptions, dataProcessingCountry, dataProcessingState), like Android without its Context. The options are set before initialization, so they apply to its first requests, and again right after it. - SetDataProcessingOptions is now recorded like the boolean settings and re-applied after initialization. Before, options set before initialization stayed in memory only and were lost at the next launch. - FacebookAppEventsUserData.ExternalId. SetUserData now replaces every field, external ID included (set or cleared after the ten-field SDK setter, which keeps the stored one), so a null property clears it as on Android. SetUserDataValue stays available for single fields. - Event parameters: decimal and ulong values are sent as exact text and floats keep their shortest form (a float 0.1 was sent as 0.10000000149011612), as on Android. Unsupported values are reported against the public "parameters" argument. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Server override: with SDK 18.0.2 an automatic-logging value set for the app at Meta overrides FacebookAutoLogAppEventsEnabled and SetAutoLogAppEventsEnabled once the app configuration is cached (SDK 19 changed this). Tell integrators to turn it off at Meta too and document IsAutoLogAppEventsEnabled as the effective value. - Initialization: what the SDK does before it is initialized, which FacebookAppEvents calls now throw or return false, and which work. - Withdrawal: what stops at once (app logging, IDFA, user ID and user data also for queued events, flushing with ExplicitOnly) and what only stops later (server override, StoreKit observation, the initialized SDK, the non-persisted flush behaviour when Login or Share initialize the SDK). - Remove claims the 18.0.2 sources do not support: purchase amounts are not rounded to three decimals, the activation threshold is Meta's session timeout (60 s by default), the advertiser tracking flag is also ignored before iOS 17 once Meta's domain configuration enables ATT handling. - Document the event parameter encoding and an API parity table with Kapusch.Facebook.Android, with the intentional gaps: no auto-init on iOS, no SDK 19 metadata switches in 18.0.2, iOS-only forwarding, advertiser tracking, SKAdNetwork and single-field user data. - CoreKit links FBAEMKit for every feature; Login and Share privacy manifests declare tracking too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Withdrawal switches flushing to explicit only, so events queued before it stay on the device, and skips the identity calls when the SDK is not initialized (they now throw; nothing is sent in that case anyway). - Granting consent again restores automatic flushing and the log line shows IsSdkInitialized. - The README states that the sample initializes the SDK at launch for its Login button, unlike a consent-first app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kapusch.Facebook.iOS.targets bundles each SDK framework's PrivacyInfo.xcprivacy from hard-coded ios-arm64 and ios-arm64_x86_64-simulator slices. An SDK release that renames a slice (SDK 19 ships ios-arm64_arm64e) would make every consuming build fail on a missing BundleResource. validate-package.py now fails when a packaged SDK xcframework lacks one of those slices or its privacy manifest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Login dead-stripping bug fixed earlier in this branch was only visible in a built app: the package and its static libraries were fine. CI now builds the sample for the simulator (Login;AppEvents, so the Share placeholder is linked too) and scripts/verify-app-symbols.py checks that the executable contains every kfb_* entry point declared by the wrapper headers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…AEMKit App Events made CoreKit, and with it FBAEMKit, part of every feature selection; the README still described it as a Share-only dependency and did not name the bundled SDK release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1.2.0 adds App Events and keeps Facebook iOS SDK 18.0.2. It is first published as 1.2.0-rc.1 from a v1.2.0-rc.1 tag on master. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces #6. Same feature:
Kapusch.Facebook.iOS1.2.0 (first published as1.2.0-rc.1) adds Meta App Events (FacebookAppEvents) and moves the Login and Share facades from Swift to Objective-C, so the build no longer depends on the Swift compiler Meta used.Differences from #6:
arm64eslice handling.master. No commit contains the encoded private pattern that feat: App Events + Facebook iOS SDK 19.0.0 (1.2.0) #6'sscripts/verify-public.pycarried. The scanner now follows the shared approach of the Android repository:KAPUSCH_PRIVATE_TERMS(a repository secret in CI) or from the untracked.git/info/private-terms;private term #N.fix(login)are those fixes.Review findings and fixes
Checked against the Facebook iOS SDK 18.0.2 sources (
FBSDKAppEvents.m,Settings*.swift,ApplicationDelegate.swift,FBSDKUserDataStore.m,FBSDKTimeSpentData.m).Calls made before the SDK is initialized were silently lost (
fix(app-events): refuse calls…). BeforeFBSDKApplicationDelegateconfigures it, the SDK:IsSKAdNetworkReportEnabledbefore initialization pinned it totruewhatever the Info.plist said.Now:
IsSdkInitializedreads it.InvalidOperationExceptionuntil then, as Android does.HandleOpenUrlandContinueUserActivitystop forwarding until then. Forwarding stores campaign IDs and feeds AEM, so it must wait for consent.API alignment with
Kapusch.Facebook.Android1.2.0-rc.1 (feat(app-events): align…):InitializeSdk(dataProcessingOptions, dataProcessingCountry, dataProcessingState).FacebookAppEventsUserData.ExternalId.SetUserDatanow replaces every field, as on Android.decimalandulongparameter values are sent as exact text.floatvalues keep their shortest form. Before,0.1fwas sent as0.10000000149011612.Consent semantics were partly inaccurate (
docs(app-events): describe SDK 18.0.2…):FacebookAutoLogAppEventsEnabled/SetAutoLogAppEventsEnabledonce the app configuration is cached. SDK 19 changed this.The sample's withdrawal did not match the recipe (
fix(sample)): it now sets explicit-only flushing and skips the identity calls when the SDK is not initialized.Packaging guards (
ci:commits):validate-package.pychecks the SDK slices and privacy manifests that the targets reference. An SDK slice rename, like 19'sarm64e, would otherwise break every consuming build.scripts/verify-app-symbols.pychecks that the app keeps everykfb_*entry point. Only a built app showed the dead-stripping bug thatfix(login)fixes.Checked with no change needed:
activateAppandInitializeSdkhop to the main thread) and the flush-behaviour values.Validation (local, Xcode 26.3, .NET 10.0.203)
bash src/Kapusch.FacebookApisForiOSComponents/Native/iOS/build.sh: OK. Every commit of the branch builds natively and in managed code (-warnaserror).dotnet packandpython3 scripts/validate-package.py: OK.bash tests/validate-feature-selection.sh: OK.python3 scripts/verify-public.py: OK, with the private terms configured. The commit messages and patches of the branch are clean too, in plain, hex and base64 forms.Login;AppEvents,AppEventsandShare. All 37kfb_*entry points are present in each build.🤖 Generated with Claude Code