Créer une application de rencontres avec Sinch Intégrer Sinch

Les applications de rencontres sont devenues l'un des genres les plus populaires de l'App Store récemment. Cependant, en raison de leur nature, développer une application de rencontres complète peut être un défi. Dans la deuxième partie de cette série, nous verrons comment nous pouvons utiliser la plate-forme Sinch dans une application iOS pour mettre en œuvre des appels vocaux et la messagerie. Ce sont deux fonctionnalités essentielles pour les applications de rencontres et elles sont étonnamment faciles à mettre en œuvre avec Sinch..

1. Vue d'ensemble

Avant de commencer à coder, examinons l'application que nous réalisons. Notre application de rencontres aura quelques fonctionnalités de base sur lesquelles nous allons nous concentrer dans ce tutoriel. Pour les comptes, les utilisateurs pourront se connecter via Facebook. Une fois connectés, ils pourront voir les autres utilisateurs de l'application qui se trouvent à proximité..

Les utilisateurs sont stockés sur le serveur avec lequel nous communiquons à partir de l'API reposante créée dans la première partie de ce didacticiel. Une fois que nous avons reçu les informations de l'utilisateur de Facebook, un nouvel utilisateur est publié sur le serveur et une session est créée. Après cela, une liste d’utilisateurs enregistrés sera récupérée pour être sélectionnée parmi.

Lorsque l'utilisateur trouve une personne avec laquelle il veut parler, il peut lui envoyer un message ou initier un appel vocal. C'est là que Sinch entrera en jeu. Nous utiliserons leur SDK pour effectuer tout le travail difficile de gestion des appels vocaux et de la messagerie..

Maintenant que nous savons ce que nous développons, il est temps de commencer.

2. Projet de démarrage

Commencez par télécharger le projet de démarrage à partir de GitHub. L'application est destinée à l'iPhone et contient l'interface utilisateur, l'authentification Facebook et la communication avec l'API RESTful déjà en place. Cela nous permettra de nous concentrer sur l'intégration avec Sinch.

Avant de poursuivre, j'aimerais toutefois discuter de l'architecture de base de l'application et de la façon dont elle communique avec le serveur. Le code qui communique avec l’API reste réside dans le UtilisateursAPIClient classe. Il utilise la bibliothèque AFNetworking pour simplifier la logique de mise en réseau. Si vous avez déjà utilisé AFNetworking, cela vous semblera très familier. Si ce n’est pas le cas, notre tutoriel sur AFNetworking est un excellent point de départ..

Dans le fichier d'en-tête, vous verrez certaines méthodes pour communiquer avec différents points de terminaison de l'API RESTful, telles que la création d'un utilisateur, le démarrage d'une session, la suppression d'un utilisateur, etc..

- (void) createUser: (utilisateur *) complétion utilisateur: (void (^) ()) action; - (void) beginUserSession: (utilisateur *) complétion d'utilisateur: action (void (^) ()); - (void) deleteUser: (utilisateur *) complétion utilisateur: (void (^) ()) action; - (void) getRegisteredUsersWithMeters: compteurs (doubles) complétions: (void (^) (NSArray * utilisateurs)) action;

Quand une réponse revient du serveur, le JSON est analysé et utilisé pour initialiser un modèle..

En parlant de modèles, il y a un modèle utilisé dans le projet, Utilisateur. Il contient des informations de base sur un utilisateur, telles que son nom, son emplacement, son identifiant, etc. L'identifiant de l'utilisateur est particulièrement important, car nous en avons besoin pour identifier de manière unique les personnes avec lesquelles nous communiquons lors de l'intégration avec Sinch..

Prenez quelques instants pour parcourir le projet afin de mieux comprendre sa structure. Comme avec les bibliothèques tierces, il est extrêmement utile de parcourir la documentation et le code source avant de les utiliser..

3. Construire et exécuter 

Si vous construisez et exécutez le projet de démonstration, vous devez être invité à vous connecter via Facebook..

Allez-y et vérifiez si vous pouvez vous connecter avec Facebook. Une fois connecté, l'application de démonstration effectue trois choses:

  • Il crée un enregistrement d'utilisateur sur le backend s'il n'y en a pas.
  • Il démarre une session utilisateur et crée un jeton d'accès pour la session..
  • Il récupère une liste d'utilisateurs dans le système.

