Подключение к ActionCable из приложения iOS
Я застрял на этом весь день. У меня очень простое примерное приложение ActionCable (приложение чата) от David Heinemeier Hansson, работающее правильно ( https://www.youtube.com/watch?v=n0WUjGkDFS0).
Я пытаюсь подключиться к интернет-сокету с помощью приложения для iPhone. Я могу получить пинг при подключении к ws://localhost:3000/cable
, но я не совсем уверен, как подписаться на каналы вне контекста JavaScript.
3 ответа
О человек, я тоже прошел через эту проблему после прочтения этого вопроса.
Через некоторое время я наконец нашел эту волшебную страницу с Github:
https://github.com/rails/rails/issues/22675
Я понимаю, что этот патч сломает некоторые тесты. Это не удивительно для меня. Но оригинальный вопрос, который я считаю, все еще актуален и не должен закрываться.
Следующий JSON, отправленный на сервер, должен быть успешным:
{"команда": "подписаться","идентификатор":{"канал":"ChangesChannel"}}
Это не! Вместо этого вы должны отправить это:
{"команда": "подписаться","идентификатор":"{\" канал \":\"ChangesChannel\"}"}
Я наконец-то получил приложение для iOS, чтобы подписаться на канал комнаты, следуя предложению пользователя Github о проблеме Rails.
Моя установка выглядит следующим образом:
- Цель С
- Использование платформы PocketSocket для подключения к веб-сокету
- Рельсы 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]]
Нам нужно использовать имя класса для канала.
В видео на YouTube DHH в качестве имени комнаты используется 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 для обработки нажатия клавиши возврата или какой-либо кнопки "отправить" где-то в вашем пользовательском интерфейсе.
Это целое демонстрационное приложение было быстрым взломом, очевидно, если бы я делал это в реальном приложении, я бы сделал свой код более чистым, более пригодным для повторного использования и полностью абстрагировал бы его в класс.
Подключение к серверу Rails с реального устройства iPhone:
Чтобы приложение 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
Я получил эти решения по следующим ссылкам:
Запрещен источник запроса: http://localhost:3001 при использовании Rails 5 и ActionCable
Сервер Rails 4.2; частный и публичный ip не работает
Надеюсь, что это поможет другим в будущем.
// сначала открываем сокет
var ws = new WebSocket("ws://localhost:3000/cable");
// подписаться на канал
// "Я" должен быть в JSON
var i = { 'command': 'subscribe', 'identifier': {'channel':'ProfileChannel', 'Param_1': 'Value_1',...}};
ws.send(i);
// После этого вы получите данные внутри функции onmessage.
Ура!
На самом деле, вот фрагмент кода, который я использую для подключения к действию кабеля.
function WebSocketTest()
{
var ws = new WebSocket("ws://localhost:3000/cable");
ws.onopen = function(data)
{
var i = JSON.stringify({"command":"subscribe" , "identifier": JSON.stringify({"channel":"CHANNEL_NAME"})});
// send data request
var j = JSON.stringify({"command":"message","identifier": JSON.stringify({"channel":"CHANNEL_NAME"}),"data": {"message":"Hello World","action": "METHOD_NAME_IN_CHANNEL","email": "abc@xyz.com", "token" : "xxxxxxxxxxxxx", "id": {"id_message" : "something", "ddd" : "something"}}})
var response = ws.send(i);
setTimeout(function()
{
var response1 = ws.send(j);
}, 1000);
};
ws.onmessage = function (evt)
{
var received_msg = evt.data;
};
}