Bienvenue dans le troisième volet de notre série sur la création d’un client Jabber avec le SDK iOS. Dans ce tutoriel, nous allons ajouter des fonctionnalités XMPP au délégué d'application. Le fait de placer les fonctionnalités dans le délégué d’application nous permettra d’accéder facilement aux fonctionnalités XMPP de n’importe où dans l’application..
Comme indiqué précédemment, l'insertion de la fonctionnalité XMPP dans le délégué d'application constitue un excellent moyen de rendre cette fonctionnalité facilement accessible dans l'application. À tout endroit du code de votre application, vous pouvez accéder au délégué d'application avec l'extrait de code suivant:
[Délégué [[UIApplication sharedApplication]]]
La classe XMPP répartira les événements au moyen de protocoles que nous définirons ci-dessous. Voici la liste des événements gérés par cette classe:
Commençons par ajouter la propriété some au délégué de l'application. Nous devons d’abord importer des éléments XMPP dans l’en-tête:
#import "XMPP.h"
C’est l’ensemble minimal de classes nécessaire à la construction de notre application. Si vous souhaitez approfondir quelque chose de plus complexe, vous pouvez consulter l'exemple fourni avec le référentiel de bibliothèque XMPP. Voici notre première implémentation de cette classe:
@class SMBuddyListViewController; @interface jabberClientAppDelegate: NSObject UIWindow * window; SMBuddyListViewController * viewController; XMPPStream * xmppStream; NSString * mot de passe; BOOL isOpen; @property (nonatomic, keep) IBOutlet UIWindow * fenêtre; @property (nonatomic, keep) IBOutlet SMBuddyListViewController * viewController; @property (nonatomic, en lecture seule) XMPPStream * xmppStream; - (BOOL) connect; - (vide) déconnecter; @fin
XMPPStream
sera le barebone de notre système de communication client-serveur et tous les messages y seront échangés. Nous définirons également les méthodes pour gérer la connexion et la déconnexion. L'implémentation de cette classe est assez complexe, nous allons donc la décomposer en plusieurs étapes. Premièrement, nous avons besoin de quelques méthodes supplémentaires pour gérer les communications client-serveur. Ceux-ci peuvent être privés, nous les plaçons donc dans l'implémentation de la classe:
@interface JabberClientAppDelegate () - (void) setupStream; - (vide) goOnline; - (void) goOffline; @end @implementation JabberClientAppDelegate @end
Ici, le plus important est setupStream
, qui crée le canal pour gérer l'échange de messages.
- (void) setupStream xmppStream = [[XMPPStream alloc] init]; [xmppStream addDelegate: self delegateQueue: dispatch_get_main_queue ()];
Juste deux lignes de code, mais derrière cela se passe beaucoup de choses. le dispatch_get_main_queue ()
est une fonction qui renvoie une référence
au mécanisme d’exécution asynchrone au niveau du système, auquel nous pouvons attribuer des tâches et recevoir des notifications. Ici on dit "simplement"
que notre classe est le délégué des notifications envoyées depuis la file d'attente principale, qui est exécutée dans le thread principal de notre application. Voir ici pour plus de détails sur Grand Central Dispatch.
Les fonctions hors ligne et en ligne peuvent informer les autres utilisateurs lorsque nous sommes connectés ou non. Ils sont définis en envoyant un XMPPPrésence
objet à travers la prise. Le serveur enverra la notification en conséquence.
- (void) goOnline XMPPPresence * presence = [présence XMPPPresence]; [[self xmppStream] sendElement: presence]; - (void) goOffline XMPPPresence * presence = [XMPPPresence presenceWithType: @ "indisponible"]; [[self xmppStream] sendElement: presence];
le relier
La méthode est la plus importante, car elle gère l’opération de connexion. Il retourne un booléen indiquant si la connexion a réussi ou non. Au début, il installe le flux, puis il utilise les données stockées dans NSUserDefaults
pour décorer le flux et appeler un message de connexion. Une vue d'alerte est affichée si la connexion n'aboutit pas.
- (BOOL) connect [auto setupStream]; NSString * jabberID = [[NSUserDefaults standardUserDefaults] stringForKey: @ "userID"]; NSString * myPassword = [[NSUserDefaults standardUserDefaults] stringForKey: @ "userPassword"]; if (! [xmppStream isDisconnected]) return YES; if (jabberID == nil || myPassword == nil) return NO; [xmppStream setMyJID: [XMPPJID jidWithString: jabberID]]; mot de passe = monPassword; NSError * error = nil; if (! [xmppStream connect: & error]) UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ "error" message: [NSString stringWithFormat: @ "Impossible de se connecter au serveur% @", [error localizedDescription]] délégué. : nil cancelButtonTitle: @ "Ok" otherButtonTitles: nil]; [alertView show]; [version alertView]; retourne NO; retourne OUI;
Par souci d'exhaustivité, nous implémentons également la méthode de déconnexion qui est définie comme suit:
- (void) disconnect [self goOffline]; [xmppStream disconnect];
Maintenant que nous disposons de certaines des fonctions de base, nous pouvons les utiliser dans des cas spécifiques, par exemple, lorsque l'application devient active ou inactive..
- (void) applicationWillResignActive: (UIApplication *) application [self disconnect]; - (void) applicationDidBecomeActive: (UIApplication *) application [connexion auto];
Nous nous retrouvons avec le noyau du système, les notifications d'événements et les comportements associés, que nous mettons en œuvre au moyen de protocoles.
Nous définirons deux protocoles, l'un pour les notifications de discussion, comme "un ami est déconnecté", et l'autre pour l'envoi des messages reçus. Le premier protocole comprend la description de trois événements:
@protocol SMChatDelegate - (void) newBuddyOnline: (NSString *) buddyName; - (void) buddyWentOffline: (NSString *) buddyName; - (vide) didDisconnect; @fin
Les deux premiers messages sont liés à la présence d'un copain. Nous réagirons en ajoutant ou en supprimant des éléments dans le tableau des contacts en ligne. La troisième ne fait que notifier le serveur lorsque notre client se déconnecte. Le second protocole est plus simple, car il ne gère que les événements de réception de message.
@protocol SMMessageDelegate - (void) newMessageReceived: (NSDictionary *) messageContent; @fin
Par souci de simplicité, pour représenter le message, nous allons utiliser un dictionnaire avec deux clés, @ "msg" et @ "expéditeur", pour représenter le message réel et l'expéditeur réel..
Les deux protocoles distribuent des messages du UIApplicationDelegate
. Nous étendons donc notre classe principale en ajoutant deux propriétés (une pour chaque délégué).
@interface JabberClientAppDelegate: NSObject ? __weak NSObject * _chatDelegate; __weak NSObject * _messageDelegate; @property (nonatomic, assign) id _chatDelegate; @property (nonatomic, assign) id _messageDelegate; @fin
Dans l'implémentation, nous devrions nous rappeler de synthétiser ces propriétés.
@synthesize _chatDelegate, _messageDelegate;
Notre classe principale est maintenant prête à envoyer des événements aux délégués. Mais quels événements? Ceux reçus du Grand Central Dispatch. Si tu te souviens,
nous avons configuré notre UIApplicationDelegate
en tant que délégué pour les messages de flux. Ces délégués ont les signatures suivantes. Les noms
sont assez explicites, mais nous avons ajouté des commentaires pour le rendre encore plus clair.
- (void) xmppStreamDidConnect: (XMPPStream *) expéditeur // connexion au serveur établie - (void) xmppStreamDidAuthenticate: (XMPPStream *) expéditeur // authentification réussie - (void) xmppStream: (XMPPStream *) sender didReceiveMessender: XMPPMessage *) message // message reçu - (void) xmppStream: expéditeur (XMPPStream *) expéditeur didReceivePresence: (XMPPPresence *) présence // un ami s'est déconnecté / en ligne
Commençons par l'authentification lorsque nous nous connectons au serveur.
- (void) xmppStreamDidConnect: (XMPPStream *) expéditeur isOpen = YES; NSError * error = nil; [[self xmppStream] authenticateWithPassword: erreur du mot de passe: & error];
Lorsque l'authentification est réussie, nous devons informer le serveur que nous sommes en ligne..
- (void) xmppStreamDidAuthenticate: (XMPPStream *) sender [self goOnline];
Lorsque nous recevons une notification de présence, nous pouvons envoyer le message au délégué de chat..
- (void) xmppStream: (XMPPStream *) expéditeur didReceivePresence: (XMPPPresence *) presence NSString * presenceType = [type de présence]; // online / offline NSString * myUsername = [utilisateur de l'expéditeur [jJJ]]; NSString * presenceFromUser = [[présence de] utilisateur]; if (! [presenceFromUser isEqualToString: monUsername]) if ([presenceType isEqualToString: @ "disponible"]) [_ _ChatDelegate newBuddyOnline: [NSString chaineWithFormat: @ "% @@% @", presenceFromUser, @ "j ]; else if ([presenceType isEqualToString: @ "indisponible"]) [_chatDelegate buddyWentOffline: [NSString stringWithFormat: @ "% @@% @", presenceFromUser, @ "jerry.local"]];
Le délégué utilisera ces événements pour renseigner le tableau des amis en ligne en conséquence (voir ci-dessous). Enfin, il nous reste le message reçu notification.
- (void) xmppStream: (XMPPStream *) expéditeur didReceiveMessage: (XMPPMessage *) message NSString * msg = [[message elementForName: @ "body"] stringValue]; NSString * from = [[message attributForName: @ "from"] stringValue]; NSMutableDictionary * m = [[NSMutableDictionary alloc] init]; [m setObject: msg forKey: @ "msg"]; [m setObject: from forKey: @ "sender"]; [_messageDelegate newMessageReceived: m]; [m release];
Dans ce cas, nous construisons un dictionnaire comme demandé par le protocole et nous appelons la méthode correspondante. À ce stade, le cœur de notre système est prêt. Il suffit de faire réagir les composants de l'interface utilisateur en conséquence.
Nous commençons par modifier le contrôleur de liste d'amis, qui gère la première vue affichée au démarrage de l'application. Nous ajoutons le délégué de discussion à l'interface comme suit:
@interface SMBuddyListViewController: UIViewController , SMChatDelegate> @fin
Nous ajoutons quelques méthodes d'accès pour pointer vers le délégué d'application et le flux:
- (JabberClientAppDelegate *) appDelegate return (JabberClientAppDelegate *) [délégué UIApplication sharedApplication]]; - (XMPPStream *) xmppStream return [[self appDelegate] xmppStream];
Nous devons également étendre la viewDidLoad
message pour définir notre contrôleur de vue en tant que délégué pour le protocole de discussion.
- (void) viewDidLoad ? JabberClientAppDelegate * del = [self appDelegate]; del._chatDelegate = self;
Lorsque la vue apparaît, si les informations d'identification ont déjà été entrées, nous appelons la méthode de connexion du délégué d'application:
- (void) viewDidAppear: (BOOL) animated [super viewDidAppear: animated]; NSString * login = [[NSUserDefaults standardUserDefaults] objectForKey: @ "userID"]; if (login) if ([[auto appDelegate] connect]]) NSLog (@ "afficher la liste des contacts"); else [self showLogin];
Enfin, nous devons ajouter ou supprimer des objets du tableau des contacts en ligne en fonction des événements envoyés par le délégué de l'application..
- (void) newBuddyOnline: (NSString *) buddyName [onlineBuddies addObject: buddyName]; [self.tView reloadData]; - (void) buddyWentOffline: (NSString *) buddyName [onlineBuddies removeObject: buddyName]; [self.tView reloadData];
Si vous exécutez l'application maintenant et qu'un ami est en ligne, la vue du tableau est renseignée avec son nom d'utilisateur, comme dans la figure suivante:
Note importante: Selon les paramètres du serveur, vous devrez peut-être attendre un certain temps avant de recevoir les notifications du "nouveau contact est en ligne". Ce temps a tendance à être de 20 à 60 secondes.
Pour démarrer une discussion avec l'utilisateur, nous devons afficher la vue de discussion lorsque la cellule correspondante est activée..
- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath NSString * userName = (NSString *) [onlineBuddies objectAtIndex: indexPath.row]; SMChatViewController * chatController = [[SMChatViewController alloc] initWithUser: nomutilisateur]; [self presentModalViewController: chatController animé: OUI];
Pour finaliser l'application, nous devons ajouter l'implémentation du délégué de message au contrôleur d'affichage de discussion. Les étapes à suivre sont similaires à celles appliquées au contrôleur de liste d'amis. Nous ajoutons le délégué dans le fichier d'interface:
@interface SMChatViewController: UIViewController , SMMessageDelegate> @fin
Nous ajoutons des accesseurs à l'implémentation:
- (JabberClientAppDelegate *) appDelegate return (JabberClientAppDelegate *) [délégué UIApplication sharedApplication]]; - (XMPPStream *) xmppStream return [[self appDelegate] xmppStream];
Nous ajoutons la mise en œuvre de initWithUser: nom d'utilisateur
:
- (id) initWithUser: (NSString *) userName if (self = [super init]) chatWithUser = userName; retourner soi-même;
Nous étendons la viewDidLoad
pour déclarer le délégué au message et définir le champ de texte comme premier répondant à la saisie au clavier:
- (void) viewDidLoad ? JabberClientAppDelegate * del = [self appDelegate]; del._messageDelegate = self; [self.messageField devient le premier répondeur];
Pour envoyer un message, nous devons créer un élément xml conformément au protocole XMPP et l’envoyer par le flux. Voici comment nous mettons à jour le envoyer le message
méthode:
- (IBAction) sendMessage NSString * messageStr = self.messageField.text; if ([messageStr length]> 0) NSXMLElement * body = [NSXMLElement elementWithName: @ "body"]; [body setStringValue: messageStr]; NSXMLElement * message = [NSXMLElement elementWithName: @ "message"]; [message addAttributeWithName: @ "type" stringValue: @ "chat"]; [message addAttributeWithName: @ "to" stringValue: chatWithUser]; [message addChild: body]; [self.xmppStream sendElement: message]; self.messageField.text = @ ""; NSString * m = [NSString stringWithFormat: @ "% @:% @", messageStr, @ "you"]; NSMutableDictionary * m = [[NSMutableDictionary alloc] init]; [m setObject: messageStr forKey: @ "msg"]; [m setObject: @ "you" forKey: @ "sender"]; [messages addObject: m]; [self.tView reloadData]; [m release];
Nous avons maintenant terminé! Vous pouvez tester la mise en œuvre finale de notre client iOS. Nous démarrons le serveur, iChat et notre client Jabber. Après un certain temps, les deux clients devraient recevoir un avis de présence et se reconnaître comme étant en ligne. Sur l'iPhone, nous tapotons sur le contact en ligne et la vue de discussion apparaît. Nous sommes maintenant prêts à discuter. Voici une capture d'écran de l'application finale au travail.
Le code source complet de ce projet est disponible sur GitHub ici.