Plus tard, lorsque vous avez créé quelques comptes de test, vous pouvez trouver des utilisateurs dans une certaine plage. Par défaut, tous les utilisateurs du système sont renvoyés car il est peu probable que d'autres utilisateurs se trouvent à proximité pendant les tests..

4. Créer un compte Sinch

Pour utiliser le SDK Sinch, vous devez d’abord créer un compte de développeur. Rendez-vous sur le site Web de Sinch et cliquez sur Commencer gratuitement en haut à droite pour vous inscrire.

Entrez votre adresse email et votre mot de passe, et acceptez les termes et conditions. Sinch lancera automatiquement un processus d’installation après votre inscription. À ce stade, il vous suffit de nommer votre application et d'ajouter une courte description. Vous pouvez ignorer le reste du guide de démarrage pour l'instant.

Ensuite, consultez le tableau de bord pour récupérer la clé API et le secret de votre application. Tu devrais voir Tableau de bord en haut du site Web de Sinch après vous être connecté avec votre nouveau compte. Sélectionner applications sur la gauche, sélectionnez l'application que vous avez créée, puis cliquez sur l'icône des clés à droite pour afficher la clé et le secret. Vous voudrez garder une trace de ces valeurs pour initialiser plus tard le client Sinch.


5. Ajouter le SDK Sinch

Il existe deux manières d’ajouter le SDK Sinch à votre projet. De loin, l'approche la plus simple consiste à utiliser CocoaPods. Ajoutez la ligne suivante à votre fichier podfile:

pod 'SinchService', '~> 1.0'

Ensuite, exécutez la commande suivante pour intégrer le SDK Sinch à votre espace de travail:

pod update

Dans ce tutoriel, cependant, je vais vous montrer comment ajouter le SDK si vous n'utilisez pas CocoaPods. Commencez par télécharger le SDK Sinch pour iOS. Visitez la section des téléchargements du site Web de Sinch et téléchargez le SDK pour iOS. Le SDK de Sinch dépend de trois cadres, nous devrons donc relier notre projet à ceux-ci en premier. dans le Navigateur de projet, choisir SinchTutorial et sélectionnez le DepuisTutorial cible.

 Sélectionner Phases de construction> Lier le binaire avec des bibliothèques et cliquez sur le bouton plus en bas.

Ajoutez les cadres suivants à la liste des cadres à associer:

  • Sécurité
  • AudioToolbox
  • AVFoundation

Nous devons également ajouter trois drapeaux d'éditeur de liens. Dans la cible Paramètres de construction, rechercher Autres drapeaux de lieur.

Ajoutez les drapeaux d'éditeur de liens suivants sous Déboguer:

-ObjC -Xlinker -lc++

Enfin, faites glisser le framework Sinch que vous avez téléchargé précédemment dans la Cadres dossier dans le Navigateur de projet. Construisez votre projet pour vous assurer qu'il n'y a pas d'erreur. Vous êtes maintenant prêt à utiliser le SDK Sinch..

6. Configuration du client Sinch

Le client Sinch est la force motrice responsable de la communication avec la plate-forme Sinch. Avant de pouvoir utiliser les fonctions de messagerie ou d’appel, nous devons initialiser une instance du client Sinch. Pour ce faire, nous avons besoin de la clé et du secret que nous avons récupérés plus tôt après la création de notre compte Sinch..

Ouvrir AppDelegate.h et importer le framework Sinch comme indiqué ci-dessous.

#importation 

Ensuite, nous devons créer une propriété pour le client Sinch et un prototype de méthode pour l’initialiser. Ajoutez l'extrait de code suivant sous le la fenêtre propriété.

@property (strong, nonatomic) id client; - (void) sinchClientWithUserId: (NSString *) userId;

Nous utiliserons cette référence au client Sinch pour facilement passer des appels et envoyer des messages. le SINClientDelegate protocole nous informera si la connexion avec le backend Sinch a été un succès.

Conforme le AppDéléguer classe à la SINClientDelegate protocole comme indiqué ci-dessous.

// conforme à SINClientDelegate @interface AppDelegate: UIResponder 

Basculer vers le fichier d’implémentation du AppDéléguer classe et mettre en œuvre le sinchClientWithUserId: méthode comme indiqué ci-dessous.

