Подтвердить что ты не робот

Подключение к ActionCable из приложения iOS

Я весь день застрял в этом. У меня очень простое приложение ActionCable (приложение для чата) Дэвида Хайннемайера Хансона, работающего правильно (https://www.youtube.com/watch?v=n0WUjGkDFS0).

Я пытаюсь подключиться к веб-подключению с помощью приложения для iPhone. Я могу получать сообщения, когда я подключаюсь к ws://localhost:3000/cable, но я не совсем уверен, как подписаться на каналы из-за контекста javascript.

4b9b3361

Ответ 1

О, человек, я тоже прошел эту проблему после прочтения этого вопроса.

Через некоторое время я наконец нашел эту магическую страницу Github:

https://github.com/rails/rails/issues/22675

Я понимаю, что этот патч нарушит некоторые тесты. Это не удивительный для меня. Но исходный вопрос, который, как мне кажется, по-прежнему актуальен и не должны быть закрыты.

Следующий JSON, отправленный на сервер, должен преуспеть:

{ "command": "subscribe", "identifier": { "channel": "ChangesChannel" }}

Это не так! Вместо этого вы должны отправить это:

{ "Команда": "Подписаться", "идентификатор": "{\" канал \ ": \" ChangesChannel\ "}" }

Наконец-то я получил приложение iOS, чтобы подписаться на канал комнаты после предложения пользователя Github о проблеме с Rails.

Моя настройка такова:

  • Цель 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 используется "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 при использовании Rails5 и ActionCable

сервер Rails 4.2; закрытый и открытый ip не работает

Надеюсь, что это поможет другим в будущем.

Ответ 2

//сначала открыть соединение сокетов

var ws = new WebSocket("ws://localhost:3000/cable");  

//подписка на канал
// 'i' должен находиться в json

var i = { 'command': 'subscribe', 'identifier': {'channel':'ProfileChannel', 'Param_1': 'Value_1',...}};

 ws.send(i);

//После этого вы получите данные внутри функции onmessage.

Ура!

Ответ 3

На самом деле, вот фрагмент кода, который я использую для подключения к экрану действий.

  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": "[email protected]",  "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;
         };
      }