[{"data":1,"prerenderedAt":339},["ShallowReactive",2],{"news-item-\u002Fvi\u002Fnews\u002Fvideochat-twilio":3},{"id":4,"title":5,"body":6,"category":324,"created by":327,"date":328,"description":329,"extension":330,"meta":331,"navigation":332,"path":333,"sections":334,"seo":335,"stem":336,"thumbnail":337,"__hash__":338},"content_vi\u002Fvi\u002Fnews\u002Fvideochat-twilio.md","Tạo ứng dụng chat voice sử dụng Twilio SDK",{"type":7,"value":8,"toc":318},"minimark",[9,14,24,27,34,40,49,53,61,69,77,80,90,93,97,100,106,113,116,121,130,133,137,141,145,152,155,159,164,167,177,180,186,189,195,201,207,213,216,234,253,259,266,270,273,279,294,297,300,305,312],[10,11,13],"h2",{"id":12},"lời-giới-thiệu","Lời giới thiệu",[15,16,17,18,23],"p",{},"Như đã đề cập ở ",[19,20,22],"a",{"href":21},"videochat-skyway","bài viết trước",", hôm nay chúng tôi sẽ hướng dẫn bạn xây dựng 1 ứng dụng iOS chat voice sử dụng Twilio Video SDK.",[15,25,26],{},"Tổng quan, Twilio Video nổi tiếng là 1 nền tảng cung cấp rất nhiều dịch vụ để xây dựng các trải nghiệm về video: chat video(voice) thời gian thực, chia sẻ màn hình. Trên thế giới, nó nhận được khá nhiều đánh giá tích cực từ người dùng.",[15,28,29],{},[19,30,31],{"href":31,"rel":32},"https:\u002F\u002Fwww.g2.com\u002Fproducts\u002Ftwilio\u002Freviews",[33],"nofollow",[15,35,36],{},[19,37,38],{"href":38,"rel":39},"https:\u002F\u002Fwww.trustradius.com\u002Fproducts\u002Ftwilio-video\u002Freviews",[33],[15,41,42,43,48],{},"Trong bài viết này, chúng ta sẽ thử sử dụng ",[19,44,47],{"href":45,"rel":46},"https:\u002F\u002Fwww.twilio.com\u002Fdocs\u002Fvideo",[33],"Video API"," để xây dụng 1 ứng dụng chat voice.",[10,50,52],{"id":51},"tạo-1-dự-án-trên-twilio","Tạo 1 dự án trên Twilio",[15,54,55,56],{},"Tạo 1 tài khoản trên ",[19,57,60],{"href":58,"rel":59},"https:\u002F\u002Fwww.twilio.com\u002Ftry-twilio",[33],"Twillo",[15,62,63,64],{},"Mở trang ",[19,65,68],{"href":66,"rel":67},"https:\u002F\u002Fwww.twilio.com\u002Fconsole",[33],"console",[15,70,71,72],{},"Tạo 1 dự án tên \"TwilioDemoChat\" hay bất kỳ tên nào mà bạn muốn tại ",[19,73,76],{"href":74,"rel":75},"https:\u002F\u002Fwww.twilio.com\u002Fconsole\u002Fprojects\u002Fcreate",[33],"link",[15,78,79],{},"Sau khi tạo dự án, sẽ hiện ra trang chào mừng",[81,82],"img",{"className":83,"alt":87,"src":88,"style":89},[84,85,86],"block","mx-auto","mb-4","","https:\u002F\u002Fhomepage-media.s3.ap-southeast-1.amazonaws.com\u002Fwp-content\u002Fuploads\u002F2020\u002F07\u002F17150511\u002FScreen-Shot-2020-07-17-at-15.03.20-1024x528.png","width: 100%;",[15,91,92],{},"Chọn \"Other\" và di chuyển đến trang dashboard để hoàn thành bước tạo dự án.",[81,94],{"className":95,"alt":87,"src":96,"style":89},[84,85,86],"https:\u002F\u002Fhomepage-media.s3.ap-southeast-1.amazonaws.com\u002Fwp-content\u002Fuploads\u002F2020\u002F07\u002F17150844\u002FScreen-Shot-2020-07-17-at-15.07.58-1024x396.png",[15,98,99],{},"Lưu ACCOUNT SID  và AUTH TOKEN  vào 1 file để sau này sử dụng.",[15,101,102],{},[103,104,105],"strong",{},"Tạo API Key",[15,107,108],{},[19,109,112],{"href":110,"rel":111},"https:\u002F\u002Fwww.twilio.com\u002Fconsole\u002Fvideo\u002Fproject\u002Fapi-keys\u002Fcreate",[33],"Tham khảo",[15,114,115],{},"Tạo 1 api key mới và lưu giữ cẩn thận, bởi vì nó chỉ hiển thị đúng 1 LẦN duy nhất.",[15,117,118],{},[103,119,120],{},"Tạo Access Tokens",[15,122,123,124,129],{},"Tại ",[19,125,128],{"href":126,"rel":127},"https:\u002F\u002Fwww.twilio.com\u002Fconsole\u002Fvideo\u002Fproject\u002Ftesting-tools",[33],"đường dẫn này"," tạo 2 access token mới, bởi vì chúng ta sẽ test với 2 user cùng kết nối tới cùng 1 room tên là \"TwilioDemoChatRoom\" and có các định danh sau \"TwilioDemoChatRoomUser1\" và \"TwilioDemoChatRoomUser2\", sau đó bấm vào nút \"Generate Access Token\" để lấy access token, lưu chúng lại để sử dụng cho iOS app.",[15,131,132],{},"CHÚ Ý: Ở môi trường release, bước này sẽ được phía server đảm nhiệm, nó sẽ tạo ra từng access token cho từng user sử dụng ở mỗi room, 1 access token  là 1 chứng thực ngắn hạn để xác nhận user tới server Twilio.",[81,134],{"className":135,"alt":87,"src":136,"style":89},[84,85,86],"https:\u002F\u002Fhomepage-media.s3.ap-southeast-1.amazonaws.com\u002Fwp-content\u002Fuploads\u002F2020\u002F07\u002F17165611\u002FScreen-Shot-2020-07-17-at-16.55.26-1024x279.png",[81,138],{"className":139,"alt":87,"src":140,"style":89},[84,85,86],"https:\u002F\u002Fhomepage-media.s3.ap-southeast-1.amazonaws.com\u002Fwp-content\u002Fuploads\u002F2020\u002F07\u002F17165657\u002FScreen-Shot-2020-07-17-at-16.56.39-1024x240.png",[10,142,144],{"id":143},"tạo-ứng-dụng-voice-chat-trên-ios","Tạo ứng dụng voice chat trên iOS",[15,146,147],{},[19,148,151],{"href":149,"rel":150},"https:\u002F\u002Fgithub.com\u002Ftwilio\u002Ftwilio-video-app-ios",[33],"Hướng dẫn",[15,153,154],{},"Tạo 1 dự án mới tên là \"TwilioDemoChat\"",[81,156],{"className":157,"alt":87,"src":158,"style":89},[84,85,86],"https:\u002F\u002Fhomepage-media.s3.ap-southeast-1.amazonaws.com\u002Fwp-content\u002Fuploads\u002F2020\u002F07\u002F17153400\u002FScreen-Shot-2020-07-17-at-15.33.09-1024x754.png",[15,160,161],{},[103,162,163],{},"Cấu hình CocoaPods",[15,165,166],{},"Tạo 1 file rỗng đặt tên là PodFile đặt tại thư mục gốc của dự án, sau đó dán đoạn code sau vào :",[168,169,174],"pre",{"className":170,"code":172,"language":173},[171],"language-text","platform :ios,'11.0'\nuse_frameworks!\n\ndef install_pods\n  pod 'TwilioVideo'\n  pod 'MBProgressHUD'\nend\n\ntarget 'TwilioDemoChat' do\n  install_pods\nend\n","text",[175,176,172],"code",{"__ignoreMap":87},[15,178,179],{},"Mở ứng dụng Terminal  chạy dòng lệnh sau ở thư mục gốc dự án để cài đặt TwilioVideo SDK:",[168,181,184],{"className":182,"code":183,"language":173},[171],"pod install\n",[175,185,183],{"__ignoreMap":87},[15,187,188],{},"Bởi vì ứng dụng chúng ta cần sử dụng micro để chat voice nên chúng ta cần thêm quyền hạn sử dụng micro vào file info.plist",[168,190,193],{"className":191,"code":192,"language":173},[171],"NSMicrophoneUsageDescription\nỨng dụng demo này sử dụng micro để chat thoại\n",[175,194,192],{"__ignoreMap":87},[15,196,197,198],{}," ",[103,199,200],{},"Áp dụng Twilio",[15,202,203,204],{},"Trước tiên chúng ta tạo 1 controller để xử lý việc tham gia room tên là ",[103,205,206],{},"TwilioController.swift",[168,208,211],{"className":209,"code":210,"language":173},[171],"import UIKit\nimport TwilioVideo\nimport MBProgressHUD\n\nlet TWILIO_TEST_ROOM = \"TwilioDemoChatRoom\"\n\nlet TWILIO_ACCESS_TOKEN_1 = \"access_token_tạo_ra_từ_bước_Tạo_Access_Token\"\nlet TWILIO_ACCESS_TOKEN_2 = \"access_token_tạo_ra_từ_bước_Tạo_Access_Token\"\n\nlet kTwilioController = TwilioController.shared\n\npublic enum RoomStatus{\n    case Connected\n    case Disconnected\n    case ParticipantConnected\n    case ParticipantDisconnected\n    case Error\n}\n\npublic typealias RoomStatusUpdatedHandler = (RoomStatus) -> (Void)\n\nclass TwilioController: NSObject {\n    static let shared = TwilioController()\n    \n    var room:Room? = nil\n    var connected = false\n    var roomStatusHandler:RoomStatusUpdatedHandler?\n    \n    private var localAudioTrack = LocalAudioTrack()\n\n    \u002F\u002Fkết nối tới room được định nghĩa ở hằng số TWILIO_TEST_ROOM\n    func  connectToRoom(handler:RoomStatusUpdatedHandler?) {\n        roomStatusHandler = handler \n        \u002F\u002Fthay đổi access token cho từng device\n        let connectOptions = ConnectOptions(token: TWILIO_ACCESS_TOKEN_2) { (builder) in\n            builder.roomName = TWILIO_TEST_ROOM\n            \n            \u002F\u002Fcấu hình âm thanh\n            if let audioTrack = self.localAudioTrack {\n                builder.audioTracks = [ audioTrack ]\n            }\n        }\n        room = TwilioVideoSDK.connect(options: connectOptions, delegate: self)\n    }\n    \n    func disconnectFromRoom()  {\n        room?.disconnect()\n    }\n    \n    \u002F\u002F MARK: - các hàm nội bộ\n    private func toastMessage(message:String){\n        print(\"toastMessage \\(message)\")\n        DispatchQueue.main.asyncAfter(deadline: .now() ) {\n            let hud = MBProgressHUD.showAdded(to: UIApplication.shared.keyWindow!, animated: true)\n            hud.mode = .text\n            hud.label.text = message\n            hud.removeFromSuperViewOnHide = true\n            hud.hide(animated: true, afterDelay: 2)\n        }\n    }\n}\n\nextension TwilioController : RoomDelegate{\n    \n    \u002F\u002Fsự kiện khi user đã kết nối tới room\n    func roomDidConnect(room: Room) {\n        connected = true\n        roomStatusHandler?(.Connected)\n    }\n    \n    \u002F\u002Fsự kiện khi user đã rời khỏi room\n    func roomDidDisconnect(room: Room, error: Error?) {\n        connected = false\n        roomStatusHandler?(.Disconnected)\n    }\n    \n    \u002F\u002Fsự kiện khi user không thể kết nối room, bởi vì lỗi timeout hay kết nối mạng...\n    func roomDidFailToConnect(room: Room, error: Error) {\n        connected = false\n        roomStatusHandler?(.Error)\n    }\n    \n    \u002F\u002Fsự kiện khi người khác tham gia room\n    func participantDidConnect(room: Room, participant: RemoteParticipant) {\n        let message = \"participant \\(participant.identity) connected\"\n        toastMessage(message: message)\n        roomStatusHandler?(.ParticipantConnected)\n    }\n    \n    \u002F\u002Fsự kiện khi người tham gia rời room\n    func participantDidDisconnect(room: Room, participant: RemoteParticipant) {\n        let message = \"participant \\(participant.identity) disconnected\"\n        toastMessage(message: message)\n        roomStatusHandler?(.ParticipantDisconnected)\n    }\n}\n",[175,212,210],{"__ignoreMap":87},[15,214,215],{},"Controller này chủ yếu có 2 hàm chính:",[15,217,218,221,222,225,226,229,230,233],{},[103,219,220],{},"connectToRoom","(): kết nối tới room cụ thể được chỉ định bởi hằng số ",[103,223,224],{},"TWILIO_TEST_ROOM"," và access token (",[103,227,228],{},"TWILIO_ACCESS_TOKEN_1","  hay ",[103,231,232],{},"TWILIO_ACCESS_TOKEN_2","). Nếu bạn chạy trên thiết bị thì hãy set access token khác nhau cho từng thiết bị. Ngoài ra function này còn xử lý các sự kiện sau:",[235,236,237,241,244,247,250],"ul",{},[238,239,240],"li",{},"Connected: sự kiện khi user đã kết nối tới room",[238,242,243],{},"Disconnected: sự kiện khi user đã rời khỏi room",[238,245,246],{},"ParticipantConnected: sự kiện khi người khác tham gia room",[238,248,249],{},"ParticipantDisconnected: sự kiện khi người tham gia rời room",[238,251,252],{},"roomDidFailToConnect: sự kiện khi user không thể kết nối room, bởi vì lỗi timeout hay kết nối mạng...",[15,254,255,258],{},[103,256,257],{},"disconnectFromRoom","():  rời khỏi room",[15,260,261,262,265],{},"Sau đó chúng ta tạo 1 view controller tên là ",[103,263,264],{},"CallViewController"," có UI đơn giản nhu sau:",[81,267],{"className":268,"alt":87,"src":269,"style":89},[84,85,86],"https:\u002F\u002Fhomepage-media.s3.ap-southeast-1.amazonaws.com\u002Fwp-content\u002Fuploads\u002F2020\u002F07\u002F20110441\u002Ftwilio_chat.png",[15,271,272],{},"Trong file CallViewController.swift  chúng ta dán đoạn code sau vào:",[168,274,277],{"className":275,"code":276,"language":173},[171],"import UIKit\nimport MBProgressHUD\n\nclass CallViewController: UIViewController {\n    \n    @IBOutlet weak var joinButton: UIButton!\n    @IBOutlet weak var roomTxt: UITextField!\n        \n    \u002F\u002Fkhi trạng thái tham gia room thay đổi, cập nhật tiêu đề tương ứng cho button tham gia\n    private var joined = false{\n        didSet{\n            if joined {\n                joinButton.setTitle(\"End call\", for: .normal)\n            }else{\n                joinButton.setTitle(\"Join\", for: .normal)\n            }\n        }\n    }\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        roomTxt.text = TWILIO_TEST_ROOM\n                \n    }\n    \n    @IBAction func joinRoom(_ sender: Any) {\n        if !joined{\n            MBProgressHUD.showAdded(to: self.view, animated: true)\n            kTwilioController.connectToRoom { (status) -> (Void) in\n                if status == .Connected {\n                    self.joined = true\n                }\n                if status == .Disconnected{\n                    self.joined = false\n                }\n                MBProgressHUD.hide(for: self.view, animated: true)\n            }\n            \n        }else{\n            kTwilioController.disconnectFromRoom()\n        }\n\n    }\n        \n}\n",[175,278,276],{"__ignoreMap":87},[15,280,281,282,285,286,289,290,293],{},"Đơn giản chúng ta có 1 textfield để hiển thị tên room kết nối (TwilioDemoChatRoom, và không thể chỉnh sửa được),  với 1  button để  ",[103,283,284],{},"Join"," hay ",[103,287,288],{},"End Call"," ( Tuỳ vào trạng thái tham gia room mà chúng ta cập nhật tiêu đề button cho phù hợp để gọi  hàm ",[103,291,292],{},"connectToRoom() hay disconnectFromRoom()"," ).",[15,295,296],{},"Bây giờ bạn hãy chạy dự án trên 2 thiết bị iOS (không hỗ trợ máy ảo) để xem nó hoạt động như thế nào.",[15,298,299],{},"Nhớ đổi access token cho từng thiết bị khi bắt đầu chạy dự án.",[15,301,302],{},[103,303,304],{},"Tham khảo:",[15,306,307],{},[19,308,311],{"href":309,"rel":310},"http:\u002F\u002Ftwilio.com\u002Fdocs\u002Fvideo\u002Fios-v1-getting-started",[33],"Twilio Video iOS Tutorial",[15,313,314],{},[19,315,317],{"href":66,"rel":316},[33],"Twilio Console",{"title":87,"searchDepth":319,"depth":319,"links":320},2,[321,322,323],{"id":12,"depth":319,"text":13},{"id":51,"depth":319,"text":52},{"id":143,"depth":319,"text":144},[325,326],"tech talk","mobile","Briswell Vietnam Co Ltd","2020-08-13","Như đã đề cập ở bài viết trước, hôm nay chúng tôi sẽ hướng dẫn bạn xây dựng 1 ứng dụng iOS chat voice sử dụng Twilio Video SDK.","md",{},true,"\u002Fvi\u002Fnews\u002Fvideochat-twilio",null,{"title":5,"description":329},"vi\u002Fnews\u002Fvideochat-twilio","https:\u002F\u002Fhomepage-media.s3.ap-southeast-1.amazonaws.com\u002Fwp-content\u002Fuploads\u002F2020\u002F08\u002F13161934\u002Ftwilio.png","MQv8StcUSoxjSogEiEJMXpvM2eA6BVOWXOXUdJoih1E",1785320528234]