即时通信

XMPPManager.h

 1 #import <Foundation/Foundation.h>
 2 #import "XMPPFramework.h"
 3
 4 @interface XMPPManager : NSObject
 5
 6 /**
 7  *  创建一个通道的属性
 8  */
 9 @property (nonatomic, strong) XMPPStream *xmppStream;
10
11 /**
12  *  管理好友,好友的列表
13  */
14 @property (nonatomic, strong) XMPPRoster *xmppRoster;
15
16 /**
17  *  和聊天相关的属性
18  */
19 @property (nonatomic, strong) XMPPMessageArchiving *messageArchiving;
20
21 /**
22  *  消息归档
23  */
24 @property (nonatomic, strong) NSManagedObjectContext *context;
25
26
27 // 创建单例
28 + (instancetype)shareManager;
29
30 // 登录的方法
31 - (void)loginWithUserName:(NSString *)userName password:(NSString *)password;
32
33 // 注册的方法
34 - (void)registerWithUserName:(NSString *)userName password:(NSString *)password;
35
36 // 添加好友
37 - (void)addFriend;
38 // 删除好友
39 - (void)removeFriendWithName:(NSString *)name;
40
41 @end

XMPPManager.m

  1 #import "XMPPManager.h"
  2
  3 // 列举枚举(登录和注册)
  4 typedef NS_ENUM(NSInteger, ConnectToServerPurpose) {
  5     ConnectToServerPurposeLogin,
  6     ConnectToServerPurposeRegister,
  7 };
  8
  9
 10 @interface XMPPManager () <XMPPStreamDelegate, XMPPRosterDelegate>
 11
 12 // 登录的属性
 13 @property (nonatomic, strong) NSString *userName;  // 用户名
 14 @property (nonatomic, strong) NSString *password;  // 密码
 15
 16 // 注册的属性
 17 @property (nonatomic, strong) NSString *registerUserName;  // 用户名
 18 @property (nonatomic, strong) NSString *registerPassword;  // 密码
 19
 20 // 记录当前是登录还是注册
 21 @property (nonatomic, assign) ConnectToServerPurpose connectToserver;
 22
 23 // 接收添加好友的名字
 24 @property (nonatomic, strong) UITextField *textField;
 25
 26 @end
 27
 28
 29 static XMPPManager *xmpp = nil;
 30
 31 @implementation XMPPManager
 32
 33 // 创建单例
 34 + (instancetype)shareManager {
 35
 36     static dispatch_once_t onceToken;
 37     dispatch_once(&onceToken, ^{
 38         xmpp = [[XMPPManager alloc] init];
 39     });
 40     return xmpp;
 41 }
 42
 43
 44 #pragma mark - 初始化
 45 - (instancetype)init {
 46
 47     if (self = [super init]) {
 48
 49 #pragma mark - 初始化通道
 50         // 初始化通道对象
 51         self.xmppStream = [[XMPPStream alloc] init];
 52
 53         // 设置openfire服务器地址
 54         self.xmppStream.hostName = kHostName;
 55
 56         // 设置服务器端口号
 57         self.xmppStream.hostPort = kHostPort;
 58
 59         // 添加代理
 60         [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
 61
 62
 63 #pragma mark - 管理好友
 64         //获取管理好友的单例对象
 65         XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance];
 66
 67         // 初始化好友列表
 68         self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage dispatchQueue:dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_BACKGROUND)];
 69
 70         // 在通道中激活添加好友
 71         [self.xmppRoster activate:self.xmppStream];
 72
 73         // 设置花名册代理
 74         [self.xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
 75
 76
 77 #pragma mark - 管理消息
 78         // 获取管理消息的存储对象
 79         XMPPMessageArchivingCoreDataStorage *storage = [XMPPMessageArchivingCoreDataStorage sharedInstance];
 80
 81         // 进行消息管理器的初始化
 82         self.messageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:storage dispatchQueue:dispatch_get_main_queue()];
 83
 84         // 在通道中激活
 85         [self.messageArchiving activate:self.xmppStream];
 86
 87         // 设置代理
 88         [self.messageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()];
 89
 90         // 管理上下文
 91         self.context = storage.mainThreadManagedObjectContext;
 92
 93     }
 94
 95     return self;
 96 }
 97
 98
 99 #pragma mark - 登录和注册的方法
