GiftStore is a dedicated module within AtomicXCore for managing live room gifting features. It enables you to build a complete gifting system for your live streaming application, supporting robust monetization and interactive experiences.Gift Panel | Barrage Gift | Full-Screen Gift |
![]() | ![]() | ![]() |
Core Concept | Type | Core Responsibilities & Description |
Gift | struct | Represents a gift data model, including ID, name, icon URL, animation resource URL (resourceURL), price (coins), and more. |
GiftCategory | struct | Represents a grouping of gifts (e.g., "Popular", "Luxury"). Contains a category name and an array of Gift objects. |
GiftState | struct | Represents the current state of the gift module. The core property, usableGifts, is a StateFlow containing the full gift list from the server. |
GiftEvent | enum | Defines the event listener for gift events in the live room. Currently, only onReceiveGift is available. This event is broadcast to all users in the room (including the sender) whenever a gift is successfully sent. |
GiftStore | class | The core management class for gift features. Used to fetch gift lists, execute send requests, and listen for global gift events. |
GiftStore instance and set up subscribers to receive gift events and gift list updates.GiftStore.create(liveID:) to obtain a GiftStore instance bound to the current live room.giftStore.giftEventPublisher to receive .onReceiveGift events.giftStore.state to obtain the usableGifts gift list.import Foundationimport AtomicXCoreimport Combineclass GiftManager {private let liveId: Stringprivate let giftStore: GiftStoreprivate var cancellables = Set<AnyCancellable>()// Expose gift events for the UI layer to subscribe and trigger animationslet giftEventPublisher = PassthroughSubject<GiftEvent, Never>()// Expose gift list externallylet giftListPublisher = CurrentValueSubject<[GiftCategory], Never>([])init(liveId: String) {self.liveId = liveId// 1. Obtain GiftStore instance using liveIdself.giftStore = GiftStore.create(liveID: liveId)subscribeToGiftState()subscribeToGiftEvents()}/// 2. Subscribe to gift eventsprivate func subscribeToGiftEvents() {giftStore.giftEventPublisher.receive(on: DispatchQueue.main).sink { [weak self] event in// Forward the event to the UI layer for handlingself?.giftEventPublisher.send(event)}.store(in: &cancellables)}/// 3. Subscribe to gift list stateprivate func subscribeToGiftState() {giftStore.state.subscribe(StatePublisherSelector(keyPath: \\GiftState.usableGifts)).receive(on: DispatchQueue.main).assign(to: \\.value, on: giftListPublisher).store(in: &cancellables)}}
Parameter | Type | Description |
categoryID | String | Unique ID of the gift category. |
name | String | Display name of the gift category. |
desc | String | Description of the gift category. |
extensionInfo | [String: String] | Extension information field. |
giftList | Array of Gift objects contained in this category. |
Parameter | Type | Description |
giftID | String | Unique ID of the gift. |
name | String | Display name of the gift. |
desc | String | Description of the gift. |
iconURL | String | Gift icon URL. |
resourceURL | String | Gift animation resource URL. |
level | UInt | Gift level. |
coins | UInt | Gift price. |
extensionInfo | [String: String] | Extension information field. |
refreshUsableGifts method to fetch gift data from the server.giftStore.refreshUsableGifts (typically after entering the room).extension GiftManager {/// Refresh the gift list from the serverfunc fetchGiftList() {giftStore.refreshUsableGifts { result inswitch result {case .success:print("Gift list fetched successfully")// On success, data is automatically updated via the state subscriptioncase .failure(let error):print("Failed to fetch gift list: \\(error.localizedDescription)")}}}}
giftID and quantity (count) from your UI.giftStore.sendGift(giftID:count:completion:)..onReceiveGift event listener to ensure consistency.extension GiftManager {/// User sends a giftfunc sendGift(giftID: String, count: UInt) {giftStore.sendGift(giftID: giftID, count: count) { result inswitch result {case .success:print("Gift \\(giftID) sent successfully")// After a successful send, everyone in the room, including the sender, will receive the onReceiveGift eventcase .failure(let error):print("Failed to send gift: \\(error.localizedDescription)")// Prompt the user, e.g., "Insufficient balance" or "Network error"}}}}
Parameter Name | Type | Description |
giftID | String | The unique ID of the gift to send. |
count | UInt | The number of gifts to send. |
completion | CompletionClosure? | Callback after sending is complete. |
GiftStore module is designed to work seamlessly with your backend infrastructure. This section outlines how to configure assets and integrate advanced playback features.refreshUsableGifts to retrieve configuration data.List<GiftCategory> to render the gift panel.
Interface Category | Interface | Request Example |
Gift Management | Add Gift Information | |
| Delete Gift Information | |
| Query Gift Information | |
Gift Category Management | Add Gift Category Information | |
| Delete Specific Gift Category Information | |
| Get Specific Gift Category Information | |
Gift Relationship Management | Add Relationship between a Specific Gift Category and Gift | |
| Delete Relationship between a Specific Gift Category and Gift | |
| Get Gift Relationships under a Specific Gift Category | |
Gift Multi-language Management | Add Gift Multi-language Information | |
| Delete Specific Gift Multi-language Information | |
| Get Gift Multi-language Information | |
| Add Gift Category Multi-language Information | |
| Delete Specific Gift Category Multi-language Information | |
| Get Gift Category Multi-language Information | |
sendGift.onReceiveGift event; if fails, the completion callback of sendGift receives an error.
Interface | Description | Request Example |
Callback Configuration - Callback before sending a gift | Pre-gifting validation hook. Allows your backend to intercept gift requests to perform necessary checks (e.g., sufficient balance, risk control) before the gift is processed. |
Comparison Item | Basic Solution (SVGAPlayer-Android) | Advanced Solution (TCEffectPlayerKit) |
Billing | Free (open source) | |
Integration Method | Manual integration via Gradle | Requires additional integration and authentication |
Formats | SVGA only | SVGA, PAG, WebP, Lottie, MP4, and more |
Performance | Good for files < 10MB | Optimized for large assets & complex layering |
Recommended Scenario | Uniform SVGA format, controllable file size | Multiple formats or higher performance/device compatibility needs |
Podfile, add the dependency for SVGAPlayer and run pod install in the terminal.target 'YourApp' do# ... other podspod 'SVGAPlayer'end
giftEventPublisher..onReceiveGift event, check if gift.resourceURL is valid and points to an SVGA file. If so, use SVGAParser to parse the URL and pass the parsed SVGAVideoEntity to the SVGAPlayer instance for playback.import UIKitimport AtomicXCoreimport Combineimport SVGAPlayer // 1. Importclass LiveRoomViewController: UIViewController, SVGAPlayerDelegate {private var giftManager: GiftManager!private var cancellables = Set<AnyCancellable>()private let svgaPlayer = SVGAPlayer() // 2. Prepare instanceprivate let svgaParser = SVGAParser()override func viewDidLoad() {super.viewDidLoad()setupSVGAPlayer()}private func setupSVGAPlayer() {svgaPlayer.delegate = selfsvgaPlayer.loops = 1 // Play once by defaultsvgaPlayer.clearsAfterStop = true // Automatically clear after playbackview.addSubview(svgaPlayer)svgaPlayer.frame = view.bounds // Full screen by defaultsvgaPlayer.isHidden = true}func setupGiftSubscription(liveId: String) {self.giftManager = GiftManager(liveId: liveId)giftManager.giftEventPublisher.sink { [weak self] event in // 3. Listen for eventsguard case .onReceiveGift(_, let gift, _, _) = event else { return }if !gift.resourceURL.isEmpty, let url = URL(string: gift.resourceURL) { // 4. Parse and playself?.playAnimation(from: url)}}.store(in: &cancellables)}private func playAnimation(from url: URL) {svgaParser.parse(with: url, completionBlock: { [weak self] videoItem inguard let self = self, let videoItem = videoItem else { return }DispatchQueue.main.async {self.svgaPlayer.videoItem = videoItemself.svgaPlayer.isHidden = falseself.svgaPlayer.startAnimation()}}, failureBlock: { error inprint("Failed to parse SVGA animation: \\(error?.localizedDescription ?? "unknown error")")// Add retry or error reporting logic as needed})}func svgaPlayerDidFinishedAnimation(_ player: SVGAPlayer!) { /* ... Handle end of playback ... */ }}
giftStore.giftEventPublisher. .onReceiveGift event, extract the sender, gift, and count.BarrageStore.create(liveID:) to get the instance bound to the current room.Barrage struct, set messageType = .text, and set textContent to the composed string (e.g., "[sender.userName] sent [gift.name] x [count]").barrageStore.appendLocalTip(message: giftTip) to insert the message into the local list.// In LiveRoomManager or a similar top-level managerprivate func setupGiftToBarrageFlow() {giftStore.giftEventPublisher.receive(on: DispatchQueue.main).sink { [weak self] event in // 1. Listen for eventsguard case .onReceiveGift(_, let gift, let count, let sender) = event else { return } // 2. Extract informationguard let self = self else { return }// 3. Get BarrageStore (assume barrageStore is already initialized)// 4. Compose messagelet tipText = "\\(sender.userName) sent \\(gift.name) x \\(count)"var giftTip = Barrage()giftTip.messageType = .textgiftTip.textContent = tipText// Optional: set a special sender// 5. Insert locallyself.barrageStore.appendLocalTip(message: giftTip)}.store(in: &cancellables)}
Store/Component | Feature Description | API Documentation |
GiftStore | Fetch gift list, send/receive gifts, listen for gift events (including sender and gift details). | |
BarrageStore | Send text/custom barrage, maintain barrage list, listen to barrage status in real time. |
refreshUsableGifts(completion) to fetch gift data from your backend. Ensure your backend is configured with gift data via the server REST API.GiftStore.setLanguage(language: String) API before calling refreshUsableGifts, passing the target language code (e.g., "en" or "zh-CN"). The server will return gift names and descriptions in the specified language.onReceiveGift event is broadcast to all room members, including the sender. If you update the UI both in the sendGift completion callback and in the onReceiveGift event, the animation will play twice.onReceiveGift event handler. Use the sendGift completion callback only for handling failures (such as "Send failed" or "Insufficient balance").sendGift, your backend performs the deduction. After returning the result to the AtomicXCore backend, it determines whether to broadcast the gift event.onReceiveGift events) are not affected by mute or frequency controls and are always delivered reliably.Feedback