Skip to content



Guide d’implémentation des Content Cards

Ce guide d’implémentation avancé et optionnel couvre les considérations du code des Content Cards, trois cas d’utilisation personnalisés créés par notre équipe, les extraits de code l’accompagnant et les directives sur l’enregistrement des impressions, des clics et des rejets. Visitez notre dépôt de démonstrations Braze sur le dépôt de démonstrations Braze sur GitHub ! Notez que ce guide d’implémentation est centré autour d’une implémentation Swift, mais les extraits de code Objective-C sont fournis aux personnes intéressées.

Considérations du code

Les Content Cards en tant qu’objets personnalisés

Tout comme une fusée à laquelle on ajoute un propulseur, vos propres objets personnalisés peuvent être étendus pour fonctionner comme des Content Cards. Les surfaces d’API limitées de ce type offrent la flexibilité de travailler avec différents backends de données de manière interchangeable. Pour cela, il suffit de se conformer au protocole ContentCardable et d’implémenter l’initialiseur (comme illustré dans les extraits de code suivants). Grâce à l’utilisation de la structure ContentCardData, vous pouvez accéder aux données ABKContentCard. Le payload ABKContentCard sera utilisé pour initialiser la structure ContentCardData et l’objet personnalisé lui-même, le tout à partir d’un type Dictionary via l’initialiseur fourni par le protocole.

L’initialiseur inclut également un enum ContentCardClassType. Cet enum est utilisé pour déterminer quel objet initialiser. Grâce aux paires clé-valeur dans le tableau de bord de Braze, vous pouvez définir une clé class_type explicite qui sera utilisée pour déterminer quel objet initialiser. Ces paires clé-valeur pour les Content Cards sont transmises dans la variable extras de l’ABKContentCard. Un autre composant essentiel de l’initialiseur est le paramètre dictionnaire metaData. Le metaData contient tout ce qui provient de l’ABKContentCard, analysé en une série de clés et de valeurs. Une fois les cartes pertinentes analysées et converties en vos objets personnalisés, l’application est prête à les utiliser comme s’ils avaient été instanciés à partir de JSON ou de toute autre source.

Une fois que vous avez une bonne compréhension de ces considérations du code, consultez nos cas d’usage pour commencer à implémenter vos objets personnalisés.

Protocole ContentCardable
Un objet ContentCardData qui représente les données ABKContentCard accompagné d’un enum ContentCardClassType. Un initialiseur utilisé pour instancier des objets personnalisés avec les métadonnées ABKContentCard.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protocol ContentCardable {
  var contentCardData: ContentCardData? { get }
  init?(metaData: [ContentCardKey: Any], classType contentCardClassType: ContentCardClassType)
}

extension ContentCardable {
  var isContentCard: Bool {
    return contentCardData != nil
  }

  func logContentCardClicked() {
    BrazeManager.shared.logContentCardClicked(idString: contentCardData?.contentCardId)
  }

  func logContentCardDismissed() {
    BrazeManager.shared.logContentCardDismissed(idString: contentCardData?.contentCardId)
  }

  func logContentCardImpression() {
    BrazeManager.shared.logContentCardImpression(idString: contentCardData?.contentCardId)
  }
}

Structure de données Content Card
ContentCardData représente les valeurs analysées d’une ABKContentCard.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct ContentCardData: Hashable {
  let contentCardId: String
  let contentCardClassType: ContentCardClassType
  let createdAt: Double
  let isDismissable: Bool
  ...
  // other Content Card properties such as expiresAt, pinned, etc.
}

extension ContentCardData: Equatable {
  static func ==(lhs: ContentCardData, rhs: ContentCardData) -> Bool {
    return lhs.contentCardId == rhs.contentCardId
  }
}

Protocole ContentCardable
Un objet ContentCardData qui représente les données ABKContentCard accompagné d’un enum ContentCardClassType, un initialiseur utilisé pour instancier des objets personnalisés avec les métadonnées ABKContentCard.

1
2
3
4
5
6
7
8
9
10
11
12
@protocol ContentCardable <NSObject>

@property (nonatomic, strong) ContentCardData *contentCardData;
- (instancetype __nullable)initWithMetaData:(NSDictionary *)metaData
                                  classType:(enum ContentCardClassType)classType;

