Time went by quickly. I worked on IM apps (using XMPP) in my first iOS job, and I’ve forgotten all about it. Use your free time to rewrite the little Demo just as a review.

I will not introduce the principle, the server installation and configuration can refer toMysql and OpenFire environment configuration for XMPPorConfiguration introduction

Environment configuration from Chen Huaizhe first from JaneLet’s go!

The Demo address I made is:gitHubThere are a lot of bugs in it don’t take it too seriously

Here’s a flow chart:

Need to introduce

JID

The address of an XMPP is called a JabberID (JID for short), which identifies the XMPP entities in the XMPP network. The JID consists of three parts: domain, Node Identifier, and Resource. Domain is an essential part of the JID. Note: The domain and user sections are case insensitive, but resource is case sensitive.

jid = [ node "@" ] domain [ "/" resource ]  
domain = fqdn / address-literal  
fqdn = (sub-domain 1*("." sub-domain))  
sub-domain = (internationalized domain label)  
address-literal = IPv4address / IPv6address  
Copy the code

Domain: indicates the gateway or server on the network.

Node Identifier: Usually represents an entity that requests and uses network services from a server or gateway (such as a client), but it can also represent other entities (such as a room in a multi-user chat system).

Resource: Typically represents a specific session (with a device), a connection (with an address), or an object attached to an entity related to a node ID entity (such as a participant in a multi-user chat room).

JID types are:

Bare JID: [email protected] Full JID: [email protected]/ ResourceCopy the code

Example:

[email protected]: indicates user stpeter on the jabber.org server.

room@service: A specific chat room designed to provide a multi-user chat service. Where “room” is the name of the chat room and “service” is the host name of the multi-user chat service.

room@service/ Nick: address of user Nick who joined the chat room. Here “room” is the name of the chat room, “service” is the host name of the multi-user chat service, and “Nick” is the nickname of the user in the chat room.

XMPP also has its own URI to identify the JID, such as XMPP :[email protected], which is preceded by XMPP: by default.

Communication primitives

There are three XMPP communication primitives: Message, Presence, and IQ.

5.1 the message

Message is a basic push message method that does not require a response. It is mainly used in IM, groupChat, Alert, and Notification applications.

The main attributes are as follows:

5.1.1 Type attribute, which has five main types:

Normal: Similar to email, it does not require any response.

Chat: Similar to instant messaging with friends in QQ, featuring real-time communication;

4. Groupchat: a groupchat similar to a chat room;

Headline: for sending alert and notification;

Error: If an error occurs in sending a message, the entity that discovered the error uses this category to notify the sender of the error;

5.1.2 To property: Identifies the receiver of the message.

5.1.3 FROM attribute: Refers to the name or identifier of the sender. To prevent address leakage, this address is usually filled in by the sender’s server, not the sender.

Payload: for example, body and subject

Example:

<message  
  to="[email protected]/contact"  
  type="chat"> <body> Hello, are you busy </body> </message>Copy the code

5.2 presence

Presence indicates the user status, for example, online, Away, and DND. When you change your state, you insert a Presence element in the context of the stream to indicate your state. To receive presence messages, you must go through an authorization process called Presence Subscription.

5.2.1 attributes:

5.2.1.1 Type Attribute Optional. There are the following categories

Subscribe: Indicates the status of subscribing to other users

Probe: requests to obtain the status of another user

Unavailable: Indicates that it is unavailable or offline

5.2.1.2 TO Property: Identifies the receiver of the message.

5.2.1.3 FROM Property: Indicates the name or identifier of the sender.

5.2.2 Payload:

5.2.2.1 show:

Chat: indicates a chat

-Blair: I’m away

Xa: eXtend Away, for a long time

DND: Don’t disturb

5.2.2.2 STATUS: Free-format, readable text. Also known as rich Presence or extended Presence, this can be used to indicate a user’s current mood, activity, song, video, chat room, web page, game, etc.

5.2.2.3 priority: Ranges from -128 to 127. High priority Resources can receive messages sent to bare JID, low priority resources cannot. A resource with a negative priority cannot receive a message sent to bare JID.

Example:

<presence from="[email protected]/pda"> <show>xa</show> <status>down the rabbit hole! </status> </presence>Copy the code

5.3 IQ (Info/Query)

A request/response mechanism whereby a request is sent from one entity and received and responded to by another entity. For example, if the client inserts an element in the context of a stream and asks the Server for its friends list, the Server returns one with the result of the request.

The main attribute is Type. Include:

Get: obtains the current field value. Similar to HTTP GET methods.

Set: Sets or replaces the value of the GET query. Similar to the HTTP PUT method.

Result: Indicates that the previous query was successfully responded. Similar to HTTP status code 200.

Error: Errors in the query and response.

Example:

<iq from="[email protected]/pda"  
    id="rr82a1z7" 
    to="[email protected]"  
    type="get"> 
  <query xmlns="jabber:iq:roster"/> 