#pragma mark - Sinch - (void) sinchClientWithUserId: (NSString *) userId if (! _client) _client = [client sinad avecApplicationKey: @ ""applicationSecret: @""environmentHost: @" sandbox.sinch.com "userId: userId]; _client.delegate = self; [_client setSupportCalling: YES]; [_client setSupportMessaging: YES]; [_client start]; [_client startListeningOnActiveOconnection];

Nous utiliserons cette méthode ultérieurement pour communiquer avec le serveur Sinch après la connexion de l'utilisateur. N'oubliez pas de saisir la clé d'application et le secret. Sinon, vous ne pourrez pas vous connecter au backend de Sinch.

Après l’initialisation du client, nous disons à Sinch que nous utiliserons les fonctions de messagerie et d’appel. Nous démarrons ensuite le client et établissons une connexion avec les services Sinch pour recevoir les appels entrants..

Ensuite, implémentez les méthodes de délégation suivantes du SINClientDelegate protocole dans le AppDéléguer classe:

- (void) clientDidStart: (id) client NSLog (@ "Le client Sinch a démarré avec succès (version:% @)", [version de Sinch]);  - (void) clientDidFail: (id) erreur client: (NSError *) erreur NSLog (@ "erreur du client Sinch:% @", [erreur localizedDescription]);  - client (void): (id) client logMessage: (NSString *) zone de message: (NSString *) zone sévérité: (SINLogSeverity) gravité timestamp: (NSDate *) timestamp if (gravité == SINLogSeverityCritical) NSLog (@ "% @", message); 

Ces méthodes de délégation consigneront les erreurs sur la console Xcode, le cas échéant, et nous informeront également lorsque nous nous sommes connectés avec Sinch..

7. Initialisation du client Sinch

Ouvrir FindUsersViewController.m et faites défiler jusqu'au beginUserSession méthode. Dans cette méthode, si une session a été démarrée sans erreur, le bloc d'achèvement est exécuté. C’est le bon moment pour initialiser le client Sinch. Remplace le //FAIRE commenter avec le bloc de code suivant:

// Lancer le client Sinch AppDelegate * delegate = (AppDelegate *) [[UIApplication sharedApplication] delegate]; [delegate sinchClientWithUserId: self.curUser.userID];

Cela appelle la méthode de démarrage du client Sinch et nous permet de commencer à utiliser ses fonctionnalités. Construisez et lancez l'application. Après avoir récupéré une liste d'utilisateurs, vérifiez que la connexion au backend de Sinch a réussi en consultant les journaux de la console Xcode..

8. Implémenter la messagerie

Nous arrivons maintenant à la partie amusante, en ajoutant un composant de messagerie avec très peu de code à l'aide du SDK de Sinch. Commencez par ouvrir MessagingViewController.m et importer le framework Sinch.

#importation 

Un peu comme le SINClientDelegate protocole fournit des méthodes utiles qui nous aident à garder une trace du statut du client, la SINMessageClientDelegate Le protocole nous permet de rester informé sur les messages entrants et sortants, leur statut, etc..

Commencez par vous conformer MessagingViewController classe à ce protocole en mettant à jour l'interface de la classe.

@interface MessagingViewController () 

Pour mettre en œuvre la messagerie, nous aurons également besoin d’un SINMessageClient exemple pour gérer les responsabilités de la messagerie. Ajouter une propriété pour le SINMessageClient exemple.

@property (strong, nonatomic) id sinchMessageClient;

Nous pouvons créer une instance d'un client de messagerie à partir du client Sinch que nous avons déjà dans le délégué de l'application. Lorsque le contrôleur de vue est chargé, nous devons en initialiser un et le définir comme délégué. Ajoutez le bloc de code suivant au contrôleur de vue viewDidLoad méthode:

// Configuration du client de message Sinch self.sinchMessageClient = [((AppDelegate *) [[UIApplication sharedApplication]] délégué]). Client messageClient]; self.sinchMessageClient.delegate = self;

Maintenant que nous avons un objet pour envoyer et recevoir des messages et un délégué pour les gérer, ajoutons du code pour composer un message. Trouvez le envoyer le message: méthode au bas du contrôleur de vue et ajoutez l'implémentation suivante:

[clé auto-licenciement]; SINOutgoingMessage * outgoingMessage = [SINOutgoingMessage messageWithRecipient: self.selectedUser.userID text: self.messageTextField.text]; [self.sinchMessageClient sendMessage: message sortant];

UNE SINOutgoingMessage l'instance contactera Sinch et transmettra le message au destinataire. Nous désignons le destinataire en fournissant son identifiant utilisateur à partir du modèle de l'utilisateur. Sinch saura où aller avec le message en le routant via son API en utilisant une combinaison de votre clé d'application, de votre secret et de l'ID utilisateur unique qui lui est transmis..

Comme un message peut être entrant ou sortant, ajoutez une énumération en haut du contrôleur de vue pour prendre en compte l'un ou l'autre scénario. Ajoutez-le en dessous des instructions d'importation.

typedef NS_ENUM (int, MessageDirection) entrant, sortant;

Enfin, nous aurons besoin du client de messagerie pour traiter et envoyer le message. Dans ce contrôleur de vue, nous devrons gérer les envois et recevoir des messages. Ajoutez l'extrait de code suivant pour recevoir un message:

#pragma mark SINMessageClient // Recevoir un message entrant. - (void) messageClient: (id) messageClient didReceiveIncomingMessage: (id) message NSLog (@ "Réception d'un message"); [self.messages addObject: @ [message, @ (entrant)]]; [self.tableView reloadData]; 

Chaque fois que nous recevons un message, nous l'ajouterons au messages tableau et recharger la vue tableau pour l'afficher. Il est important de noter que le SINMessage objet est composé de deux éléments, l'objet de message lui-même et sa direction (entrant ou sortant).

Maintenant que nous pouvons envoyer et recevoir des messages, nous devons savoir quand un message est terminé si nous en sommes l'auteur afin que nous puissions mettre à jour la source de données et recharger la vue tabulaire. Pour ce faire, nous mettons en œuvre le message envoyé: recipientId: comme indiqué ci-dessous.

// Terminer l'envoi d'un message - (void) messageSent: (id) message recipientId: (NSString *) recipientId NSLog (@ "L'envoi d'un message est terminé"); self.messageTextField.text = @ ""; [self.messages addObject: @ [message, @ (sortant)]]; [self.tableView reloadData]; 

Nous pouvons également implémenter certaines des méthodes de délégation restantes pour nous aider à dépanner le processus de messagerie en cas de problème. Ajoutez les méthodes de délégué suivantes sous la message envoyé: recipientId: méthode.

// Impossible d'envoyer un message - (void) messageFailed: (id) info message: (id) messageFailureInfo NSLog (@ "Échec de l'envoi d'un message:% @", messageFailureInfo.error.localizedDescription);  - (void) messageDelivered: (id) info NSLog (@ "Le message a été remis");  - Message (void): (id) message shouldSendPushNotifications: (NSArray *) pushPairs NSLog (@ "Le destinataire n'est pas en ligne. \ nIl faut avertir le destinataire via Push (non implémenté dans ce tutoriel). \ nVeuillez vous reporter à la documentation."); 

À ce stade, la messagerie avec Sinch est prête à être utilisée. La dernière chose à faire est de modifier le tableView: cellForRowAtIndexPath: méthode pour afficher les messages. Voici à quoi devrait ressembler l'implémentation:

- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath UITableViewCell * cell; identifiant message = [self.messages [indexPath.row] firstObject]; MessageDirection direction = (MessageDirection) [[self.messages [indexPath.row] lastObject] intValue]; if (direction == entrante) cell = [self.tableView dequeueReusableCellWithIdentifier: CELL_ID_RECIPIENT]; ((RecipientTableViewCell *) cell) .message.text = message.text;  else cell = [self.tableView dequeueReusableCellWithIdentifier: CELL_ID_USER]; ((UsersTableViewCell *) cell) .message.text = message.text;  cellule de retour; 

9. Tester la messagerie

Générez et exécutez l'application pour vous assurer qu'il n'y a pas d'erreur. Vous êtes prêt à tester la fonctionnalité de messagerie. Pour l'essayer, vous aurez besoin d'accéder à un autre compte Facebook..