- (BOOL)isContentCard;
- (void)logContentCardImpression;
- (void)logContentCardClicked;
- (void)logContentCardDismissed;

@end

Structure de données Content Card
ContentCardData représente les valeurs analysées d’une ABKContentCard.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@interface ContentCardData : NSObject

+ (ContentCardClassType)contentCardClassTypeForString:(NSString *)rawValue;

- (instancetype)initWithIdString:(NSString *)idString
                       classType:(ContentCardClassType)classType
                       createdAt:(double)createdAt isDismissible:(BOOL)isDismissible;

@property (nonatomic, readonly) NSString *contentCardId;
@property (nonatomic) ContentCardClassType classType;
@property (nonatomic, readonly) double *createdAt;
@property (nonatomic, readonly) BOOL isDismissible;
...
// other Content Card properties such as expiresAt, pinned, etc.

@end

Initialiseur d’objet personnalisé
Les métadonnées d’une ABKContentCard sont utilisées pour remplir les variables de votre objet. Les paires clé-valeur configurées dans le tableau de bord de Braze sont représentées dans le dictionnaire « extras ».

1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension CustomObject: ContentCardable {
  init?(metaData: [ContentCardKey: Any], classType contentCardClassType: ContentCardClassType) {
    guard let idString = metaData[.idString] as? String,
      let createdAt = metaData[.created] as? Double,
      let isDismissable = metaData[.dismissable] as? Bool,
      let extras = metaData[.extras] as? [AnyHashable: Any],
      else { return nil }

    let contentCardData = ContentCardData(contentCardId: idString, contentCardClassType: contentCardClassType, createdAt: createdAt, isDismissable: isDismissable)
    let customObjectProperty = extras["YOUR-CUSTOM-OBJECT-PROPERTY"] as? String

    self.init(contentCardData: contentCardData, property: customObjectProperty)
  }
}

Identification des types
L’enum ContentCardClassType représente la valeur class_type dans le tableau de bord de Braze. Cette valeur est également utilisée comme identifiant de filtre pour afficher les Content Cards à différents emplacements.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum ContentCardClassType: Hashable {
  case yourValue
  case yourOtherValue
  ...
  case none

  init(rawType: String?) {
    switch rawType?.lowercased() {
    case "your_value": // these values much match the value set in the Braze dashboard
      self = .yourValue
    case "your_other_value": // these values much match the value set in the Braze dashboard
      self = .yourOtherValue
    ...
    default:
      self = .none
    }
  }
}