</iq> 
Copy the code

XMPPFramework structure and core classes

Before moving on to the next step, let’s talk about the XMPPFramework’s directory structure to make it easier for beginners to read the article. Take a look at the picture below:

Although there are a number of directories, most of our development focus is on classes in the Core and Extensions directories. What are the directories mainly used for?

Authentication: this name is associated with Authentication.

Categories: Mainly some extensions, especially the NSXMLElement+XMPP extension are required.

Core: This is the XMPP Core file directory, which is where we’ll focus most of our attention.

Extensions: This directory is an extension of XMPP that extends various protocols and separate functions, with each subdirectory being a separate subfunction. The most commonly used features are Reconnect, Roster, CoreDataStorage, and others.

Utilities: These are helper classes that we developers don’t care about.

Vendor: This directory is the third party library referenced by XMPP, such as CocoaAsyncSocket, KissXML, etc. We don’t need to worry about that either.

Reading this, you have an understanding of the XMPPFramework’s structure.

The concept of knowledge

Login requires an account, which is actually a user unique identifier (JID), represented by the XMPPJID class in XMPP. So what does a user unique Identifier (JID) consist of?

The JID consists of the user name, domain name, and resource name. The format of the JID is user@domain/resource, for example, [email protected] /Anthony. Correspond to the three attributes user, Domain, and resource in the XMPPJID class.

If the HOST name is not set, the domain of the JID is used as the HOST name, and the port number is optional. The default is 5222 and there is generally no need to change it.

XMPPStream class

We want to connect to the server, we must use XMPPStream class, which provides a lot of API and property Settings, through socket implementation. Did we see the Verdor directory, which contains CocoaAsyncSocket, a well-known socket programming library? The XMPPStream class also complies with and implements the GCDAsyncSocketDelegate proxy for client-server interaction.

@interface XMPPStream : NSObject <GCDAsyncSocketDelegate>
Copy the code

After creating an XMPPStream object, we need to set the proxy to call back our proxy method. This is supported multicast delegate, which means that multiple proxy objects can be set for an XMPPStream object. The protocol is XMPPStreamDelegate:

- (void)addDelegate:(id)delegatedelegateQueue:(dispatch_queue_t)delegateQueue;
Copy the code

When we don’t want an XMPPStream object to continue receiving proxy callbacks, we remove the proxy by:

- (void)removeDelegate:(id)delegatedelegateQueue:(dispatch_queue_t)delegateQueue;
- (void)removeDelegate:(id)delegate;
Copy the code

Next, we’ll set the host and port by setting these two properties:

/**
* The server'hostname that should be used to make the TCP connection. This property is optional. If no host is set, the default is domain */ @property (readwrite, copy) NSString *hostName; /** * The port the xmpp server is running on. * If you do not explicitly set the port, the default port will be used. * If you set the port to zero, the default port will be used. * * The default port is 5222. **/ @property (readwrite, assign) UInt16 hostPort;Copy the code

XMPPStream has an XMPPJID class object as an attribute to identify the user, since we’ll need myJID for many of our subsequent operations:

@property (readwrite, copy) XMPPJID *myJID;
Copy the code

The management of the user’s online status is handed over to the XMPPPresence class, which is also used as XMPPStream attributes, combined into XMPPStream, a lot of subsequent operations about the user is needed to handle the user state:

/**
* Represents the last sent presence element concerning the presence of myJID on the server.
* In other words, it represents the presence as others see us.
*
* This excludes presence elements sent concerning subscriptions, MUC rooms, etc.
*
* @see resendMyPresence
**/
@property (strong, readonly) XMPPPresence *myPresence;
Copy the code

XMPPStreamDelegate

This protocol is critical, and a lot of our work is focused on proxy callbacks for this protocol. It is divided into several types of proxy apis, such as authorized, registered, secure, and so on:

