Skip to content

Latest commit

 

History

History

(back to top)

Usage

This sample showcases how to send audio recordings.

Subscribe features Description
.message Callback to receive all messages of a subscribed channel
.presence Callback to get the users states (e.g join/leave/userstates) of a channel

Sample Code

Initialize the Agora RTM SDK

// Initialize the Agora RTM SDK
let config = AgoraRtmClientConfig(appId: "your_app_id" , userId: "user_id")
var agoraRtmKit: AgoraRtmClientKit = try AgoraRtmClientKit(config, delegate: self)

Login to Agora Server

// Login to Agora Server
if let (response, error) = await agoraRtmKit?.login("user_token") {
    if error == nil{
       // Login successful
    }else{
      // Login failed
    }
} else {
    // Login failed
}

Subscribe to a Channel

// Define the subscription feature
let subOptions: AgoraRtmSubscribeOptions = AgoraRtmSubscribeOptions()
subOptions.features =  [.message, .presence]

// Subscribe to a channel  
if let (response, error) = await agoraRtmKit?.subscribe(channelName: channelName, option: subOptions){
    if error == nil{
       // Subscribe successful
    }else{
      // Subscribe failed
    }
}

File Functions

Publish The File

@MainActor
func publishToChannel(channelName: String, fileURL: URL) async -> Bool{
    
    if let fileData = convertFileToData(fileURL: fileURL), !fileURL.pathExtension.isEmpty {
        // Split the file into 32KB chunks
        let dataChunks = splitDataIntoChunks(data: fileData)
        
        // PART 1 :  First send the file info to receivers to let them know there is an incoming  file with filesize dataChunks.count
        let pubOptions = AgoraRtmPublishOptions()
        pubOptions.channelType = .message
        pubOptions.customType = fileInfoKey
        
        let fileInfo = FileInfo(id: UUID(), name: fileURL.lastPathComponent, countOf32KB: dataChunks.count, type: fileURL.pathExtension, url: "", owner: userID)
        
        if let JSONString = convertObjectToJsonString(object: fileInfo) {
            if let (_, error) = await agoraRtmKit?.publish(channelName: channelName, message: JSONString, option: pubOptions){
                if error == nil {
                    // Success - Add local record
                }else{
                    // Failed
                    return false
                }
            }
        }

        // PART 2 : Send the chunk files one by one
        let pubOptions2 = AgoraRtmPublishOptions()
        pubOptions2.channelType = .message
        pubOptions2.customType = fileChunkKey

        // Publish each chunk one-by-one
        for dataChunk in dataChunks {
            if let (_, error) = await agoraRtmKit?.publish(channelName: channelName, data: dataChunk, option: pubOptions2){
                if error == nil {
                // Publish successful 
                    
                }else{
                // Publish failed 
                }
            }
        }
        
        return true
    }
    
    return false
}

Relevant Functions

// MARK: FUNCTION TO RETRIEVE THE ACTUAL FILES ON LOCAL DIRECTORY
func getDocumentsDirectory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    return paths[0]
    
}

// MARK: FILE AND DATA CONVERSION
func convertFileToData(fileURL: URL) -> Data? {
    do {
        let fileData = try Data(contentsOf: fileURL)
        return fileData
    } catch {
        print("Failed to get data from file")
    }
    return nil
}

// MARK:Save the file to local storage
func convertSaveDataToFile(data: Data, fileName: String, fileType: String, sender: String) -> URL? {
    do {
        let tempFileURL = getDocumentsDirectory().appendingPathComponent("\(fileName)")
        
        try data.write(to: tempFileURL)
        
        return tempFileURL
    } catch {
        print("Failed to convert data to file: \(error)")
    }
    
    return nil
}

// MARK: Split the file ino chunks of 32KB since Message Channel packet size is 32KB
func splitDataIntoChunks(data: Data, chunkSize: Int = 32000) -> [Data] {
    var offset = 0
    var chunks = [Data]()
    
    while offset < data.count {
        let length = min(chunkSize, data.count - offset)
        let chunk = data.subdata(in: offset..<(offset+length))
        chunks.append(chunk)
        offset += length
    }
    return chunks
}

// MARK: After received all the data chunks, combine them into one file 
func combineDataChunks(chunks: [Data]) -> Data {
    var combinedData = Data()
    for chunk in chunks {
        combinedData.append(chunk)
    }
    return combinedData
}


func deleteAllFiles() {
    let fileManager = FileManager.default
    let documentsURL = getDocumentsDirectory()
    
    do {
        let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles])
        
        for file in fileURLs {
            try fileManager.removeItem(at: file)
            print("File \(file.lastPathComponent) was deleted.")
        }
        
    } catch {
        print("An error occurred while deleting files: \(error)")
    }
}

Logout RTM

// Logout RTM server
func logoutRTM(){
    agoraRtmKit?.logout()
    agoraRtmKit?.destroy()

    // call delete all function to delete all audio files stored in local directory
    deleteAllAudioFiles()
}

Setup RTM Callbacks

// Receive 'message' event notifications in subscribed message channels and subscribed topics.
func rtmKit(_ rtmKit: AgoraRtmClientKit, didReceiveMessageEvent event: AgoraRtmMessageEvent) {
    switch event.channelType {
    case .message:
        print("Received msg = \(event.message.stringData ?? "Empty") from \(event.publisher)")
        if event.customType == fileInfoKey { 
            // Remote user event.publisher file details (e.g. size, name, type, etc)
        }
        else if event.customType == fileChunkKey { 
            // Remote user receiving the chunks of file
        }
        break
    case .stream:
        break
    case .user:
        break
    case .none:
        break
    @unknown default:
    }
}

// Receive 'presence' event notifications in subscribed message channels and joined stream channels.
func rtmKit(_ rtmKit: AgoraRtmClientKit, didReceivePresenceEvent event: AgoraRtmPresenceEvent) {

    if event.type == .remoteLeaveChannel || event.type == .remoteConnectionTimeout {
    // A remote user left the channel
        
    }else if event.type == .remoteJoinChannel && event.publisher != nil {
     // A remote user subscribe the channel
        
    }else if event.type == .snapshot {
    // Get a snapshot of all the subscribed users' including 'presence' data (aka temporary key-value pairs storage)
        
    }else if event.type == .remoteStateChanged {
    // A remote user's 'presence' data was changed
    }
}

References

(back to top)

Note

  • (Alternative) You could upload the files to a cloud storage (e.g. AWS S3), then send the download link via RTM

License

Distributed under the MIT License. See LICENSE.txt for more information.

(back to top)

Contact

Bac Huang - [email protected]

Project Link: https://github.com/Bac1314/APIExample_AgoraRTM2x

(back to top)