# Feature flags en el iOS SDK

`Pushwoosh.configure.featureFlags` proporciona a su aplicación iOS los [feature flags](/es/product/audience-data-and-segmentation/feature-flags/) configurados para la aplicación en el Panel de control. Pushwoosh evalúa las reglas del flag en el servidor para el dispositivo y el usuario actuales, y el SDK devuelve el resultado: si el flag está activado para este dispositivo y qué valores de propiedades se aplican.

<Aside type="tip" title="Función beta">
Los feature flags están en beta y solo están disponibles en los planes que los incluyen. En otros planes, el servidor no devuelve ningún flag, por lo que cada flag recurre al valor predeterminado que usted pasa en el código.
</Aside>

## Requisitos previos

* Pushwoosh iOS SDK 7.2.10 o posterior, integrado como se describe en el [Resumen del SDK de iOS](/es/developer/pushwoosh-sdk/ios-sdk/). Los feature flags vienen en los módulos obligatorios `PushwooshFramework` y `PushwooshCore`, por lo que no necesita agregar otro módulo. Funcionan en la propia aplicación iOS. Las extensiones de la aplicación, como una Notification Service Extension, nunca solicitan flags.
* Al menos un flag creado en **Audience** → **Feature Flags**. Necesita su **Key** y las claves y los tipos de sus propiedades. Use las claves exactamente como aparecen allí, incluidas mayúsculas y minúsculas. Por ejemplo, `get("New_Checkout")` no encuentra un flag con la clave `new_checkout`.
* Si la aplicación envía las solicitudes del SDK a través de un proxy inverso, su URL debe terminar en `json/1.3/`. De lo contrario, el SDK no solicita flags y cada flag recurre a su valor predeterminado.

## Leer un flag

Pase siempre un valor predeterminado. Se aplica hasta que el SDK tenga flags para el dispositivo, y siempre que el flag no exista, esté archivado o el plan no incluya feature flags.

```swift
import PushwooshFramework

let flags = Pushwoosh.configure.featureFlags

if let checkout = flags.get("new_checkout"), checkout.isEnabled {
    showNewCheckout(
        buttonColor: checkout.string(forProperty: "button_color", default: "blue") ?? "blue",
        maxItems: Int(checkout.number(forProperty: "max_items", default: 3))
    )
} else {
    showOldCheckout()
}
```

Para comprobar solo si el flag está activado, use `isEnabled`:

```swift
let isNewCheckout = Pushwoosh.configure.featureFlags.isEnabled("new_checkout", default: false)
```

En Objective-C, llame a `[PushwooshConfig featureFlags]`.

## Qué devuelve un flag

`get(_:)` devuelve un objeto `PWFeatureFlag`, o `nil` cuando el SDK aún no tiene flags para el dispositivo o el flag no está en la respuesta. Un flag archivado no está en la respuesta. `get` solo lee los flags almacenados y nunca espera a la red. El objeto tiene estas propiedades:

| Propiedad | Tipo | Descripción |
| :---- | :---- | :---- |
| `key` | String | La clave del flag. |
| `isEnabled` | Bool | Si el flag está activado para este dispositivo. |
| `ruleId` | String | La regla que decidió el resultado, por ejemplo `r_1a2b3c4d`. `default` cuando ninguna regla coincidió con el dispositivo, y una cadena vacía cuando el flag está desactivado de emergencia (killed). |

Lea los valores de las propiedades con el método que corresponda al tipo de la propiedad. Cuando el flag está desactivado, las propiedades contienen los valores predeterminados establecidos en el Panel de control.

| Método | Devuelve |
| :---- | :---- |
| `string(forProperty:default:)` | El valor de una propiedad **String**, o su valor predeterminado. |
| `number(forProperty:default:)` | El valor de una propiedad **Number** como `Double`, o su valor predeterminado. |
| `bool(forProperty:default:)` | El valor de una propiedad **Boolean**, o su valor predeterminado. |
| `jsonObject(forProperty:)` | El valor de una propiedad **JSON** que contiene un objeto, como diccionario, o `nil`. |
| `jsonArray(forProperty:)` | El valor de una propiedad **JSON** que contiene un array, o `nil`. |

Cada método devuelve su valor predeterminado, o `nil` en el caso de los métodos JSON, cuando la propiedad falta o contiene un valor de otro tipo.

`isEnabled(_:default:)` devuelve el valor predeterminado cuando `get(_:)` devuelve `nil`.

<span id="keep-flags-up-to-date" />

## Mantener los flags actualizados

El SDK almacena la última respuesta en el dispositivo durante un máximo de 24 horas. Los flags almacenados solo son válidos para el User ID y el Application Code de la aplicación para los que se solicitaron. El SDK los actualiza:

* cuando comienza una sesión, es decir, cuando la aplicación se activa por primera vez o después de al menos 30 minutos en segundo plano;
* cada hora mientras la aplicación está en primer plano;
* después de que el dispositivo se registra para recibir notificaciones push, si la solicitud anterior se envió antes del registro;
* justo después de que el User ID cambia con `setUserId`. Los flags almacenados del usuario anterior se descartan, por lo que un usuario nuevo nunca los ve.