- (void)xmppStreamWillConnect:(XMPPStream *)sender; // This proxy method is called when the TCP socket is already connected to the remote host. // If the App needs to run in the background, set XMPPStream's enableBackgroundingOnSocket attributes - (void) xmppStream: (xmppStream *) sendersocketDidConnect: GCDAsyncSocket *) socket; / / when the TCP connection with the server will be back after the proxy method - (void) xmppStreamDidStartNegotiation (XMPPStream *) sender; // The TLS transport layer protocol will be used to verify the security Settings when the callback // Settings will be passed to startTLS. Need to add in the Settings GCDAsyncSocketManuallyEvaluateTrust = YES / / - (void) xmppStream: (xmppStream *)senderwillSecureWithSettings:(NSMutableDictionary *)settings; // After the above method is executed, The next step is to execute the proxy callback // used to manually verify that the TCP handshake is trusted - (void)xmppStream:(xmppStream *)senderdidReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler; - (void)xmppStreamDidSecure:(XMPPStream *)sender; This proxy method is called back when the XML stream is fully open (that is, when the connection to the server is complete). You can now safely communicate with the server. - (void)xmppStreamDidConnect:(XMPPStream *)sender; - (void)xmppStreamDidRegister:(XMPPStream *)sender; - (void)xmppStream:(xmppStream *)senderdidNotRegister:(NSXMLElement *)error; / / authorization by the callback, namely login successful callback - (void) xmppStreamDidAuthenticate (XMPPStream *) sender; / / authorization failed callback, namely login failed callback - (void) xmppStream: (xmppStream *) senderdidNotAuthenticate (NSXMLElement *) error; // The callback to bind JID Resource, which is a standard part of the authorizer. When the JID user name is verified, the next step is to validate the resource. If standard binding is used, return nil or do not implement this method - (id 
      
       )xmppStreamWillBind:(XMPPStream *)sender; // This proxy method is called back if the server does not allow resource selection due to a resouce conflict. Return the specified resource or return nil and let the server automatically help us choose. You don't usually implement it. - (NSString *)xmppStream:(XMPPStream *)senderalternativeResourceForConflictingResource:(NSString *)conflictingResource; // callback when IQ (message query) will be sent - (XMPPIQ *)xmppStream:(xmppStream *)senderwillReceiveIQ:(XMPPIQ *) IQ; / / will receive the message of the callback - (XMPPMessage *) xmppStream: (xmppStream *) senderwillReceiveMessage (XMPPMessage *) message; / / will receive a user online callback - (XMPPPresence *) xmppStream: (xmppStream *) senderwillReceivePresence (XMPPPresence *) presence; /** * This method is called if any of the xmppStream:willReceiveX: methods filter the incoming stanza. * * It may be useful for some extensions to know that something was received, **/ / When xmppStream:willReceiveX filters stanza, this proxy method will be called back. // By implementing this proxy method, you can know the reason for filtering, which is helpful. - (void)xmppStreamDidFilterStanza:(XMPPStream *)sender; // this proxy method is called back after receiving IQ (message query) - (BOOL)xmppStream:(xmppStream *)senderdidReceiveIQ:(XMPPIQ *) IQ; // this proxy method is called after receiving a message - (void)xmppStream:(xmppStream *)senderdidReceiveMessage:(XMPPMessage *)message; / / after receiving the user online status messages to callback the proxy method - (void) xmppStream: (xmppStream *) senderdidReceivePresence (XMPPPresence *) presence; - (void)xmppStream:(xmppStream *)senderdidReceiveError:(NSXMLElement *)error; // this proxy method will be called back when IQ (message query) is about to be sent - (XMPPIQ *)xmppStream:(xmppStream *)senderwillSendIQ:(XMPPIQ *) IQ; // this proxy method is called when a message is about to be sent - (XMPPMessage *)xmppStream:(xmppStream *)senderwillSendMessage:(XMPPMessage *)message; // this method is called when a user presence message is to be sent - (XMPPPresence *)xmppStream:(xmppStream *)senderwillSendPresence:(XMPPPresence *)presence; - (void)xmppStream:(xmppStream *)senderdidSendIQ:(XMPPIQ *) IQ; // after sending a message successfully, this proxy method is called - (void)xmppStream:(xmppStream *)senderdidSendMessage:(XMPPMessage *)message; - (void)xmppStream:(xmppStream *)senderdidSendPresence:(XMPPPresence *)presence; - (void)xmppStream:(xmppStream *)senderdidFailToSendIQ:(XMPPIQ *)iqerror:(NSError *)error; // After sending a message fails, Agents to callback the method - (void) xmppStream: (xmppStream senderdidFailToSendMessage: (*) XMPPMessage *) messageerror (NSError *) error; // After sending the user online status failure message, Will be back this method - (void) xmppStream: (xmppStream senderdidFailToSendPresence: (*) XMPPPresence *) presenceerror (NSError *) error; / / when changed the JID information, agents will callback this method - (void) xmppStreamDidChangeMyJID (XMPPStream XMPPStream *); / / when the Stream was told disconnected with the server callback agents this method - (void) xmppStreamWasToldToDisconnect (XMPPStream *) sender; / / sent when < / stream: the stream > nodes, the agents will callback this method - (void) xmppStreamDidSendClosingStreamStanza (XMPPStream *) sender; / / connection timeout callback when the proxy method - (void) xmppStreamConnectDidTimeout (XMPPStream *) sender; // this proxy method is called when disconnecting from the server - (void)xmppStreamDidDisconnect:(XMPPStream *)senderwithError:(NSError *)error; / / p2p type - (void) xmppStream: (xmppStream *) senderdidReceiveP2PFeatures (streamFeatures NSXMLElement *); - (void)xmppStream:(XMPPStream *)senderwillSendP2PFeatures:(NSXMLElement *)streamFeatures; - (void)xmppStream:(XMPPStream *)senderdidRegisterModule:(id)module; - (void)xmppStream:(XMPPStream *)senderwillUnregisterModule:(id)module; // This proxy method is called back when non-XMPP element nodes are sent. That is, if the element sent is not // < IQ >, 
       
         or 
        
         , Agents will be callback the method - (void) xmppStream: (xmppStream *) senderdidSendCustomElement (NSXMLElement *) element; // This proxy method is called back when a non-XMPP element node is received. That is, if the element received is not // < IQ >, 
         
           or 
          
           , Agents will be callback the method - (void) xmppStream: (xmppStream *) senderdidReceiveCustomElement (NSXMLElement *) element;
          
         
        
       
      Copy the code

