> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encorekit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Objective-C SDK

> Integrate Encore into Objective-C iOS apps via the EncoreObjC overlay pod.

## Overview

`EncoreObjC` is a thin `@objc` overlay over the Swift [EncoreKit](https://github.com/EncoreKit/ios-sdk) SDK, distributed as a source pod on CocoaPods. Every method is a translation shim — all business logic lives in EncoreKit — so the Obj-C surface mirrors the Swift API 1:1 semantically. Use this page to install the bridge, wire it up in an Obj-C AppDelegate, and map each method back to the Swift docs for full semantics.

### When to Use

<CardGroup cols={2}>
  <Card title="Use EncoreObjC if" icon="check">
    * Pure Objective-C codebases
    * Legacy iOS apps that haven't adopted Swift
    * Teams standardized on CocoaPods
    * Mixed codebases where `.m` files need direct access
  </Card>

  <Card title="Use EncoreKit (Swift) if" icon="info-circle">
    * Pure Swift or SwiftUI codebases
    * Mixed codebases where Swift files drive integration
    * You want `async`/`await` and typed errors
    * You prefer Swift Package Manager
  </Card>
</CardGroup>

<Info>
  Both pods point at the same singleton. In mixed codebases, Swift files should `import Encore` and call the typed Swift API — only `.m` files need `@import EncoreObjC`.
</Info>

***

## Install

<Steps>
  <Step title="Add EncoreObjC to your Podfile">
    Target iOS 15.0 or later and enable `use_frameworks!`. Install directly from the tagged Git release:

    ```ruby Podfile theme={null}
    platform :ios, '15.0'
    use_frameworks!

    target 'MyApp' do
      pod 'EncoreObjC', :git => 'https://github.com/EncoreKit/objc.git', :tag => 'v0.1.0'
    end
    ```

    The standard trunk form — `pod 'EncoreObjC', '~> 0.1'` — is also available and is the recommended steady-state install once CocoaPods' CDN shard index has refreshed for this release.

    <Info>
      The Git-ref form resolves by cloning the public repo at a specific tag, so it works immediately after a release — independent of CocoaPods shard-index propagation (which can lag 15–60 min after `pod trunk push`). The `'~> 0.1'` trunk form is canonical once the CDN catches up and is preferred for consumers who want automatic patch bumps.
    </Info>
  </Step>

  <Step title="Install the pod">
    From the directory containing your `Podfile`:

    ```bash theme={null}
    pod install --repo-update
    ```
  </Step>

  <Step title="Open the workspace">
    From now on, open `MyApp.xcworkspace` (not `.xcodeproj`). Build to confirm the pod linked cleanly.

    ```objc theme={null}
    @import EncoreObjC;
    ```

    <Check>
      If `@import EncoreObjC;` resolves in Xcode, you're ready to configure.
    </Check>
  </Step>
</Steps>

***

## Quickstart

Configure the SDK at launch, identify the current user, register a purchase handler, and present a placement. The full flow fits in one `AppDelegate.m`:

```objc AppDelegate.m theme={null}
#import "AppDelegate.h"
@import EncoreObjC;

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // 1. Configure
    EncoreOptions *options = [[EncoreOptions alloc] init];
    options.logLevel = EncoreLogLevelInfo;
    [[EncoreClient shared] configureWithApiKey:@"pk_live_your_key_here"
                                       options:options];

    // 2. Identify the user
    EncoreUserAttributes *attrs = [[EncoreUserAttributes alloc] init];
    attrs.email = @"user@example.com";
    attrs.subscriptionTier = @"free";
    [[EncoreClient shared] identifyWithUserId:@"user_123" attributes:attrs];

    // 3. Register the purchase handler
    [[EncoreClient shared] onPurchaseRequest:^(EncorePurchaseRequest *request,
                                               void (^done)(NSError *error)) {
        // Kick off your StoreKit / RevenueCat / Adapty purchase here.
        // Call done(nil) on success, or done(error) on failure.
        NSLog(@"Purchase requested for productId=%@", request.productId);
        done(nil);
    }];

    // 4. Handle dismissal / no-offer passthrough
    [[EncoreClient shared] onPassthrough:^(NSString *placementId) {
        NSLog(@"Passthrough for %@", placementId);
    }];

    return YES;
}

@end
```

Present a placement anywhere in your app:

```objc ViewController.m theme={null}
- (void)presentPaywall {
    [[[EncoreClient shared] placement:@"paywall"]
        showWithCompletion:^(EncorePresentationResult *result, NSError *error) {
            if (error) {
                NSLog(@"Show failed: %@", error.localizedDescription);
                return;
            }
            if (result.kind == EncorePresentationKindGranted) {
                NSLog(@"Granted entitlement kind=%ld", (long)result.entitlement.kind);
            } else {
                NSLog(@"Not granted. reason=%ld", (long)result.reason);
            }
        }];
}
```

<Tip>
  `showWithCompletion:` is auto-generated from the `async throws` Swift method via [SE-0297](https://github.com/apple/swift-evolution/blob/main/proposals/0297-concurrency-objc.md). The completion block always receives either a result or an error, never both.
</Tip>

***

## API Mapping

Every Obj-C method forwards directly to the Swift SDK. Use the Swift SDK Reference for method-level semantics; the table below is your translation key.

| Swift                                    | Objective-C                                                                                  |
| :--------------------------------------- | :------------------------------------------------------------------------------------------- |
| `Encore.shared`                          | `[EncoreClient shared]`                                                                      |
| `configure(apiKey:options:)`             | `-configureWithApiKey:options:`                                                              |
| `identify(userId:attributes:)`           | `-identifyWithUserId:attributes:`                                                            |
| `setUserAttributes(_:)`                  | `-setUserAttributes:`                                                                        |
| `reset()`                                | `-reset`                                                                                     |
| `placement(_:).show()` (`async throws`)  | `-placement:` → `[builder showWithCompletion:]`                                              |
| `placement(_:).show()` (fire-and-forget) | `-placement:` → `[builder showAndForget]`                                                    |
| `isActive(_:in:)` (`async`)              | `-isActive:scope:completion:`                                                                |
| `revokeEntitlements()` (`async throws`)  | `-revokeEntitlementsWithCompletion:`                                                         |
| `onPurchaseRequest { … }`                | `-onPurchaseRequest:` (block receives `EncorePurchaseRequest *` + `void (^done)(NSError *)`) |
| `onPassthrough { … }`                    | `-onPassthrough:`                                                                            |
| `EncoreError` (enum)                     | `NSError` — see [Error Handling](#error-handling)                                            |

***

## Error Handling

All Obj-C completion handlers receive an optional `NSError *`. Errors originating from EncoreKit use the domain published on `EncoreErrorInfo` and numeric codes defined by `EncoreErrorCode`:

```objc theme={null}
[[EncoreClient shared] revokeEntitlementsWithCompletion:^(NSError *error) {
    if (!error) { return; }

    if ([error.domain isEqualToString:EncoreErrorInfo.domain]) {
        switch (error.code) {
            case EncoreErrorCodeIntegrationNotConfigured:
                NSLog(@"SDK not configured — call configureWithApiKey: first");
                break;
            case EncoreErrorCodeProtocolAPI: {
                NSNumber *status = error.userInfo[EncoreErrorInfo.statusKey];
                NSString *message = error.userInfo[EncoreErrorInfo.messageKey];
                NSLog(@"API error %@: %@", status, message);
                break;
            }
            default:
                NSLog(@"Encore error: %@", error.localizedDescription);
        }
    }
}];
```

Available codes: `EncoreErrorCodeTransportNetwork`, `EncoreErrorCodeTransportPersistence`, `EncoreErrorCodeProtocolHTTP`, `EncoreErrorCodeProtocolAPI`, `EncoreErrorCodeProtocolDecoding`, `EncoreErrorCodeIntegrationNotConfigured`, `EncoreErrorCodeIntegrationInvalidApiKey`, `EncoreErrorCodeIntegrationInvalidURL`, `EncoreErrorCodeDomain`. The `userInfo` dictionary may include `EncoreErrorInfo.statusKey`, `EncoreErrorInfo.apiCodeKey`, and `EncoreErrorInfo.messageKey` depending on the error.

***

## Known Constraints

<Warning>
  The Obj-C overlay is intentionally a subset of the Swift surface. The following gaps exist in `EncoreObjC` 0.1:

  * **`isActivePublisher` (Combine) is not bridged.** Poll `-isActive:scope:completion:` instead if you need live entitlement state.
  * **`onPurchaseComplete` is not bridged in v0.1.** Its argument is `StoreKit.Transaction`, which is a Swift struct and not representable in Obj-C. Use `onPurchaseRequest` and sync state from your purchase code directly.
  * **`EncoreOptions.unlockMode` is accepted but ignored.** It's parked until EncoreKit promotes `UnlockMode` to public API. Setting it has no effect today.
  * **Purchase handler completion takes only `NSError *`.** The Swift handler's `Bool` return is not surfaced in v0.1 — success/failure is signalled by passing `nil` or a non-nil error to `done(...)`.
</Warning>

***

## Mixed Codebases

If your app has both Swift and Obj-C files, use each language's native SDK from its own files — they share a single underlying singleton.

```swift UserSession.swift theme={null}
import Encore

// Swift files use the typed Swift API.
Encore.shared.identify(userId: "user_123", attributes: attrs)
```

```objc LegacyBridge.m theme={null}
@import EncoreObjC;

// Obj-C files use the bridge.
[[EncoreClient shared] identifyWithUserId:@"user_123" attributes:attrs];
```

Configure the SDK once from whichever file owns app launch — subsequent calls from either language hit the same instance.

***

## Next Steps

Full API semantics — parameter defaults, threading, edge cases — live in the Swift SDK Reference. Use the [API Mapping](#api-mapping) table above to translate each Swift signature to its Obj-C selector:

<CardGroup cols={2}>
  <Card title="configure(apiKey:)" icon="book" href="/publishers/ios/sdk-reference/configure">
    Configuration options and initialization order
  </Card>

  <Card title="identify(userId:)" icon="book" href="/publishers/ios/sdk-reference/identify">
    User identification and attribute behavior
  </Card>

  <Card title="placement(_:).show()" icon="book" href="/publishers/ios/sdk-reference/placement">
    Placement presentation flow and results
  </Card>

  <Card title="onPurchaseRequest" icon="book" href="/publishers/ios/sdk-reference/on-purchase-request">
    Wiring StoreKit, RevenueCat, Adapty, or custom purchase logic
  </Card>
</CardGroup>

<Info>
  Swift reference docs apply to the Obj-C bridge with the mapping above. If a method isn't listed in the [API Mapping](#api-mapping) table, it's not bridged in v0.1 — see [Known Constraints](#known-constraints).
</Info>