Sur votre appareil iOS, créez et exécutez l'application, puis connectez-vous avec le premier compte. Sur le simulateur iOS, exécutez une autre instance de l'application et connectez-vous avec le deuxième compte. Accédez au profil de chacun sur chaque appareil et appuyez sur Message.

À ce stade, vous pouvez entrer un message et appuyer sur Envoyer. Il arrivera presque instantanément sur le périphérique du destinataire. Vous avez fini de mettre en place un client de messagerie fonctionnel pour votre application de rencontre en utilisant la plateforme Sinch..

Si l'utilisateur n'est pas en ligne, le client essaiera d'envoyer le message, mais il ne parviendra pas à l'autre utilisateur. Dans ce cas, vous pouvez implémenter des notifications push pour informer le destinataire qu'il a reçu un message..

10. Appels VoIP

La dernière fonctionnalité pour laquelle nous utiliserons Sinch est l’appel VoIP (Voice over IP). Ouvrir CallingViewController.m et importer le framework Sinch une dernière fois.

#importation 

Comme auparavant, nous utiliserons certains protocoles pour gérer les appels. le SINCallClientDelegate et SINCallDelegate les protocoles nous fournissent des méthodes pour initier et recevoir un appel et être mis à jour sur l'état d'un appel. Mettre à jour l'interface du  CallingViewController classe pour se conformer aux deux protocoles.

@interface CallingViewController () 

Pour mettre en œuvre les appels VoIP, nous avons besoin d’un accès au client Sinch ainsi qu’à un SINCall objet. Ajouter une propriété pour le SINCall exemple comme indiqué ci-dessous.

@property (strong, nonatomic) id sinchCall;

Dans viewDidLoad, définir le contrôleur de vue en tant que délégué du client d'appel Sinch.

// Affecter ce VC en tant que délégué d'appel de Sinch self.delegate = (AppDelegate *) [[UIApplication sharedApplication] delegate]]; self.delegate.client.callClient.delegate = self;

Pour mieux comprendre la section suivante, il est utile de passer en revue les scénarios auxquels les utilisateurs seront confrontés lors de l’utilisation de la fonction d’appel. Un utilisateur pourrait être:

  • recevoir un appel
  • faire un appel
  • raccrocher un appel existant
  • rejeter un appel entrant

Parlons d'abord de l'établissement et de la réception d'un appel. Ajoutez le code suivant au makeOrAcceptCallFromSelectedUser: méthode:

#pragma mark - Lancer ou accepter un appel - (IBAction) makeOAccepetCallFromSelectedUser: (UIButton *) expéditeur if ([expéditeur.titleLabel.text isEqualToString: @ "appel"]) self.sinchCall = [self.delegate.client.callClient callUser : self.selectedUser.userID]; self.sinchCall.delegate = self; self.lblCallStatus.text = [NSString stringWithFormat: @ "- en appelant% @ -", self.selectedUser.userName]; self.btnDeny.hidden = NO; [self.btnDeny setTitle: @ "Raccrocher" pourState: UIControlStateNormal];  else [self.sinchCall answer]; self.lblCallStatus.text = [NSString stringWithFormat: @ "- conversation avec% @ -", self.selectedUser.userName]; 

Nous vérifions si nous passons un appel ou si nous en recevons un. Si nous lançons l'appel, callUserWithId: va commencer le processus d'appel et initialiser le sinchCall propriété.

Si nous répondons à un appel, le sinchCall la propriété sera déjà initialisée et nous commençons simplement l'appel en appelant réponse dessus.

Ensuite, nous allons implémenter le rejet et le raccrochage d’un appel. Ajoutez le code suivant au throwOrHangUpCallFromSelectedUser: méthode:

#pragma mark - Refuser ou raccrocher - (IBAction) rejeterOrHangUpCallFromSelectedUser: (UIButton *) sender [self.sinchCall hangup]; self.lblCallStatus.text = [NSString stringWithFormat: @ "- appel avec% @ terminé -", self.selectedUser.userName]; 

Ce code est plus facile à digérer, car peu importe la situation, il est traité en appelant Raccrocher sur le sinchCall propriété.

Il ne nous reste plus qu'à implémenter les méthodes de délégation requises. Ce sera un peu de code, mais c'est simple à comprendre et les noms de méthodes sont très descriptifs.