To this, also understood XMPPStream 5566 bar!!

XMPPIQ

Message query (IQ) is handled through this class. XMPP gives us iQ-easy classes to create for quickly generating XML data. If the header file is declared as follows:

@interfacexmppIQ: XMPPElement // Generate IQ + (XMPPIQ *) IQ; + (XMPPIQ *)iqWithType:(NSString *)type;
+ (XMPPIQ *)iqWithType:(NSString *)typeto:(XMPPJID *)jid;
+ (XMPPIQ *)iqWithType:(NSString *)typeto:(XMPPJID *)jidelementID:(NSString *)eid;
+ (XMPPIQ *)iqWithType:(NSString *)typeto:(XMPPJID *)jidelementID:(NSString *)eidchild:(NSXMLElement *)childElement;
+ (XMPPIQ *)iqWithType:(NSString *)typeelementID:(NSString *)eid;
+ (XMPPIQ *)iqWithType:(NSString *)typeelementID:(NSString *)eidchild:(NSXMLElement *)childElement;
+ (XMPPIQ *)iqWithType:(NSString *)typechild:(NSXMLElement *)childElement;
 
- (id)init;
- (id)initWithType:(NSString *)type;
- (id)initWithType:(NSString *)typeto:(XMPPJID *)jid;
- (id)initWithType:(NSString *)typeto:(XMPPJID *)jidelementID:(NSString *)eid;
- (id)initWithType:(NSString *)typeto:(XMPPJID *)jidelementID:(NSString *)eidchild:(NSXMLElement *)childElement;
- (id)initWithType:(NSString *)typeelementID:(NSString *)eid;
- (id)initWithType:(NSString *)typeelementID:(NSString *)eidchild:(NSXMLElement *)childElement;
- (id)initWithType:(NSString *)typechild:(NSXMLElement *)childElement;
 
// IQ类型,看下面的说明
- (NSString *)type; / / determinetypeType - (BOOL) isGetIQ; - (BOOL)isSetIQ; - (BOOL)isResultIQ; - (BOOL)isErrorIQ; / / whentypeTo get orsetThe API is useful for specifying whether a response is required. - (BOOL)requiresResponse; - (NSXMLElement *)childElement; - (NSXMLElement *)childErrorElement; @endCopy the code

IQ is a request/response mechanism where a request is sent from one entity and received and responded to by another entity. For example, if the client inserts an element in the context of a stream and asks the Server for its friends list, the Server returns one with the result of the request.

There are the following categories (optional Settings such as get) :

Get: obtains the current field value. Similar to HTTP GET methods.

Set: Sets or replaces the value of the GET query. Similar to the HTTP PUT method.

Result: Indicates that the previous query was successfully responded. Similar to HTTP status code 200.

Error: Errors in the query and response.

Here is an example of IQ:

<iqfrom="[email protected]/ios"  
    id="xxxxxxx" 
    to="[email protected]/ios"  
    type="get"> 
  <queryxmlns="jabber:iq:roster"/> 
</iq> 
Copy the code

XMPPPresence

This class represents nodes, and we generate XML data through methods provided by this class. It represents the user’s online status, and its header has very little content:

@interfaceXMPPPresence: XMPPElement
 
// Converts an NSXMLElement to an XMPPPresence element in place (no memory allocations or copying)
+ (XMPPPresence *)presenceFromElement:(NSXMLElement *)element;
 
+ (XMPPPresence *)presence;
+ (XMPPPresence *)presenceWithType:(NSString *)type;
// type: User online status // to: receiver JID + (XMPPPresence *)presenceWithType:(NSString *) typeTO :(XMPPJID *)to; - (id)init; - (id)initWithType:(NSString *)type;
 
// type: User online status // to: receiver JID - (id)initWithType:(NSString *) TypeTO :(XMPPJID *)to; - (NSString *)type;
 
- (NSString *)show;
- (NSString *)status;
 
- (int)priority;
 
- (int)intShow;
 
- (BOOL)isErrorPresence;
 
@end
Copy the code

Presence indicates the user status, for example, online, Away, and DND. When you change your state, you insert a Presence element in the context of the stream to indicate your state. To receive presence messages, you must go through an authorization process called Presence Subscription.

There are the following categories (optional Settings such as subscribe) :

Subscribe: Indicates the status of subscribing to other users

Probe: requests to obtain the status of another user

