哦,伙计,读完这个问题后,我也遇到了这个问题。
折腾了一阵子,终于找到了这个神奇的 Github issue page:
https://github.com/rails/rails/issues/22675
我明白这个补丁会破坏一些测试。那不是
令我惊讶。但我认为最初的问题仍然相关
并且不应关闭。
以下 JSON 发送到服务器应该会成功:
{"command": "subscribe","identifier":{"channel":"ChangesChannel"}}
没有!相反,您必须发送:
{“命令”:
"订阅","标识符":"{\"频道\":\"ChangesChannel\"}"}
根据 Github 用户关于 Rails 问题的建议,我终于让 iOS 应用订阅房间频道。
我的设置如下:
- 目标 C
- 使用 PocketSocket 框架进行网络套接字连接
- Rails 5 RC1
- Ruby 2.2.4p230
我假设你知道如何使用 Cocoapods 安装 PocketSocket。
相关代码如下:
ViewController.h
#import <PocketSocket/PSWebSocket.h>
@interface ViewController : UIViewController <PSWebSocketDelegate, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate>
@property (nonatomic, strong) PSWebSocket *socket;
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initViews];
[self initConstraints];
[self initSocket];
}
-(void)initSocket
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"ws://localhost:3000/cable"]];
self.socket = [PSWebSocket clientSocketWithRequest:request];
self.socket.delegate = self;
[self.socket open];
}
-(void)joinChannel:(NSString *)channelName
{
NSString *strChannel = @"{ \"channel\": \"RoomChannel\" }";
id data = @{
@"command": @"subscribe",
@"identifier": strChannel
};
NSData * jsonData = [NSJSONSerialization dataWithJSONObject:data options:0 error:nil];
NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(@"myString= %@", myString);
[self.socket send:myString];
}
#pragma mark - PSWebSocketDelegate Methods -
-(void)webSocketDidOpen:(PSWebSocket *)webSocket
{
NSLog(@"The websocket handshake completed and is now open!");
[self joinChannel:@"RoomChannel"];
}
-(void)webSocket:(PSWebSocket *)webSocket didReceiveMessage:(id)message
{
NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *messageType = json[@"type"];
if(![messageType isEqualToString:@"ping"] && ![messageType isEqualToString:@"welcome"])
{
NSLog(@"The websocket received a message: %@", json[@"message"]);
[self.messages addObject:json[@"message"]];
[self.tableView reloadData];
}
}
-(void)webSocket:(PSWebSocket *)webSocket didFailWithError:(NSError *)error
{
NSLog(@"The websocket handshake/connection failed with an error: %@", error);
}
-(void)webSocket:(PSWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean
{
NSLog(@"The websocket closed with code: %@, reason: %@, wasClean: %@", @(code), reason, (wasClean) ? @"YES": @"NO");
}
重要提示:
我还对订阅类源代码进行了一点挖掘:
def add(data)
id_key = data['identifier']
id_options = ActiveSupport::JSON.decode(id_key).with_indifferent_access
subscription_klass = connection.server.channel_classes[id_options[:channel]]
if subscription_klass
subscriptions[id_key] ||= subscription_klass.new(connection, id_key, id_options)
else
logger.error "Subscription class not found (#{data.inspect})"
end
end
注意这一行:
connection.server.channel_classes[id_options[:channel]]
我们需要使用频道的类名。
DHH youtube 视频使用“room_channel”作为房间名称,但该频道的类文件名为“RoomChannel”。
我们需要使用类名而不是频道的实例名。
发送消息
以防其他人也想知道如何发送消息,这是我向服务器发送消息的 iOS 代码:
-(void)sendMessage:(NSString *)message
{
NSString *strMessage = [[NSString alloc] initWithFormat:@"{ \"action\": \"speak\", \"message\": \"%@\" }", message];
NSString *strChannel = @"{ \"channel\": \"RoomChannel\" }";
id data = @{
@"command": @"message",
@"identifier": strChannel,
@"data": strMessage
};
NSData * jsonData = [NSJSONSerialization dataWithJSONObject:data options:0 error:nil];
NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(@"myString= %@", myString);
[self.socket send:myString];
}
这假设你已经连接了你的 UITextField 来处理按下返回键或者你 UI 上某处的一些“发送”按钮。
整个演示应用程序是一个快速的 hack,显然,如果我要在一个真正的应用程序中完成它,我会让我的代码更简洁、更可重用并将其抽象为一个类。
从真实的 iPhone 设备连接到 Rails 服务器:
为了让 iPhone 应用与真实设备上的 Rails 服务器通信,而不是 iPhone 模拟器。
执行以下操作:
- 检查计算机的 TCP/IP 地址。例如,在我的 iMac 上,有时可能是 10.1.1.10(如果使用 DHCP,将来会自动更改)。
-
编辑 Rail 的 config > environment > development.rb 文件,并将以下行放在 end 关键字之前的某处:
Rails.application.config.action_cable.allowed_request_origins = ['http://10.1.1.10:3000']
-
使用以下命令启动 Rails 服务器:
rails server -b 0.0.0.0
在 iPhone 设备上构建并运行您的 iPhone 应用程序。您现在应该可以连接和发送消息了:D
我从以下链接获得了这些解决方案:
Request origin not allowed: http://localhost:3001 when using Rails5 and ActionCable
Rails 4.2 server; private and public ip not working
希望将来能帮助其他人。