私はこれに一日中立ち往生しています。 David Heinemeier Hanssonによる非常にシンプルなActionCableサンプルアプリ(チャットアプリ)が正しく機能しています( https://www.youtube.com/watch?v=n0WUjGkDFS )。
IPhoneアプリでWebSocket接続を確立しようとしています。 ws://localhost:3000/cable
に接続するとpingを受信できますが、JavaScriptコンテキストの外部からチャネルをサブスクライブする方法がよくわかりません。
ああ、私もこの質問を読んだ後、この問題を経験しました。
しばらくして、私はついにこの魔法のGithub問題ページを見つけました:
https://github.com/Rails/rails/issues/22675
このパッチがいくつかのテストに失敗することを私は理解しています。それは私にとって驚くべきことではありません。しかし、私が信じている元の問題は依然として関連性があり、閉じるべきではありません。
サーバーに送信された次のJSONは成功するはずです。
{"command": "subscribe"、 "identifier":{"channel": "ChangesChannel"}}
そうではありません!代わりに、これを送信する必要があります。
{"command": "subscribe"、 "identifier": "{\" channel\":\" ChangesChannel\"}"}
Rails問題についてのGithubユーザーの提案に従って、iOSアプリをルームチャネルにサブスクライブするようになりました。
私の設定は次のとおりです。
Cocoapodsを使用してPocketSocketをインストールする方法を知っていると思います。
関連するコードは次のとおりです。
#import <PocketSocket/PSWebSocket.h>
@interface ViewController : UIViewController <PSWebSocketDelegate, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate>
@property (nonatomic, strong) PSWebSocket *socket;
- (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のどこかにあるリターンキーまたは「送信」ボタンの押下を処理していることを前提としています。
このデモアプリ全体は簡単なハックでした。明らかに、実際のアプリでそれを行う場合は、コードをよりクリーンで再利用可能にし、クラスにまとめて抽象化します。
IPhoneアプリがRails iPhoneシミュレーターではなく、実際のデバイス上のサーバーと通信するため。
以下をせよ:
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
私は次のリンクからこれらの解決策を得ました:
リクエストオリジンは許可されていません:Rails5とActionCableを使用している場合はhttp:// localhost:3001
Rails 4.2サーバー、プライベートおよびパブリックIPが機能しない
それが将来他の人を助けることを願っています。
//最初にソケット接続を開きます
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」関数内でデータを受け取ります。
乾杯!
実際、これが私がアクションケーブルに接続するために使用しているコードスニペットです。
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;
};
}