Unavailable: Indicates that it is unavailable or offline

Nodes have the following types, such as DND:

Chat: indicates a chat

-Blair: I’m away

Xa: eXtend Away, for a long time

DND: Don’t disturb

node

This node represents state information, and the content is relatively free, which can be almost any type of content. It is often used to indicate the user’s current mood, activities, songs listened to, videos watched, chat rooms, websites visited, games played and so on.

node

– the range of 128-127. High priority Resources can receive messages sent to bare JID, low priority resources cannot. A resource with a negative priority cannot receive a message sent to bare JID.

Send an example of a user’s online status:

<presencefrom="[email protected]/pda"> <show> DND </show> <status> Browser search: standard brother's technical blog, or directly visit www.henishuo.com</status> </presence>Copy the code

XMPPMessage

XMPPMessage is the data provided by the XMPP framework for generating XML messages. Its header file is as follows:

@interfaceXMPPMessage: XMPPElement
 
+ (XMPPMessage *)messageFromElement:(NSXMLElement *)element;
 
+ (XMPPMessage *)message;
+ (XMPPMessage *)messageWithType:(NSString *)type;
+ (XMPPMessage *)messageWithType:(NSString *)typeto:(XMPPJID *)to;
+ (XMPPMessage *)messageWithType:(NSString *)typeto:(XMPPJID *)jidelementID:(NSString *)eid;
+ (XMPPMessage *)messageWithType:(NSString *)typeto:(XMPPJID *)jidelementID:(NSString *)eidchild:(NSXMLElement *)childElement;
+ (XMPPMessage *)messageWithType:(NSString *)typeelementID:(NSString *)eid;
+ (XMPPMessage *)messageWithType:(NSString *)typeelementID:(NSString *)eidchild:(NSXMLElement *)childElement;
+ (XMPPMessage *)messageWithType:(NSString *)typechild:(NSXMLElement *)childElement;
 
- (id)init;
- (id)initWithType:(NSString *)type;
- (id)initWithType:(NSString *)typeto:(XMPPJID *)to;
- (id)initWithType:(NSString *)typeto:(XMPPJID *)jidelementID:(NSString *)eid;
- (id)initWithType:(NSString *)typeto:(XMPPJID *)jidelementID:(NSString *)eidchild:(NSXMLElement *)childElement;
- (id)initWithType:(NSString *)typeelementID:(NSString *)eid;
- (id)initWithType:(NSString *)typeelementID:(NSString *)eidchild:(NSXMLElement *)childElement;
- (id)initWithType:(NSString *)typechild:(NSXMLElement *)childElement;
 
- (NSString *)type;
- (NSString *)subject;
- (NSString *)body;
- (NSString *)bodyForLanguage:(NSString *)language;
- (NSString *)thread;
 
- (void)addSubject:(NSString *)subject;
- (void)addBody:(NSString *)body;
- (void)addBody:(NSString *)bodywithLanguage:(NSString *)language;
- (void)addThread:(NSString *)thread;
 
- (BOOL)isChatMessage;
- (BOOL)isChatMessageWithBody;
- (BOOL)isErrorMessage;
- (BOOL)isMessageWithBody;
 
- (NSError *)errorMessage;
 
@end
Copy the code

Message is a basic push message method that does not require a response. It is mainly used in IM, groupChat, Alert, and Notification applications.

There are the following categories (optional Settings such as chat) :

Normal: Similar to email, it does not require any response.

Chat: Similar to instant messaging with friends in QQ, featuring real-time communication;

4. Groupchat: a groupchat similar to a chat room;

Headline: for sending alert and notification;

Error: If an error occurs in sending a message, the entity that discovered the error uses this category to notify the sender of the error;

node

The content to be sent is placed under the body node

Examples of message nodes:

<messageto="[email protected]/contact" type="chat"> <body> Hello? Is the name of your blog a tech blog called Biogo? Is the address http://www.henishuo.com? </body> </message>Copy the code

A lot of, in fact, I am from other people’s article copy

The login

The login process is like this:

1. Initialize an xmppStream

2. Connect to the server (successful or failed)

3. Server validation based on success (success or failure)

4. On the basis of success, send the online message

Initialize related classes

- (XMPPStream *)xmppStream{
   if(! _xmppStream = [[xmppStream alloc] init]; [_xmppStream] // Set the server addresssetHostName:@"192.168.21.202"]; // Set the port number [_xmppStream]setHostPort:5222];
   }
   return _xmppStream;
}
Copy the code
- (void)loginWithName:(NSString *)userName password:(NSString *)password{// mark the destination for connecting to the server self.connecttype = XMPPLogin; / / here records the user to enter the password, the login (registration) in the use of the self. The currentUser. Password = password; self.currentUser.userName = userName; // create xmppjid (user 0, @param NSString user name, domain name, how to log in to the server (Apple, Jid = [XMPPJID jidWithUser:userName domain: domain resource: resource]; self.xmppStream.myJID = jid; // Connect to the server [self connectToServer]; } - (void)connectToServer{// If a connection already exists or is in progress, disconnect the current connection and start a new oneif ([self.xmppStream isConnected] || [self.xmppStream isConnecting]) {
       [self logout]; } NSError *error = nil; [self. XmppStream connectWithTimeout: 6.0 f error: & error];if (error) {
       NSLog(@"error = %@",error); }}# pragma XMPPStream agent