100 // 登录方法的实现
101 - (void)loginWithUserName:(NSString *)userName password:(NSString *)password {
102
103     self.userName = userName;
104     self.password= password;
105
106     // 记录登录状态
107     self.connectToserver = ConnectToServerPurposeLogin;
108
109     // 连接服务器
110     [self linkServer];
111 }
112
113
114 // 注册方法的实现
115 - (void)registerWithUserName:(NSString *)userName password:(NSString *)password {
116
117     self.registerUserName = userName;
118     self.registerPassword = password;
119
120     // 记录登录状态
121     self.connectToserver = ConnectToServerPurposeRegister;
122
123     // 连接服务器
124     [self linkServer];
125 }
126
127
128 #pragma mark - 连接服务器
129 - (void)linkServer {
130
131     // 判断是登录还是注册
132     NSString *string = [[NSString alloc] init];
133     switch (self.connectToserver) {
134         case ConnectToServerPurposeLogin:
135             string = [NSString stringWithFormat:@"%@", self.userName];
136             break;
137         case ConnectToServerPurposeRegister:
138             string = [NSString stringWithFormat:@"%@", self.registerUserName];
139             break;
140
141         default:
142             break;
143     }
144
145     // 要链接服务器,要有用户认证
146     // 身份证
147     // 参数1:用户名
148     // 参数2:域名
149     // 参数3:资源
150     // 通过通道进行身份验证
151     XMPPJID *jid = [XMPPJID jidWithUser:string domain:kDomin resource:kResource];
152     self.xmppStream.myJID = jid;
153
154     // 如果当前聊天工具处于链接状态或者已经链接了,此时,你需要去切断链接,【此逻辑不唯一】
155     if ([self.xmppStream isConnected] || [self.xmppStream isConnecting]) {
156         [self disconnectToServer];
157     }
158
159     // 链接超时
160     NSError *error = nil;
161     [self.xmppStream connectWithTimeout:5 error:&error];
162
163     if (error) {
164         NSLog(@"服务器链接超时");
165     }
166 }
167
168
169 // 断开链接
170 - (void)disconnectToServer {
171
172     // 当前用户下线了
173     XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"];
174
175     // 告诉通道下线了
176     [self.xmppStream sendElement:presence];
177
178     // 使通道失去链接
179     [self.xmppStream disconnect];
180 }
181
182
183 #pragma mark - XMPPStreamDelegate协议方法
184 #pragma mark - 链接超时
185 - (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender {
186
187     NSLog(@"连接超时");
188 }
189
190
191 #pragma mark - 连接成功
192 - (void)xmppStreamDidConnect:(XMPPStream *)sender {
193
194     NSLog(@"连接成功");
195
196     // 区分登录和注册进行密码验证
197     switch (self.connectToserver) {
198         case ConnectToServerPurposeLogin:
199             // 登录密码的验证
200             [self.xmppStream authenticateWithPassword:self.password error:nil];
201             break;
202         case ConnectToServerPurposeRegister:
203             // 注册密码的验证
204             [self.xmppStream registerWithPassword:self.registerPassword error:nil];
205             break;
206
207         default:
208             break;
209     }
210 }
211
212
213 #pragma mark - 断开链接
214 - (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error {
215
216     // 1.自己手动断开
217     // 2.被动断开
218     if (error) {
219         NSLog(@"断开链接,error = %@", error);
220     }
221 }
222
223
224 #pragma mark - 认证失败(登录失败)
225 - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error {
226
227     NSLog(@"认证失败, error = %@", error);
228 }
229
230
231 #pragma mark - 认证成功(登录成功)
232 - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
233
234     // 上线
235     XMPPPresence *presennce = [XMPPPresence presenceWithType:@"available"];
236     [self.xmppStream sendElement:presennce];
237
238     NSLog(@"登录成功");
239 }
240
241
242 #pragma mark - 添加好友
243 - (void)addFriend {
244
245     UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"添加好友" message:@"请输入好友的姓名" preferredStyle:UIAlertControllerStyleAlert];
246
247     __weak typeof(self)mySelf = self;
248     [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
249         mySelf.textField = textField;
250     }];
251
252     UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
253         NSLog(@"取消添加好友");
254     }];
255     UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
256         // 使用JID记录
257         XMPPJID *friendJID = [XMPPJID jidWithUser:mySelf.textField.text domain:kDomin resource:kResource];
258
259         // 监听好友的动作
260         [mySelf.xmppRoster subscribePresenceToUser:friendJID];
261         NSLog(@"===============%@", mySelf.textField.text);
262         // 添加好友
263         [mySelf.xmppRoster addUser:friendJID withNickname:mySelf.textField.text];
264     }];
265
266     [alertController addAction:action1];
267     [alertController addAction:action2];
268
269     [[self getCurrentVC] presentViewController:alertController animated:YES completion:nil];
270 }
271
272
273 //获取当前屏幕显示的viewcontroller
274 - (UIViewController *)getCurrentVC
275 {
276     UIViewController *result = nil;
277
278     UIWindow * window = [[UIApplication sharedApplication] keyWindow];
279     if (window.windowLevel != UIWindowLevelNormal)
280     {
281         NSArray *windows = [[UIApplication sharedApplication] windows];
282         for(UIWindow * tmpWin in windows)
283         {
284             if (tmpWin.windowLevel == UIWindowLevelNormal)
285             {
286                 window = tmpWin;
287                 break;
288             }
289         }
290     }
291
292     UIView *frontView = [[window subviews] objectAtIndex:0];
293     id nextResponder = [frontView nextResponder];
294
295     if ([nextResponder isKindOfClass:[UIViewController class]])
296         result = nextResponder;
297     else
298         result = window.rootViewController;
299
300     return result;
301 }
302
303
304 #pragma mark - 删除好友
305 - (void)removeFriendWithName:(NSString *)name {
306
307     // 使用JID记录要删除的用户
308     XMPPJID *friendJID = [XMPPJID jidWithUser:name domain:kDomin resource:kResource];
309
310     // 停止监听好友
311     [self.xmppRoster unsubscribePresenceFromUser:friendJID];
312
313     // 删除好友
314     [self.xmppRoster removeUser:friendJID];
315 }
316
317
318 #pragma mark - XMPPRosterDelegate代理方法
319 // 开始获取好友
320 - (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender {
321
322     NSLog(@"开始获取好友");
323 }
324
325
326 // 结束获取好友
327 - (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender {
328
329     NSLog(@"结束获取好友");
330 }
331
332
333 // 接受好友的信息
334 - (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item {
335
336     // 好友状态的显示信息
337     NSLog(@"好友信息====%@", item);
338 }
339
340
341 // 收到好友监听请求,是同意还是拒绝
342 - (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence {
343
344     NSLog(@"获取好友请求");
345 }
346
347 @end

LoginViewController.m

  1 #import "LoginViewController.h"
  2 #import "Masonry.h"
  3 #import "RegisterViewController.h"
  4 #import "XMPPManager.h"
  5 #import "RosterTableViewController.h"
  6
  7 @interface LoginViewController () <XMPPStreamDelegate>
  8
  9 /**
 10  *  姓名输入框
 11  */
 12 @property (nonatomic, strong) UITextField *userTextField;
 13
 14 /**
 15  *  密码输入框
 16  */
 17 @property (nonatomic, strong) UITextField *passwordTextField;
 18
 19 /**
 20  *  登录按钮
 21  */
 22 @property (nonatomic, strong) UIButton *loginButton;
 23
 24 /**
 25  *  注册按钮
 26  */
 27 @property (nonatomic, strong) UIButton *registerButton;
 28
 29 @end
 30
 31 @implementation LoginViewController
 32
 33 - (void)viewDidLoad {
 34     [super viewDidLoad];
 35     // Do any additional setup after loading the view.
 36
 37     self.navigationItem.title = @"首页";
 38     self.view.backgroundColor = [UIColor orangeColor];
 39
 40
 41     // 布局页面
 42     [self initLayout];
 43
 44
 45     // 给登录和注册按钮添加事件
 46     [_loginButton addTarget:self action:@selector(loginClick:) forControlEvents:UIControlEventTouchUpInside];
 47     [_registerButton addTarget:self action:@selector(registerClick:) forControlEvents:UIControlEventTouchUpInside];
 48
 49
 50     // 设置代理
 51     [[XMPPManager shareManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
 52
 53 }
 54
 55
 56 #pragma mark - 登录和注册按钮的点击事件
 57 // 登录按钮的点击事件
 58 - (void)loginClick:(UIButton *)sender {
 59
 60     [[XMPPManager shareManager] loginWithUserName:self.userTextField.text password:self.passwordTextField.text];
 61 }
 62
 63
 64 // 注册按钮的点击事件
 65 - (void)registerClick:(UIButton *)sender {
 66
 67     RegisterViewController *registerVC = [[RegisterViewController alloc] init];
 68     [self.navigationController pushViewController:registerVC animated:YES];
 69 }
 70
 71
 72 #pragma mark - XMPPStreamDelegate代理方法
 73 // 登录成功
 74 - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
 75
 76     NSLog(@"登录成功");
 77
 78     RosterTableViewController *rosterVC = [[RosterTableViewController alloc] init];
 79     [self.navigationController pushViewController:rosterVC animated:YES];
 80 }
 81
 82 // 登录失败
 83 - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error {
 84
 85     NSLog(@"登录失败");
 86 }
 87
 88
 89 #pragma mark - 布局页面
 90 - (void)initLayout {
 91
 92     // 创建子控件
 93     _userTextField = [[UITextField alloc] init];
 94     _userTextField.borderStyle = UITextBorderStyleRoundedRect;
 95     _userTextField.layer.cornerRadius = 5;
 96     _userTextField.backgroundColor = [UIColor whiteColor];
 97     _userTextField.placeholder = @"请输入用户名";
 98     [self.view addSubview:_userTextField];
 99
100     _passwordTextField = [[UITextField alloc] init];
101     _passwordTextField.borderStyle = UITextBorderStyleRoundedRect;
102     _passwordTextField.layer.cornerRadius = 5;
103     _passwordTextField.backgroundColor = [UIColor whiteColor];
104     _passwordTextField.placeholder = @"请输入密码";
105     [self.view addSubview:_passwordTextField];
106
107     _loginButton = [UIButton buttonWithType:UIButtonTypeSystem];
108     _loginButton.layer.cornerRadius = 5;
109     [_loginButton setTitle:@"登   录" forState:UIControlStateNormal];
110     [_loginButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
111     _loginButton.backgroundColor = [UIColor whiteColor];
112     [self.view addSubview:_loginButton];
113
114     _registerButton = [UIButton buttonWithType:UIButtonTypeSystem];
115     _registerButton.layer.cornerRadius = 5;
116     [_registerButton setTitle:@"注   册" forState:UIControlStateNormal];
117     [_registerButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
118     _registerButton.backgroundColor = [UIColor whiteColor];
119     [self.view addSubview:_registerButton];
120
121
122     // 设置frame
123     [_userTextField mas_makeConstraints:^(MASConstraintMaker *make) {
124
125         make.top.offset(120);
126         make.left.offset(50);
127         make.right.offset(-50);
128         make.height.mas_equalTo(50);
129
130     }];
131
132     [_passwordTextField mas_makeConstraints:^(MASConstraintMaker *make) {
133
134         make.top.equalTo(_userTextField.mas_bottom).offset(50);
135         make.left.offset(50);
136         make.right.offset(-50);
137         make.height.mas_equalTo(50);
138
139     }];
140
141     [_loginButton mas_makeConstraints:^(MASConstraintMaker *make) {
142
143         make.top.equalTo(_passwordTextField.mas_bottom).offset(50);
144         make.left.offset(80);
145         make.width.mas_equalTo((self.view.frame.size.width - 80 - 80 - 50) / 2);
146         make.height.mas_equalTo(50);
147
148     }];
149
150     [_registerButton mas_makeConstraints:^(MASConstraintMaker *make) {
151
152         make.top.equalTo(_loginButton.mas_top);
153         make.right.offset(-80);
154         make.width.mas_equalTo((self.view.frame.size.width - 80 - 80 - 50) / 2);
155         make.height.mas_equalTo(50);
156
157     }];
158 }
159
160 @end

RegisterViewController.m

  1 #import "RegisterViewController.h"
  2 #import "Masonry.h"
  3 #import "XMPPManager.h"
  4
  5 @interface RegisterViewController () <XMPPStreamDelegate>
  6
  7 /**
  8  *  姓名输入框
  9  */
 10 @property (nonatomic, strong) UITextField *userTextField;
 11
 12 /**
 13  *  密码输入框
 14  */
 15 @property (nonatomic, strong) UITextField *passwordTextField;
 16
 17 /**
 18  *  注册按钮
 19  */
 20 @property (nonatomic, strong) UIButton *registerButton;
 21
 22 @end
 23
 24 @implementation RegisterViewController
 25
 26 - (void)viewDidLoad {
 27     [super viewDidLoad];
 28     // Do any additional setup after loading the view.
 29
 30
 31     self.navigationItem.title = @"注册";
 32     self.view.backgroundColor = [UIColor purpleColor];
 33
 34
 35     // 页面布局
 36     [self initLayout];
 37
 38
 39     // 设置代理
 40     [[XMPPManager shareManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
 41
 42
 43     // 注册按钮的点击事件
 44     [_registerButton addTarget:self action:@selector(registerClick:) forControlEvents:UIControlEventTouchUpInside];
 45
 46 }
 47
 48
 49 #pragma mark - 注册按钮点击事件
 50 - (void)registerClick:(UIButton *)sender {
 51
 52     [[XMPPManager shareManager] registerWithUserName:self.userTextField.text password:self.passwordTextField.text];
 53 }
 54
 55
 56 #pragma mark - XMPPStreamDelegate实现协议方法
 57 // 实现协议是为了实现需求:注册成功之后dismiss到登录界面
 58 // 注册成功
 59 - (void)xmppStreamDidRegister:(XMPPStream *)sender {
 60
 61     NSLog(@"注册成功");
 62     [self.navigationController popViewControllerAnimated:YES];
 63 }
 64
 65
 66 // 注册失败
 67 - (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error {
 68
 69     NSLog(@"注册失败 = %@", error);
 70 }
 71
 72
 73 #pragma mark - 布局页面
 74 - (void)initLayout {
 75
 76     // 创建子控件
 77     _userTextField = [[UITextField alloc] init];
 78     _userTextField.borderStyle = UITextBorderStyleRoundedRect;
 79     _userTextField.layer.cornerRadius = 5;
 80     _userTextField.backgroundColor = [UIColor whiteColor];
 81     _userTextField.placeholder = @"请输入用户名";
 82     [self.view addSubview:_userTextField];
 83
 84     _passwordTextField = [[UITextField alloc] init];
 85     _passwordTextField.borderStyle = UITextBorderStyleRoundedRect;
 86     _passwordTextField.layer.cornerRadius = 5;
 87     _passwordTextField.backgroundColor = [UIColor whiteColor];
 88     _passwordTextField.placeholder = @"请输入密码";
 89     [self.view addSubview:_passwordTextField];
 90
 91     _registerButton = [UIButton buttonWithType:UIButtonTypeSystem];
 92     _registerButton.layer.cornerRadius = 5;
 93     [_registerButton setTitle:@"注   册" forState:UIControlStateNormal];
 94     [_registerButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
 95     _registerButton.backgroundColor = [UIColor whiteColor];
 96     [self.view addSubview:_registerButton];
 97
 98
 99     // 设置frame
100     [_userTextField mas_makeConstraints:^(MASConstraintMaker *make) {
101
102         make.top.offset(120);
103         make.left.offset(50);
104         make.right.offset(-50);
105         make.height.mas_equalTo(50);
106
107     }];
108
109     [_passwordTextField mas_makeConstraints:^(MASConstraintMaker *make) {
110
111         make.top.equalTo(_userTextField.mas_bottom).offset(50);
112         make.left.offset(50);
113         make.right.offset(-50);
114         make.height.mas_equalTo(50);
115
116     }];
117
118     [_registerButton mas_makeConstraints:^(MASConstraintMaker *make) {
119
120         make.top.equalTo(_passwordTextField.mas_bottom).offset(50);
121         make.left.offset(150);
122         make.right.offset(-150);
123         make.height.mas_equalTo(50);
124
125     }];
126 }
127
128 @end

RosterTableViewController.m

  1 #import "RosterTableViewController.h"
  2 #import "XMPPManager.h"
  3 #import "ChatTableViewController.h"
  4
  5 @interface RosterTableViewController () <XMPPStreamDelegate, XMPPRosterDelegate>
  6
  7 /**
  8  *  存储好友列表的数组
  9  */
 10 @property (nonatomic, strong) NSMutableArray *allRosterArray;
 11
 12 /**
 13  *  用来存储发送请求者的JID
 14  */
 15 @property (nonatomic, strong) XMPPJID *fromJID;
 16
 17
 18 @end
 19
 20 @implementation RosterTableViewController
 21
 22 // 懒加载
 23 - (NSMutableArray *)allRosterArray {
 24
 25     if (!_allRosterArray) {
 26         _allRosterArray = [NSMutableArray array];
 27     }
 28     return _allRosterArray;
 29 }
 30
 31
 32 - (void)viewDidLoad {
 33     [super viewDidLoad];
 34
 35
 36     self.navigationItem.title = @"好友列表";
 37
 38
 39     // 设置代理
 40     [[XMPPManager shareManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
 41     [[XMPPManager shareManager].xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
 42
 43
 44     // 注册cell
 45     [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
 46
 47
 48     // 设置添加按钮和返回按钮
 49     self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addFriendAction:)];
 50     self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAction:)];
 51
 52 }
 53
 54
 55 #pragma mark - 添加和注销按钮的实现
 56 // 添加按钮的方法实现
 57 - (void)addFriendAction:(UIBarButtonItem *)sender {
 58
 59     [[XMPPManager shareManager] addFriend];
 60 }
 61
 62 // 注销按钮的方法实现
 63 - (void)cancelAction:(UIBarButtonItem *)sender {
 64
 65     [self.navigationController popViewControllerAnimated:YES];
 66 }
 67
 68
 69 #pragma mark - 设置cell
 70 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
 71
 72     return self.allRosterArray.count;
 73 }
 74
 75
 76 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 77
 78     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
 79     // 根据情况分析,合理添加判断
 80     if (self.allRosterArray.count > 0) {
 81         //获取用户
 82         XMPPJID *jid = self.allRosterArray[indexPath.row];
 83         cell.textLabel.text = jid.user;
 84     }
 85
 86     return cell;
 87 }
 88
 89
 90 #pragma mark - 编辑页面(删除)
 91 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
 92
 93     return YES;
 94 }
 95
 96
 97 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
 98     if (editingStyle == UITableViewCellEditingStyleDelete) {
 99         // 处理要删除的数据
100         // 要删除用户的jid
101         XMPPJID *jid = self.allRosterArray[indexPath.row];
102         [[XMPPManager shareManager] removeFriendWithName:jid.user];
103         [self.allRosterArray removeObjectAtIndex:indexPath.row];
104         [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
105     }
106 }
107
108
109 #pragma mark - XMPPRosterDelegate代理方法
110 // 开始获取好友
111 - (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender {
112
113     NSLog(@"开始获取好友");
114 }
115
116 // 结束获取好友
117 - (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender {
118
119     // 当前页面适用于显示好友列表的,所以在结束好友获取的代理方法中要进行刷新页面,然后将数据显示
120     // 刷新页面
121     [self.tableView reloadData];
122 }
123
124
125 // 接收好友的信息
126 // 这个代理方法会被执行多次,每添加完好友,相应的好友信息都需要反馈
127 - (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item {
128
129     /*
130      both 互为好友
131      none 互不为好友
132      to 我已添加对方为好友,但是对方还没有接收
133      from 对方已添加自己为好友,但是自己还没有接收
134      remove 曾经删除的好友
135      */
136     // 描述自己和对方之间的关系
137     NSString *description = [[item attributeForName:@"subscription"] stringValue];
138     NSLog(@"description = %@", description);
139
140     if ([description isEqualToString:@"none"] || [description isEqualToString:@"to"] || [description isEqualToString:@"from"] || [description isEqualToString:@"both"]) {
141
142         // 添加好友
143         // 获取要添加好友的JID
144         NSString *friendJID = [[item attributeForName:@"jid"] stringValue];
145         XMPPJID *jid = [XMPPJID jidWithString:friendJID];
146
147         // 如果数组中含有这个用户就不用进行接下来的操作
148         if ([self.allRosterArray containsObject:jid]) {
149             return;
150         }
151
152         // 添加好友到数组中
153         [self.allRosterArray addObject:jid];
154
155         // 在tableView中添加这条数据
156         NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.allRosterArray.count - 1 inSection:0];
157         [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];
158     }
159 }
160
161
162 // 对方好友是同意还是拒绝
163 - (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence {
164
165     self.fromJID = presence.from;
166     // 需要相关的提醒框去确定是否接受
167
168     UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"好友请求" message:@"是否接受好友请求" preferredStyle:UIAlertControllerStyleAlert];
169
170     __weak typeof(self)weakSelf = self;
171     UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"接受" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
172         // 在花名册中去接受好友
173         [[XMPPManager shareManager].xmppRoster acceptPresenceSubscriptionRequestFrom:weakSelf.fromJID andAddToRoster:YES];
174     }];
175     UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"拒绝" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
176         // 在花名册中拒绝好友
177         [[XMPPManager shareManager].xmppRoster rejectPresenceSubscriptionRequestFrom:weakSelf.fromJID];
178     }];
179
180     [alertController addAction:action1];
181     [alertController addAction:action2];
182
183     [self presentViewController:alertController animated:YES completion:^{
184
185     }];
186 }
187
188
189 #pragma mark - XMPPStreamDelegate代理方法
190 // 获取好友是否在线
191 - (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence {
192
193     NSLog(@"status = %@", presence.status);
194
195     NSString *type = presence.type;
196     NSString *presenceOfUser = presence.to.user;
197
198     // 判断当前用户是否为你的好友
199     if ([presenceOfUser isEqualToString:[sender myJID].user]) {
200         if ([type isEqualToString:@"avaialble"]) {
201             NSLog(@"该用户处于上线状态");
202         } else if([type isEqualToString:@"uianaialble"]) {
203             NSLog(@"该用户处于下线状态");
204         }
205     }
206
207 }
208
209
210 #pragma mark - 点击cell进入聊天界面
211 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
212
213     ChatTableViewController *chatVC = [[ChatTableViewController alloc] init];
214     XMPPJID *jid = self.allRosterArray[indexPath.row];
215
216     chatVC.chatToJID = jid;
217     [self.navigationController pushViewController:chatVC animated:YES];
218 }
219
220 @end

ChatTableViewController.h

 1 #import <UIKit/UIKit.h>
 2 #import "XMPPManager.h"
 3
 4 @interface ChatTableViewController : UITableViewController
 5
 6 /**
 7  *  当前和谁进行聊天
 8  */
 9 @property (nonatomic, strong) XMPPJID *chatToJID;
10
11 @end

ChatTableViewController.m

  1 #import "ChatTableViewController.h"
  2 #import "ChatCell.h"
  3
  4 @interface ChatTableViewController () <XMPPStreamDelegate>
  5
  6 /**
  7  *  存储消息
  8  */
  9 @property (nonatomic, strong) NSMutableArray *allMessageArray;
 10
 11 @end
 12
 13 @implementation ChatTableViewController
 14
 15 // 懒加载
 16 - (NSMutableArray *)allMessageArray {
 17
 18     if (!_allMessageArray) {
 19         _allMessageArray = [NSMutableArray array];
 20     }
 21     return _allMessageArray;
 22 }
 23
 24
 25 - (void)viewDidLoad {
 26     [super viewDidLoad];
 27
 28     // 隐藏tableView每一行的线
 29     self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
 30
 31
 32     // 设置代理
 33     [[XMPPManager shareManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
 34
 35
 36     // 获取显示消息的方法
 37     [self showMessage];
 38
 39
 40     // 注册cell
 41     [self.tableView registerClass:[ChatCell class] forCellReuseIdentifier:@"cell"];
 42
 43
 44     // 写两个按钮,一个用于发送,一个用于取消
 45     self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"发送" style:UIBarButtonItemStylePlain target:self action:@selector(sureAction)];
 46     self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"取消" style:UIBarButtonItemStylePlain target:self action:@selector(cancelAction)];
 47 }
 48
 49
 50 #pragma mark - 按钮的发送方法和取消方法
 51 // 发送方法
 52 - (void)sureAction {
 53
 54     // 设置message的body
 55     XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.chatToJID];
 56
 57     [message addBody:@"我爱你"];
 58
 59     // 通过通道进行消息发送
 60     [[XMPPManager shareManager].xmppStream sendElement:message];
 61 }
 62
 63 // 取消方法
 64 - (void)cancelAction {
 65
 66     [self.navigationController popViewControllerAnimated:YES];
 67 }
 68
 69
 70 #pragma mark - 显示消息
 71 - (void)showMessage {
 72
 73     // 获取管理上下文
 74     NSManagedObjectContext *context = [XMPPManager shareManager].context;
 75
 76     // 初始化请求对象
 77     NSFetchRequest *request = [[NSFetchRequest alloc] init];
 78
 79     // 获取实体
 80     NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:context];
 81
 82     // 设置查询请求的实体
 83     [request setEntity:entity];
 84
 85     // 设置谓词查询(当前用户的JID,对方用户的JID)
 86     NSPredicate *predicate = [NSPredicate predicateWithFormat:@"streamBareJidStr == %@ AND bareJidStr == %@",[XMPPManager shareManager].xmppStream.myJID.bare,self.chatToJID.bare];
 87
 88     [request setPredicate:predicate];
 89
 90     // 按照时间顺序进行排列
 91     NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:YES];
 92
 93     // 设置排序
 94     [request setSortDescriptors:@[sort]];
 95
 96     // 执行相关的操作
 97     NSArray *resultArray = [context executeFetchRequest:request error:nil];
 98
 99     // 先清空消息数组
100     [self.allMessageArray removeAllObjects];
101
102     // 在进行添加context执行的结果数组
103     [self.allMessageArray addObjectsFromArray:resultArray];
104
105     // 刷新UI
106     [self.tableView reloadData];
107
108     // 当前聊天记录跳到最后一行
109     if (self.allMessageArray.count > 0) {
110         NSIndexPath * indexPath = [NSIndexPath indexPathForRow:self.allMessageArray.count-1 inSection:0];
111         // 跳到最后一行
112         [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionBottom];
113     }
114 }
115
116
117 #pragma mark - 设置cell
118 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
119
120     return 1;
121 }
122
123 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
124
125     return self.allMessageArray.count;
126 }
127
128
129 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
130
131     ChatCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
132
133     // 千万要记住数组里存储的message对象是XMPPMessageArchiving_Message_CoreDataObject类型的
134     XMPPMessageArchiving_Message_CoreDataObject *message = [self.allMessageArray objectAtIndex:indexPath.row];
135
136     // 设置cell的相关数据
137     cell.isOut = message.isOutgoing;
138     cell.message = message.body;
139
140
141     return cell;
142 }
143
144
145 // 设置cell高度
146 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
147
148     return 70;
149 }
150
151
152 #pragma mark - XMPPStreamDelegate的相关代理方法
153 // 发送消息成功
154 - (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message {
155
156     // 重新显示相关信息
157     [self showMessage];
158 }
159
160 // 接受消息成功
161 - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message {
162
163     // 重新显示相关信息
164     [self showMessage];
165 }
166
167
168 // 发送消息失败
169 - (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error {
170
171     NSLog(@"发送消息失败");
172 }
173
174 @end

ChatCell.h

 1 #import <UIKit/UIKit.h>
 2
 3 @interface ChatCell : UITableViewCell
 4
 5 /**
 6  *  判断是对方发送过来的消息,还是自己发出的消息
 7  */
 8 @property (nonatomic, assign) BOOL isOut;
 9
10 /**
11  *  消息内容
12  */
13 @property (nonatomic, strong) NSString *message;
14
15 @end

ChatCell.m

 1 #import "ChatCell.h"
 2
 3 @interface ChatCell ()
 4
 5 @property(nonatomic,strong)UIImageView * headerImageView;
 6 @property(nonatomic,strong)UIImageView * backgroundImageView;
 7
 8 /**
 9  *  显示每一条聊天内容
10  */
11 @property(nonatomic,strong)UILabel * contentLabel;
12
13 @end
14
15
16 @implementation ChatCell
17
18 //使用懒加载创建并初始化所有UI控件
19 - (UILabel *)contentLabel{
20     if (_contentLabel == nil) {
21         _contentLabel = [[UILabel alloc] init];
22     }
23     return _contentLabel;
24 }
25 - (UIImageView *)backgroundImageView
26 {
27     if (_backgroundImageView == nil) {
28         _backgroundImageView = [[UIImageView alloc] init];
29     }
30     return _backgroundImageView;
31 }
32 - (UIImageView *)headerImageView
33 {
34     if (_headerImageView == nil) {
35         _headerImageView = [[UIImageView alloc] init];
36     }
37     return _headerImageView;
38 }
39
40
41 - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
42     self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
43     if (self) {
44         //设置cell不能选中
45         self.selectionStyle = UITableViewCellSelectionStyleNone;
46
47         [self.contentView addSubview:self.backgroundImageView];
48         [self.contentView addSubview:self.headerImageView];
49         [self.backgroundImageView addSubview:self.contentLabel];
50     }
51     return self;
52 }
53
54
55 //重写isOut的setter方法,来设定cell上的不同布局
56 - (void)setIsOut:(BOOL)isOut
57 {
58     _isOut = isOut;
59     CGRect rect = self.frame;
60     if (_isOut) {
61         self.headerImageView.frame = CGRectMake(rect.size.width-50, 10, 50, 50);
62         self.headerImageView.image = [UIImage imageNamed:@"xiaoming"];
63     }else{
64         self.headerImageView.frame = CGRectMake(0, 10, 50, 50);
65         self.headerImageView.image = [UIImage imageNamed:@"angel"];
66     }
67 }
68
69
70 //重写message方法,在cell上显示聊天记录
71 - (void)setMessage:(NSString *)message
72 {
73     if (_message != message) {
74         _message = message;
75         self.contentLabel.text = _message;
76         //        self.contentLabel.numberOfLines = 0;
77         [self.contentLabel sizeToFit];
78
79         CGRect rect = self.frame;
80         if (self.isOut) {//发出去的
81             self.backgroundImageView.image = [[UIImage imageNamed:@"chat_to"] stretchableImageWithLeftCapWidth:45 topCapHeight:40];
82             self.backgroundImageView.frame = CGRectMake(rect.size.width - self.contentLabel.frame.size.width - 50-20, 10, self.contentLabel.frame.size.width+20, rect.size.height-20);
83         }else{//接收的
84             self.backgroundImageView.image = [[UIImage imageNamed:@"chat_from"] stretchableImageWithLeftCapWidth:45 topCapHeight:40];
85             self.backgroundImageView.frame = CGRectMake(50, 10,self.contentLabel.frame.size.width+40, rect.size.height-20);
86         }
87         //因为contentLabel已经自适应文字大小,故不用设置宽高,但需要设置位置
88         self.contentLabel.center = CGPointMake(self.backgroundImageView.frame.size.width/2.0, self.backgroundImageView.frame.size.height/2.0);
89
90     }
91 }
92
93 @end
时间: 2024-10-27 14:03:27

即时通信的相关文章

iOS开发之使用XMPPFramework实现即时通信

iOS开发之使用XMPPFramework实现即时通信 关于XMPP的理论介绍在本篇博客中就不做赘述了,如何在我们之前的微信中加入XMPP协议来实现通信呢?下面将会介绍一下XMPP的基本的知识,让我们的微信可以实现互联通信.要做的准备工作是要有服务器支持XMPP协议,然后通过spark注册个测试账号,最后就可以通过XMPP用我们已有的账号和密码进行通信啦.至于如何使服务器支持XMPP协议,如何通过Spark注册账号,不是本篇博客的论述主题,本篇博客中主要是如何在我们的App中使用XMPP协议.

java即时通信小例子

学习java一段时间了,今天写来一个即时通信的小例子练手在其过程中也学到了一些知识拿出来和大家分享,请路过的各位大神多多赐教... 好了下面讲一下基本的思路: 首先,编写服务器端的程序,简单点说吧就是在服务上开启一个线程循环等待客户端的连接,为每一个连接到服务器的Socket 创建一个实例存放在集合中,然后使用InputSteam读取从客户端发过来的数据然后转发给除自己之外的所有的客户端 然后是客户端,首先开启一个线程连接都服务器端,循环监听服务器发送回来的数据 这里要说一下为了明确是谁发出的消

结束QQ即时通信垄断,开辟即时通信互联互通instantnet时代

蓬勃发展的即时通信产业 即时通信(IM)是指能够即时发送和接收互联网消息等的业务. 即时通信,就是瞬间把信息发送给对方,如果不是即时发送的,我们就说离线的,email邮件式的,不是立即看见的. 即时通信开始是短消息的,也可以发长文件,微博也是短消息的,传文件我们一般不叫即时通信. 不过即时通信已经发展成集交流.资讯.娱乐.搜索.电子商务.办公协作和企业客户服务等为一体的综合化信息平台. 这样即时通信范围就大了,但是这时候已经是一个综合化信息平台,不是单纯的发送消息. 我们目前还是定位在即时通信就

C语言 linux环境基于socket的简易即时通信程序

转载请注明出处:http://www.cnblogs.com/kevince/p/3891033.html   By Kevince 最近在看linux网络编程相关,现学现卖,就写了一个简易的C/S即时通信程序,代码如下: head.h 1 /*头文件,client和server编译时都需要使用*/ 2 #include <unistd.h> 3 #include <stdio.h> 4 #include <sys/types.h> 5 #include <sys

[C# 网络编程系列]专题九:实现类似QQ的即时通信程序

转自:http://www.cnblogs.com/zhili/archive/2012/09/23/2666987.html 引言: 前面专题中介绍了UDP.TCP和P2P编程,并且通过一些小的示例来让大家更好的理解它们的工作原理以及怎样.Net类库去实现它们的.为了让大家更好的理解我们平常中常见的软件QQ的工作原理,所以在本专题中将利用前面专题介绍的知识来实现一个类似QQ的聊天程序. 一.即时通信系统 在我们的生活中经常使用即时通信的软件,我们经常接触到的有:QQ.阿里旺旺.MSN等等.这些

Android IM即时通信开发总结

IM即时通信的最大成功实例应该就是微信了吧,,,这次的IM即时通信是建立在Bmob上的,借用了人家的服务端和IM.Android SDK 传送: Andorid快速入门 详细开发文档 IM快速入门 它的文档组织不是很好,我觉得基本上就是这三个链接比较好,所有的链接都来自这个页面 http://docs.bmob.cn/android/developdoc/index.html?menukey=develop_doc&key=develop_android 然后就是要自己看类库文档,不得不说,写得

iOS学习43即时通信之XMPP(2)

本篇是 即时通信之XMPP(2) 接上次 即时通信之XMPP(1) 1. 好友列表 1> 初始化好友花名册 // 获取管理好友的单例对象 XMPPRosterCoreDataStorage *rosterStorage = [XMPPRosterCoreDataStorage sharedInstance]; // 给roster属性进行初始化 self.xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:rosterStorage di

基于mina的的android即时通信app

前段时间研究mina框架,发现很适合做即时通信后台,经过几个月的研发本人开发了一套基于mina框架的android即时通信app,暂命名为E聊.鉴于本人能力有限还存在不少bug,希望大家原谅并指出,bug和部分功能还在不断完善中,过段时间部分代码会进行开源,敬请关注. 主要功能包括: 1.私聊,群聊 2.支持文字.语音.图片.文件.小视频等 3.位置共享(开发中) 4.朋友圈(图文发布.评论等,小视频功能正在开发中) 5.上线通知,断线重连 6.接收离线消息 7.更多功能正在筹划中... 演示图

.NET 即时通信,WebSocket服务端实例

即时通信常用手段 1.第三方平台 谷歌.腾讯 环信等多如牛毛,其中谷歌即时通信是免费的,但免费就是免费的并不好用.其他的一些第三方一般收费的,使用要则限流(1s/限制x条消息)要么则限制用户数. 但稳定性什么都还不错,又能将服务压力甩出 2.System.Net.Sockets.Socket,也能写一套较好的服务器端.在.NET 4.5之前用较多,使用起来麻烦.需要对数据包进行解析等操作(但貌似网上有对超长包的处理方法) 3.System.Net.WebSockets.WebSocket,这个,

基于XMPP协议的Android即时通信系

以前做过一个基于XMPP协议的聊天社交软件,总结了一下.发出来. 设计基于开源的XMPP即时通信协议,采用C/S体系结构,通过GPRS无线网络用TCP协议连接到服务器,以架设开源的Openfn'e服务器作为即时通讯平台. 系统主要由以下部分组成:一是服务器,负责管理发出的连接或者与其他实体的会话,接收或转发XML(ExtensibleMarkup Language)流元素给授权的客户端.服务器等:二是客户终端.它与服务器相连,通过XMPP获得由服务器或任何其它相关的服务所提供的全部功能.三是协议