Para reaccionar a los nuevos valores, agregue un listener que adopte `PWFeatureFlagsUpdateListener`. Su `featureFlagsDidUpdate()` se llama en el hilo principal después de una actualización que cambió algún flag. El SDK mantiene los listeners con referencias débiles. Mantenga una referencia fuerte al suyo, o se liberará y nunca se llamará:

```swift
final class CheckoutViewController: UIViewController, PWFeatureFlagsUpdateListener {
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        Pushwoosh.configure.featureFlags.addUpdateListener(self)
    }

    override func viewWillDisappear(_ animated: Bool) {
        Pushwoosh.configure.featureFlags.removeUpdateListener(self)
        super.viewWillDisappear(animated)
    }

    func featureFlagsDidUpdate() {
        render()
    }
}
```

Para solicitar los flags usted mismo, por ejemplo después de una acción que puede mover al usuario a otro segmento, llame a `refresh(completion:)`. Mientras los flags almacenados son válidos, envía como máximo una solicitud cada 5 minutos y, dentro de ese intervalo, responde con los flags almacenados. La completion se ejecuta en el hilo principal:

```swift
Pushwoosh.configure.featureFlags.refresh { error in
    if let error = error as NSError? {
        let status = error.code
        // the stored flags, or your defaults, stay in effect
    }
    render()
}
```

En caso de fallo, el error tiene el dominio `PWFeatureFlagsErrorDomain` y su `code` es el estado HTTP. El código es `0` cuando la solicitud no obtuvo respuesta HTTP o no se envió, y `200` cuando el servidor respondió pero la respuesta no se pudo usar.

Para probar las actualizaciones sin el límite de 5 minutos, establezca la clave Boolean `Pushwoosh_FEATURE_FLAGS_DISABLE_THROTTLE` en `Info.plist` en `YES`.

Si Pushwoosh no puede determinar a tiempo los segmentos del dispositivo, devuelve una respuesta parcial. El SDK no la almacena en el dispositivo, y la siguiente actualización trae la respuesta completa.

Cuando un flag se desactiva de emergencia (kill) en el Panel de control, los dispositivos lo reciben desactivado y con los valores predeterminados de las propiedades en la siguiente actualización. Cuando el SDK no puede conectarse con el servidor, sigue usando los flags almacenados hasta que tienen 24 horas de antigüedad, y después sus valores predeterminados.

<span id="log-impressions" />

## Registrar impresiones

Una impresión le indica a Pushwoosh que un usuario realmente llegó al código que controla un flag, para que pueda comparar a los usuarios que recibieron la función con los que no. Llame a `logImpression(_:)` en el momento en que el usuario ve la función o su versión anterior, tanto en la rama activada como en la desactivada, porque los usuarios con el flag desactivado son su grupo de comparación:

```swift
let flags = Pushwoosh.configure.featureFlags
let isOn = flags.isEnabled("new_checkout", default: false)
flags.logImpression("new_checkout")
if isOn {
    showNewCheckout()
} else {
    showOldCheckout()
}
```

El SDK envía el evento `PW_FeatureFlagImpression` con estos atributos:

| Atributo | Tipo | Valor |
| :---- | :---- | :---- |
| `flag_key` | string | La clave del flag. |
| `rule_id` | string | La regla que decidió el resultado, como en `ruleId`. |
| `enabled` | boolean | Si el flag estaba activado. |

No necesita crear el evento de antemano, porque Pushwoosh agrega `PW_FeatureFlagImpression` con estos atributos a **Audience** → **Events** cuando llega la primera impresión. El SDK envía una impresión por flag, regla y estado en una sesión, y no envía nada cuando no hay flag para la clave. Si el envío falla, la siguiente llamada a `logImpression` para el flag la vuelve a enviar.

Las impresiones también permiten a los equipos de marketing crear un segmento de usuarios que llegaron a la función: el estado **vio la función (30 días)** de la [condición de feature flag](/es/product/audience-data-and-segmentation/feature-flags/#find-users-who-reached-a-feature).

## Referencia de métodos

`Pushwoosh.configure.featureFlags` tiene estos métodos:

| Método | Descripción |
| :---- | :---- |
| `get(_:)` | Devuelve el `PWFeatureFlag` de la clave, o `nil`. Solo lee los flags almacenados. |
| `isEnabled(_:default:)` | Devuelve si el flag está activado, o el valor predeterminado cuando no hay flag. |
| `refresh(completion:)` | Solicita los flags, con un límite de una vez cada 5 minutos mientras los flags almacenados son válidos. La completion recibe `nil` o un error en `PWFeatureFlagsErrorDomain`, y puede ser `nil`. |
| `addUpdateListener(_:)` | Llama al `featureFlagsDidUpdate()` del listener después de cada actualización que cambió un flag. El listener se mantiene con una referencia débil. |
| `removeUpdateListener(_:)` | Elimina un listener agregado con `addUpdateListener(_:)`. |
| `logImpression(_:)` | Envía un evento `PW_FeatureFlagImpression` para la regla y el estado actuales del flag. |