Pragma mark method that failed to connect to the server
- (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender{
   [SVProgressHUD showErrorWithStatus:@"Connection to server timed out"];
   NSLog(@"Failed to connect to server method, please check whether the network is normal");
}

- (void)xmppStream:(XMPPStream *)sender didReceiveError:(DDXMLElement *)error{
   [SVProgressHUD showErrorWithStatus:@"Failed to connect to server"];
   NSLog(@"didReceiveError");
}

Pragma mark method used to successfully connect to the server
- (void)xmppStreamDidConnect:(XMPPStream *)sender{
   NSLog(@"Method of successful connection to server"); / / loginif(self.connectType == XMPPLogin) { NSError *error = nil; / / to the server to send password authentication / / verification may fail or succeed [sender authenticateWithPassword: self, currentUser. Password error: & error];if(! error){ self.currentUser.jid = sender.myJID; NSLog(@"Login successful");
       }else{
           NSLog(@"Login failed"); }} // Registerelse{// Send a password registration to the server (successful or failed)if([sender registerWithPassword:self.currentUser.password error:nil]){
           NSLog(@"Registration successful");
       }else{
           NSLog(@"Registration failed"); }}} / / login successful - (void) xmppStreamDidAuthenticate (XMPPStream *) sender {NSLog (@"XmppStreamDidAuthenticate login successful"); _isLogout = NO; // initiate online status [self goOnline]; / / login notice to inform the [[NSNotificationCenter defaultCenter] postNotificationName: LoginSuccess object: nil]; } - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{ NSLog(@"Incorrect password %@",error); _isLogout = YES; [self offline]; / / login notification failure notice [[NSNotificationCenter defaultCenter] postNotificationName: LoginFaild object: nil]; } // send the presence status to the server - (void)goOnline{// send a <presence/> default value.  // show is fixed, there are several types of DND, XA, away, chat, XMPPPresence *presence = [XMPPPresence presence]; [presence addChild:[DDXMLNode elementWithName:@"status" stringValue:@"I'm busy right now."]];
   [presence addChild:[DDXMLNode elementWithName:@"show" stringValue:@"xa"]];
   
   [self.xmppStream sendElement:presence];
}

Copy the code

The above is the login process, more violence directly posted a pile of code

## Add friends

Add is actually sending an IQ request

 #pragma Mark Add a friend
- (void)addFriend:(UserModel *)user{
    if(user){// 'nickname' = 'nickname' Is not his personal information nickname in [[XMPPManager shareInstanceManager]. XmppRoster addUser: user. The jid withNickname: user. The userName]; }}# Pragma Mark ===== Friends module =======*/ - (void)xmppRoster:(xmppRoster *)sender DidReceivePresenceSubscriptionRequest presence: (XMPPPresence *) {/ / add buddy will subscribe to the other party, Self.pj_newfriend.jid = presence. From; self.pj_newfriend.jid = presence. NSString *message = [NSString stringWithFormat:@"[%@] want to friend you",presence.from.bare];
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:message delegate:self cancelButtonTitle:@"Reject" otherButtonTitles:@"Agree", nil]; [alertView show]; } - (void)xmppStream:(xmppStream *)sender didReceivePresence:(XMPPPresence *)presenceif ([presence.type isEqualToString:@"unsubscribe"[self.xmpproster removeUser:presence. From]; }}Copy the code