Initialiseur d’objet personnalisé
Les métadonnées d’une ABKContentCard sont utilisées pour remplir les variables de votre objet. Les paires clé-valeur configurées dans le tableau de bord de Braze sont représentées dans le dictionnaire « extras ».

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (id _Nullable)initWithMetaData:(nonnull NSDictionary *)metaData classType:(enum ContentCardClassType)classType {
  self = [super init];
  if (self) {
    if ([metaData objectForKey:ContentCardKeyIdString] && [metaData objectForKey:ContentCardKeyCreated] && [metaData objectForKey:ContentCardKeyDismissible] && [metaData objectForKey:ContentCardKeyExtras]) {
      NSDictionary  *extras = metaData[ContentCardKeyExtras];
      NSString *idString = metaData[ContentCardKeyIdString];
      double createdAt = [metaData[ContentCardKeyCreated] doubleValue];
      BOOL isDismissible = metaData[ContentCardKeyDismissible];

      if ([extras objectForKey: @"YOUR-CUSTOM-PROPERTY")
        _customObjectProperty = extras[@"YOUR-CUSTOM-OBJECT-PROPERTY"];

      self.contentCardData = [[ContentCardData alloc] initWithIdString:idString classType:classType createdAt:createdAt isDismissible:isDismissible];

      return self;
    }
  }
  return nil;
}

Identification des types
L’enum ContentCardClassType représente la valeur class_type dans le tableau de bord de Braze. Cette valeur est également utilisée comme identifiant de filtre pour afficher les Content Cards à différents emplacements.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef NS_ENUM(NSInteger, ContentCardClassType) {
  ContentCardClassTypeNone = 0,
  ContentCardClassTypeYourValue,
  ContentCardClassTypeYourOtherValue,
  ...
};

+ (NSArray *)contentCardClassTypeArray {
  return @[ @"", @"your_value", @"your_other_value" ];
}

+ (ContentCardClassType)contentCardClassTypeForString:(NSString*)rawValue {
  if ([[self contentCardClassTypeArray] indexOfObject:rawValue] == NSNotFound) {
    return ContentCardClassTypeNone;
  } else {
    NSInteger value = [[self contentCardClassTypeArray] indexOfObject:rawValue];
    return (ContentCardClassType) value;
  }
}

Demande de Content Cards
Tant que l’observateur est conservé en mémoire, le rappel de notification du SDK Braze peut être attendu.

1
2
3
4
func loadContentCards() {
  BrazeManager.shared.addObserverForContentCards(observer: self, selector: #selector(contentCardsUpdated))
  BrazeManager.shared.requestContentCardsRefresh()
}

Gestion du rappel du SDK Content Cards
Transférez le rappel de notification vers le fichier utilitaire pour analyser les données du payload de votre ou vos objets personnalisés.

1
2
3
4
5
@objc func contentCardsUpdated(_ notification: Notification) {
  guard let contentCards = BrazeManager.shared.handleContentCardsUpdated(notification, for: [.yourValue]) as? [CustomObject],!contentCards.isEmpty else { return }

 // do something with your array of custom objects
}

Utilisation des Content Cards
Le class_type est transmis en tant que filtre pour ne retourner que les Content Cards ayant un class_type correspondant.

1
2
3
4
5
func handleContentCardsUpdated(_ notification: Notification, for classTypes: [ContentCardClassType]) -> [ContentCardable] {
  guard let updateIsSuccessful = notification.userInfo?[ABKContentCardsProcessedIsSuccessfulKey] as? Bool, updateIsSuccessful, let cards = contentCards else { return [] }

  return convertContentCards(cards, for: classTypes)
}

Demande de Content Cards
Tant que l’observateur est conservé en mémoire, le rappel de notification du SDK Braze peut être attendu.

1
2
3
4
- (void)loadContentCards {
  [[BrazeManager shared] addObserverForContentCards:self selector:@selector(contentCardsUpdated:)];
  [[BrazeManager shared] requestContentCardsRefresh];
}

Gestion du rappel du SDK Content Cards
Transférez le rappel de notification vers le fichier utilitaire pour analyser les données du payload de votre ou vos objets personnalisés.

1
2
3
4
5
6
- (void)contentCardsUpdated:(NSNotification *)notification {
  NSArray *classTypes = @[@(ContentCardClassTypeYourValue)];
  NSArray *contentCards = [[BrazeManager shared] handleContentCardsUpdated:notification forClassTypes:classTypes];

  // do something with your array of custom objects
}

Utilisation des Content Cards
Le class_type est transmis en tant que filtre pour ne retourner que les Content Cards ayant un class_type correspondant.

1
2
3
4
5
6
7
8
- (NSArray *)handleContentCardsUpdated:(NSNotification *)notification forClassType:(ContentCardClassType)classType {
  BOOL updateIsSuccessful = [notification.userInfo[ABKContentCardsProcessedIsSuccessfulKey] boolValue];
  if (updateIsSuccessful) {
    return [self convertContentCards:self.contentCards forClassType:classType];
  } else {
    return @[];
  }
}

Utilisation des données du payload
Parcourt le tableau de Content Cards et n’analyse que les cartes ayant un class_type correspondant. Le payload d’une ABKContentCard est analysé en un Dictionary.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func convertContentCards(_ cards: [ABKContentCard], for classTypes: [ContentCardClassType]) -> [ContentCardable] {
  var contentCardables: [ContentCardable] = []

  for card in cards {
    let classTypeString = card.extras?[ContentCardKey.classType.rawValue] as? String
    let classType = ContentCardClassType(rawType: classTypeString)
    guard classTypes.contains(classType) else { continue }

    var metaData: [ContentCardKey: Any] = [:]
    switch card {
    case let banner as ABKBannerContentCard:
      metaData[.image] = banner.image
    case let captioned as ABKCaptionedImageContentCard:
      metaData[.title] = captioned.title
      metaData[.cardDescription] = captioned.cardDescription
      metaData[.image] = captioned.image
    case let classic as ABKClassicContentCard:
      metaData[.title] = classic.title
      metaData[.cardDescription] = classic.cardDescription
    default:
      break
    }

    metaData[.idString] = card.idString
    metaData[.created] = card.created
    metaData[.dismissible] = card.dismissible
    metaData[.urlString] = card.urlString
    metaData[.extras] = card.extras
    ...
    // other Content Card properties such as expiresAt, pinned, etc.

    if let contentCardable = contentCardable(with: metaData, for: classType) {
      contentCardables.append(contentCardable)
    }
  }
  return contentCardables
}

Initialisation de vos objets personnalisés à partir des données du payload Content Card
Le class_type est utilisé pour déterminer lequel de vos objets personnalisés sera initialisé à partir des données du payload.

1
2
3
4
5
6
7
8
9
10
11
func contentCardable(with metaData: [ContentCardKey: Any], for classType: ContentCardClassType) -> ContentCardable? {
  switch classType {
  case .yourValue:
    return CustomObject(metaData: metaData, classType: classType)
  case .yourOtherValue:
    return OtherCustomObject(metaData: metaData, classType: classType)
  ...
  default:
    return nil
  }
}

Utilisation des données du payload
Parcourt le tableau de Content Cards et n’analyse que les cartes ayant un class_type correspondant. Le payload d’une ABKContentCard est analysé en un Dictionary.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
- (NSArray *)convertContentCards:(NSArray<ABKContentCard*> *)cards forClassType:(ContentCardClassType)classType {
  NSMutableArray *contentCardables = [[NSMutableArray alloc] init];      for (ABKContentCard *card in cards) {
    NSString *classTypeString = [card.extras objectForKey:ContentCardKeyClassType];
    ContentCardClassType cardClassType = [ContentCardData contentCardClassTypeForString: classTypeString];
    if (cardClassType != classType) { continue; }

    NSMutableDictionary *metaData = [[NSMutableDictionary alloc] init];
    if ([card isKindOfClass:[ABKBannerContentCard class]]) {
      ABKBannerContentCard *banner = (ABKBannerContentCard *)card;
      metaData[ContentCardKeyImage] = banner.image;
    } else if ([card isKindOfClass:[ABKCaptionedImageContentCard class]]) {
      ABKCaptionedImageContentCard *captioned = (ABKCaptionedImageContentCard *)card;
      metaData[ContentCardKeyTitle] = captioned.title;
      metaData[ContentCardKeyCardDescription] = captioned.cardDescription;
      metaData[ContentCardKeyImage] = captioned.image;
    } else if ([card isKindOfClass:[ABKClassicContentCard class]]) {
      ABKClassicContentCard *classic = (ABKClassicContentCard *)card;
      metaData[ContentCardKeyCardDescription] = classic.title;
      metaData[ContentCardKeyImage] = classic.image;
    }

    metaData[ContentCardKeyIdString] = card.idString;
    metaData[ContentCardKeyCreated] = [NSNumber numberWithDouble:card.created];
    metaData[ContentCardKeyDismissible] = [NSNumber numberWithBool:card.dismissible];
    metaData[ContentCardKeyUrlString] = card.urlString;
    metaData[ContentCardKeyExtras] = card.extras;
    ...
    // other Content Card properties such as expiresAt, pinned, etc.

    id<ContentCardable> contentCardable = [self contentCardableWithMetaData:metaData forClassType:classType];
    if (contentCardable) {
      [contentCardables addObject:contentCardable];
    }
  }

  return contentCardables;
}

Initialisation de vos objets personnalisés à partir des données du payload Content Card
Le class_type est utilisé pour déterminer lequel de vos objets personnalisés sera initialisé à partir des données du payload.

1
2
3
4
5
6
7
8
9
10
11
- (id<ContentCardable>)contentCardableWithMetaData:(NSDictionary *)metaData forClassType:(ContentCardClassType)classType {
  switch (classType) {
    case ContentCardClassTypeYourValue:
      return [[CustomObject alloc] initWithMetaData:metaData classType:classType];
    case ContentCardClassTypeYourOtherValue:
      return nil;
    ...
    default:
      return nil;
  }
}

Cas d’usage

Nous avons fourni trois cas d’usage dans la section suivante. Chacun offre une explication détaillée, des extraits de code pertinents et un aperçu de la manière dont les variables des Content Cards peuvent se présenter et être utilisées dans le tableau de bord de Braze :

Content Cards comme contenu supplémentaire

Flux avec une liste hybride mélangeant données locales et Content Cards Braze.

Vous pouvez intégrer de façon fluide les Content Cards dans un flux existant, permettant aux données de plusieurs flux de se charger simultanément. Cela crée une expérience cohérente et harmonieuse avec les Content Cards Braze et le contenu de flux existant.

L’exemple ci-joint montre une UICollectionView avec une liste hybride d’éléments alimentés par des données locales et des Content Cards propulsées par Braze. Ainsi, les Content Cards peuvent se fondre parfaitement dans le contenu existant.

Configuration du tableau de bord

Cette Content Card est délivrée par une Campaign déclenchée par API avec des paires clé-valeur déclenchées par API. C’est idéal pour les Campaigns où les valeurs de la carte dépendent de facteurs externes pour déterminer le contenu à afficher à l’utilisateur. Notez que class_type doit être connu au moment de la configuration.

Les paires clé-valeur pour le cas d'usage de Content Cards supplémentaires. Dans cet exemple, différents aspects de la carte tels que « tile_id », « tile_deeplink » et « tile_title » sont définis à l'aide de Liquid.

Prêt à enregistrer les analyses ?

Consultez la section suivante pour mieux comprendre à quoi devrait ressembler le flux de données.

Content Cards dans un centre de messages


Les Content Cards peuvent être utilisées dans un format de centre de messages où chaque message est sa propre carte. Chaque message du centre de messages est alimenté via un payload de Content Card, et chaque carte contient des paires clé-valeur supplémentaires qui contrôlent l’UI/UX au clic. Dans l’exemple suivant, un message vous dirige vers une vue personnalisée arbitraire, tandis qu’un autre ouvre une webview affichant du HTML personnalisé.

Centre de messages Content Card avec des cartes de messages individuelles.

Configuration du tableau de bord

Pour les types de messages suivants, la paire clé-valeur class_type doit être ajoutée à la configuration de votre tableau de bord. Les valeurs assignées ici sont arbitraires mais doivent être distinguables entre les types de classes. Ces paires clé-valeur sont les identifiants clés que l’application examine pour décider où naviguer lorsque l’utilisateur clique sur un message abrégé de la boîte de réception.

Les paires clé-valeur pour ce cas d’usage comprennent :

  • message_header défini comme Full Page
  • class_type défini comme message_full_page

Exemple de message Content Card en pleine page.

Les paires clé-valeur pour ce cas d’usage comprennent :

  • message_header défini comme HTML
  • class_type défini comme message_webview
  • message_title

Ce message recherche également une paire clé-valeur HTML, mais si vous travaillez avec un domaine web, une paire clé-valeur URL est également valide.

Content Card ouvrant une webview HTML à partir d'une paire clé-valeur.

Explication complémentaire

La logique du centre de messages est pilotée par le contentCardClassType fourni par les paires clé-valeur de Braze. En utilisant la méthode addContentCardToView, vous pouvez à la fois filtrer et identifier ces types de classes.

Utilisation de class_type pour le comportement au clic
Lorsqu’un message est cliqué, le ContentCardClassType gère la manière dont l’écran suivant doit être rempli.

1
2
3
4
5
6
7
8
9
10
func addContentCardToView(with message: Message) {
    switch message.contentCardData?.contentCardClassType {
      case .message(.fullPage):
        loadContentCardFullPageView(with: message as! FullPageMessage)
      case .message(.webView):
        loadContentCardWebView(with: message as! WebViewMessage)
      default:
        break
    }
}

Utilisation de class_type pour le comportement au clic
Lorsqu’un message est cliqué, le ContentCardClassType gère la manière dont l’écran suivant doit être rempli.

1
2
3
4
5
6
7
8
9
10
11
12
- (void)addContentCardToView:(Message *)message {
  switch (message.contentCardData.classType) {
    case ContentCardClassTypeMessageFullPage:
      [self loadContentCardFullPageView:(FullPageMessage *)message];
      break;
    case ContentCardClassTypeMessageWebview:
      [self loadContentCardWebView:(WebViewMessage *)message];
      break;
    default:
      break;
  }
}
Prêt à enregistrer les analyses ?

Consultez la section suivante pour mieux comprendre à quoi devrait ressembler le flux de données.

Une Content Card interactive affichant une promotion de 50 pour cent apparaît dans le coin inférieur gauche de l'écran. Après un clic, la promotion est appliquée au panier.

Content Cards interactives


Les Content Cards peuvent être utilisées pour créer des expériences dynamiques et interactives pour vos utilisateurs. Dans l’exemple ci-joint, une fenêtre contextuelle de Content Card apparaît au moment du paiement pour offrir aux utilisateurs des promotions de dernière minute.

Des cartes bien placées comme celle-ci sont un excellent moyen d’inciter les utilisateurs à effectuer des actions spécifiques.


Configuration du tableau de bord

La configuration du tableau de bord pour les Content Cards interactives est simple. Les paires clé-valeur pour ce cas d’usage comprennent un discount_percentage défini comme le montant de remise souhaité et un class_type défini comme coupon_code. Ces paires clé-valeur déterminent comment les Content Cards spécifiques à un type sont filtrées et affichées sur l’écran de paiement.

Content Card interactive affichant une promotion au moment du paiement.

Prêt à enregistrer les analyses ?

Consultez la section suivante pour mieux comprendre à quoi devrait ressembler le flux de données.

Personnalisation du mode sombre

Par défaut, les vues de Content Cards répondent automatiquement aux changements de mode sombre sur l’appareil avec un ensemble de couleurs thématiques.

Ce comportement peut être remplacé comme décrit dans notre guide de style personnalisé.

Enregistrement des impressions, clics et rejets

Après avoir étendu vos objets personnalisés pour fonctionner comme des Content Cards, l’enregistrement d’indicateurs pertinents tels que les impressions, les clics et les rejets est rapide. Cela peut être réalisé en utilisant un protocole ContentCardable qui référence et fournit des données à un fichier auxiliaire pour être enregistrées par le SDK Braze.

Composants d’implémentation

Enregistrement des analyses
Les méthodes d’enregistrement peuvent être appelées directement depuis les objets conformes au protocole ContentCardable.

1
2
3
customObject.logContentCardImpression()
customObject.logContentCardClicked()
customObject.logContentCardDismissed()

Récupération de l’ABKContentCard
L’idString transmis depuis votre objet personnalisé est utilisé pour identifier la Content Card associée afin d’enregistrer les analyses.

1
2
3
4
5
6
7
8
9
10
11
extension BrazeManager {
  func logContentCardImpression(idString: String?) {
    guard let contentCard = getContentCard(forString: idString) else { return }

    contentCard.logContentCardImpression()
  }

  private func getContentCard(forString idString: String?) -> ABKContentCard? {
    return contentCards?.first(where: { $0.idString == idString })
  }
}

Enregistrement des analyses
Les méthodes d’enregistrement peuvent être appelées directement depuis les objets conformes au protocole ContentCardable.

1
2
3
[customObject logContentCardImpression];
[customObject logContentCardClicked];
[customObject logContentCardDismissed];

Récupération de l’ABKContentCard
L’idString transmis depuis votre objet personnalisé est utilisé pour identifier la Content Card associée afin d’enregistrer les analyses.

1
2
3
4
5
6
7
8
9
10
11
- (void)logContentCardImpression:(NSString *)idString {
  ABKContentCard *contentCard = [self getContentCard:idString];
  [contentCard logContentCardImpression];
}

- (ABKContentCard *)getContentCard:(NSString *)idString {
  NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self.idString == %@", idString];
  NSArray *filteredArray = [self.contentCards filteredArrayUsingPredicate:predicate];

  return filteredArray.firstObject;
}

Fichiers utilitaires

Fichier utilitaire ContentCardKey
1
2
3
4
5
6
7
8
enum ContentCardKey: String {
  case idString
  case created
  case classType = "class_type"
  case dismissible
  case extras
  ...
}
1
2
3
4
5
6
static NSString *const ContentCardKeyIdString = @"idString";
static NSString *const ContentCardKeyCreated = @"created";
static NSString *const ContentCardKeyClassType = @"class_type";
static NSString *const ContentCardKeyDismissible = @"dismissible";
static NSString *const ContentCardKeyExtras = @"extras";
...
New Stuff!