#pragma mark - SINCall / Client - Client (void): (id) client didReceiveIncomingCall: (id) call self.sinchCall = call; self.sinchCall.delegate = self; self.lblCallStatus.text = [NSString stringWithFormat: @ "- appel entrant de% @ -", self.selectedUser.userName]; [self.btnAcceptOrCall setTitle: @ "Accepter l'appel" pourState: UIControlStateNormal]; self.btnDeny.hidden = NO;  - (void) callDidEstablish: (id) appelez NSLog (@ "Appel connecté."); self.btnDeny.hidden = NO; [self.btnDeny setTitle: @ "Raccrocher" pourState: UIControlStateNormal]; self.lblCallStatus.text = @ "- appel connecté -"; self.btnAcceptOrCall.hidden = YES;  - (void) callDidEnd: (id) appelez NSLog (@ "Appel terminé"); self.sinchCall = nil; self.lblCallStatus.text = @ "- appel terminé -"; self.btnDeny.hidden = YES; dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t) (1 * NSEC_PER_SEC)), dispatch_get_main_queue (), ^ [self.navigationController popViewControllerAnimated: YES];);  - (void) callDidProgress: (id) call // Cette méthode vous permet de jouer une sonnerie et de mettre à jour ui pour afficher la progression de l'appel. 

La première méthode, didReceiveIncomingCall:, gère la réception d’un appel et la configuration du sinchCall propriété. Nous saurons également quand l'appel commence et se termine à partir de callDidEstablish: et callDidEnd:.

La dernière méthode, callDidProgress:, est vide pour le moment, mais il serait utile de mettre à jour l'interface utilisateur avec des informations utiles, telles que la durée de l'appel. Vous pourriez utiliser SINCallDetail et son heure établie propriété de faire facilement ce calcul.

11. Test Calling

Exécutez deux instances de l'application à l'aide de deux comptes Facebook, comme vous le faisiez auparavant. Accédez au profil de chacun et appuyez sur Appel.

Frappez le vert Appel bouton sur votre appareil ou dans le simulateur iOS. Une fois l'appel envoyé à Sinch, l'interface utilisateur se mettra à jour pour informer l'utilisateur que l'appel est en cours..

De même, si l'un des utilisateurs reçoit un appel, l'interface utilisateur ressemblera à ceci:

Enfin, lorsque l'appel est en cours, l'interface utilisateur reflète l'état de l'appel. Un bouton intitulé Raccrocher sera visible pour mettre fin à l'appel.

Semblable à la messagerie, l'application ne permet pas d'appeler un utilisateur qui n'est pas en ligne. Comme pour la messagerie, vous pouvez résoudre ce problème en utilisant des notifications push pour informer l'utilisateur que quelqu'un a tenté de vous appeler..

12. Où aller ensuite

Plusieurs parties de l'application peuvent être améliorées. Le plus notable est l’intégration des notifications push. Sinch a récemment ajouté un support natif pour les notifications push à son SDK iOS, soulageant ainsi les développeurs. Avec les notifications push, vous pouvez informer les utilisateurs qui n'utilisent pas actuellement l'application qu'ils ont reçu un appel ou un message..

Un autre ajout utile serait de stocker des messages et des conversations sur un serveur distant. De cette façon, les utilisateurs peuvent remonter dans le temps et visionner les conversations passées qu'ils ont eues. Cela rend également l'expérience de messagerie plus fluide puisqu'ils pourraient reprendre une conversation là où ils l'avaient laissée..

Ce ne sont que quelques idées. Je suis sûr que vous pouvez penser à quelques autres applications qui rendraient l'application plus complète et plus attrayante..

Conclusion

Le puissant SDK de Sinch rend deux tâches intimidantes accessibles, simples et abordables. Comme vous l'avez vu dans ce didacticiel, Sinch permet aux développeurs d'intégrer la messagerie et l'appel dans leurs applications en quelques minutes. Le SDK Sinch n'est pas limité à iOS. Si votre application de rencontre a un composant de site Web, par exemple, vous pouvez utiliser son API JavaScript pour implémenter les mêmes fonctionnalités..

Si vous êtes bloqué en cours de route, ne vous inquiétez pas, vous pouvez trouver le projet terminé sur GitHub. Vous pouvez maintenant prendre ce que vous avez appris dans ce tutoriel et créer le prochain Tinder..