XMPPRoster gets a list of friends (it gets a list of friends that are online

XMPPRoster can do friend-related things: get a friend list, add a friend, receive a friend request, agree to add a friend, reject a friend

XMPPRosterCoreDataStorage used to store the friend (need to know some CoreData related)

Initialize related classes

- (XMPPRoster *)xmppRoster{
   if(! _xmppRoster){ _xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:self.xmppRosterCoreDataStorage dispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; // Turn off auto-sync and call the fetchRoster method to pull [_xmppRoster] where you need to pull the buddy listsetAutoFetchRoster:NO];
   }
   return _xmppRoster;
}

- (XMPPRosterCoreDataStorage *)xmppRosterCoreDataStorage{
   if(! _xmppRosterCoreDataStorage){ // 3. Friends module to support our management, synchronization, application, remove buddy _xmppRosterCoreDataStorage = [XMPPRosterCoreDataStorage sharedInstance]; }return _xmppRosterCoreDataStorage;
}
Copy the code

Where you need to recruit online friends:

// Friend list broker [[XMPPManager shareInstanceManager].xmpProster addDelegate:self delegateQueue:dispatch_get_main_queue()]; [[XMPPManager shareInstanceManager]. XmppRoster fetchRoster];Copy the code

The above code executes with a callback

Pragma mark -- Friend list protocol method/ * * * start synchronization server sends to come over to your buddy list * * / - (void) xmppRosterDidBeginPopulating: (XMPPRoster *) sender {} every friends - / / received (void)xmppRoster:(xmppRoster *)sender didReceiveRosterItem:(NSXMLElement *)item{// get the jid of item NSString *jid = [[item attributeForName:@"jid"]stringValue]; // Convert to XMPPJID XMPPJID *userJID = [XMPPJID jidWithString:jid]; NSLog(@"% @",[userJID user]); UserModel *friend = [[UserModel alloc] initWithJid:userJID]; [self.contacts addObject:friend]; } / * * / / / * * * end of synchronous receive friends list IQ will enter method, and it has been deposited in my memory - (void) xmppRosterDidEndPopulating (XMPPRoster *) sender {NSLog (@"End of buddy list, here to pass value outside");
   [self loadFreinds];
   [self.tableView reloadData];
   [self.tableView.mj_header endRefreshing];
}

Copy the code

Synchronize friends in SQLite

/ * * * friends list load NSFetchedResultsController, official explanation is read from the Core Data can be effectively management to provide UITableView object Data. Configure a request and specify the database table to be queried. 2. Set at least one sort method for the obtained data. 3. Set filtering conditions for the data. Listen for changes to its associated context (nS-ManageDoBJectContext) and report these changes 2. */ - (void)loadFreinds{// Display buddy data (saved in xmpproster.sqlite) //1. Context, Associate XMPPRoster. Sqlite NSManagedObjectContext *rosterContext = [[XMPPManager shareInstanceManager].xmppRosterCoreDataStorage mainThreadManagedObjectContext]; / / 2. The request which table query NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName: @"XMPPUserCoreDataStorageObject"]; // set sort descriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"displayName"ascending:YES]; request.sortDescriptors = @[sort]; // Predicate = [predicateWithFormat:@. // Predicate = [predicateWithFormat:@"subscription ! = % @"The @"none"]; request.predicate = predicate; / / 3. Perform the request/controller / 3.1 create results _resultContr = [[NSFetchedResultsController alloc] initWithFetchRequest: request managedObjectContext:rosterContext sectionNameKeyPath:nil cacheName:nil]; _resultContr.delegate = self; NSError *error = nil; [_resultContr performFetch:&error];for(XMPPUserCoreDataStorageObject *user in _resultContr.fetchedObjects){
       NSLog(@Friend: "% @",user.displayName); UserModel *friend = [[UserModel alloc] initWithXMPPUserCoreDataStorageObject:user]; [self.contacts addObject:friend]; }}Copy the code

Next, enter the chat module

Send text

// This method handles sending an XML stanza. * If the XMPPStream is not connected, this method does nothing. **/ - (void)sendElement:(NSXMLElement *)element;Copy the code

Encapsulate the message itself and send it

// sendTextMessage - (void)sendTextMessage{XMPPMessage *message = [XMPPMessage messageWithType:@"chat"to:_chatUserModel.jid]; [message addBody:_inputBar.messageContent]; [message addSubject:TextMessage]; / / custom text message classes PJContentMessage * contentMessage = [[PJContentMessage alloc] initWithXMPPMessage: message]; contentMessage.showMessageIn = ShowMessageInRight; [self.chatArray addObject:contentMessage]; [self.tableView reloadData]; [self tableViewScrollToBottom]; [[XMPPManager shareInstanceManager].xmppStream sendElement:message]; }Copy the code

Sending pictures

Sending mode:

1: first turn the picture into base 2 (NSData) format, and then turn it into a string using Base64. When the text is sent, set its attributes on the sender, and the receiver can judge what is transmitted by judging its attributes. If it’s a picture, use Base64 to decompress the string into NSData and turn it into a picture.

2: Convert the image to base 2 directly, and then upload it to the server using ASI. Then the sender sends the address of your image to the receiver, and then the receiver can download it from this address.

About sending pictures:

Voice words first through AVAudioRecorder recording, select a good format (ACC, AMR). Wechat is amR transcoding. And then the rest is the same as the picture scheme

The basic idea behind sending images and audio files is:

Images are first converted to binaries, which are then base64 encoded into strings. Add a child node to the message to be sent, and the stringValue of the node sets the encoded string. Then, when retrieving the message file after the message is sent, the stringValue of the child node should be extracted from the string after Base64 through messageType to determine whether it is the picture information. If it is the picture information, the stringValue of the child node should be extracted from the node name set before.

- (void)sendImageMessage:(UIImage *)image{XMPPMessage *message = [XMPPMessage messageWithType:@"chat"to:_chatUserModel.jid]; [message addBody:ImageMessage]; [message addSubject:ImageMessage]; / / custom picture PJImageMessage * imageMessage = [[PJImageMessage alloc] initWithXMPPMessage: message]; imageMessage.showMessageIn = ShowMessageInRight; // Name the image imagemessage. imageName = [NSString stringWithFormat:@"% @ % @",[[PJDateTool shareInstance] currentDate],_chatUserModel.userName]; NSData *imageData = UIImagePNGRepresentation(image); NSString *imageBase64Str = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; / / send you send pictures to cached [PJCacheManager cacheImage: imageData imageMessage: imageMessage]; / / set the node content, the content of the picture base64str XMPPElement * attachment = [XMPPElement elementWithName: ImageMessage stringValue:imageBase64Str]; [message addChild:attachment]; / / picture node name (image is loaded through the picture name query load in to the cache) XMPPElement * imageAttachment = [XMPPElement elementWithName: XMPPElementImageMessage stringValue:imageMessage.imageName]; // Contains child nodes [message addChild:imageAttachment]; [self.chatArray addObject:imageMessage]; [self.tableView reloadData]; [self tableViewScrollToBottom]; [[XMPPManager shareInstanceManager].xmppStream sendElement:message]; }Copy the code

Image caching

+ (void)cacheImage:(NSData *)imageData imageMessage:(PJImageMessage *)imageMessage{if(! imageData || [NSString isBlankString:imageMessage.imageName]){return; } NSString *sandoxPath = NSHomeDirectory(); // Set an image storage path NSString *imageDirectoryPath = [sandoxPath stringByAppendingString:[NSString stringWithFormat:@"/Documents/%@",[XMPPManager shareInstanceManager].currentUser.userName]]; // Create a folder for each chatter user NSFileManager *fileManager = [NSFileManager defaultManager];if(! [fileManager fileExistsAtPath: imageDirectoryPath]) {/ / directory does not exist to create a [fileManager createDirectoryAtPath: imageDirectoryPath withIntermediateDirectories:YES attributes:nil error:nil]; } / / create the file path nsstrings * filePath = [imageDirectoryPath stringByAppendingPathComponent: [nsstrings stringWithFormat: @"%@.png",imageMessage.imageName]];
    
    if([imageData writeToFile:filePath atomically:YES]){// Set the local path of the cached image imagemessage. localUrl = filePath; }}Copy the code

Send voice

- (void)sendVoiceMesage:(PJVoiceMessage *)voiceMessage{XMPPMessage - (void)sendVoiceMesage:(PJVoiceMessage *)voiceMessage{XMPPMessage *message = [XMPPMessage messageWithType:@"chat"to:_chatUserModel.jid]; [message addBody:VoiceMessage]; [message addSubject:VoiceMessage]; voiceMessage.showMessageIn = ShowMessageInRight; NSData *voiceData = [NSData dataWithContentsOfFile:voiceMessage.localUrl]; NSString *voiceBase64Str = [voiceData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; / / set the node content, the content of the speech base64str XMPPElement * attachment = [XMPPElement elementWithName: VoiceMessage stringValue:voiceBase64Str]; NSLog(@"voiceBase64Str:%@",voiceBase64Str); [message addChild:attachment]; / / voice node name (voice is loaded by voice name query load in to the cache) XMPPElement * voiceAttachment = [XMPPElement elementWithName: XMPPElementVoiceMessage stringValue:voiceMessage.voiceName]; // Contains child nodes [message addChild:voiceAttachment]; [self.chatArray addObject:voiceMessage]; [self.tableView reloadData]; [self tableViewScrollToBottom]; [[XMPPManager shareInstanceManager].xmppStream sendElement:message]; }Copy the code

Caches voice messages after receiving them

+ (void)cacheVoice:(NSData *)voiceData voiceMessage:(PJVoiceMessage *)voiceMessage{if(! voiceData || [NSString isBlankString:voiceMessage.voiceName]){return; } NSString *sandoxPath = NSHomeDirectory(); NSString *voiceDirectoryPath = [sandoxPath stringByAppendingString:[NSString stringWithFormat:@"/Documents/%@/voice/",[XMPPManager shareInstanceManager].currentUser.userName]]; // Create a folder for each chatter user NSFileManager *fileManager = [NSFileManager defaultManager];if(! [fileManager fileExistsAtPath: voiceDirectoryPath]) {/ / directory does not exist to create a [fileManager createDirectoryAtPath: voiceDirectoryPath withIntermediateDirectories:YES attributes:nil error:nil]; } / / create the file path nsstrings * filePath = [voiceDirectoryPath stringByAppendingPathComponent: [nsstrings stringWithFormat: @"%@.aac",voiceMessage.voiceName]];
    
    if([voiceData writeToFile:filePath atomically:YES]){// Set the local path for cached voicemessage. localUrl = filePath; }}Copy the code

Ok, that’s about it, more basic, I do the Demo address is:gitHubThere are a lot of bugs in it don’t take it too seriously