Hướng dẫn tích hợp SDK iOS với bản Objective-C
Danh sách file cần thiết và các bước hướng dẫn tích hợp iTapSDK iOS với bản Objective-C vào dự án.
1. Danh sách file cần thiết
Phần tiêu đề “1. Danh sách file cần thiết”Trước khi tích hợp, bạn cần chuẩn bị đầy đủ các file sau (do đội ngũ VTVLive cung cấp):
AFNetworking.xcframeworkFBAEMKit.xcframeworkFBSDKCoreKit_Basics.xcframeworkFBSDKCoreKit.xcframeworkFBSDKLoginKit.xcframeworkFBSDKShareKit.xcframeworkiTapSdk.xcframeworkISdkTrackier.xcframeworkRecapchaSdk.xcframeworkRXSwift.xcframework- Google-Facebook
.plist AppConfigDefault.plist- Google-Facebook
.plist.txt Podfile- Project demo mẫu (example)
- File
.mobileprovision
Sau khi test xong ở môi trường dev, chúng tôi sẽ cung cấp key riêng cho môi trường production.
2. Các bước tích hợp SDK
Phần tiêu đề “2. Các bước tích hợp SDK”Để tích hợp SDK, bạn hãy làm theo đúng thứ tự các bước dưới đây.
2.1. Thêm framework và library vào project
Phần tiêu đề “2.1. Thêm framework và library vào project”- Trong project chính (target app), chọn chế độ Embed & Sign cho các library đã liệt kê ở trên.

- Trong UnityFramework (nếu game build từ Unity), chọn chế độ Do Not Embed cho các library — vì các library này đã được embed ở target chính, tránh bị nhân đôi.

2.2. Thêm Notification Service Extension
Phần tiêu đề “2.2. Thêm Notification Service Extension”Extension này giúp SDK xử lý và hiển thị đúng nội dung push notification (kể cả khi có ảnh, dữ liệu tuỳ biến từ Firebase).
- Mở project iOS trong Xcode.
- Vào File > New > Target…

- Trong danh sách template, tìm và chọn Notification Service Extension.
- Bấm Next, đặt tên target — ví dụ
FirebaseNotificationServiceExtension— rồi bấm Finish. - Khi Xcode hỏi có muốn kích hoạt scheme mới không, chọn Activate.

2.3. Cài đặt Firebase SDK cho Extension
Phần tiêu đề “2.3. Cài đặt Firebase SDK cho Extension”- Mở file
Podfilevà thêm một target mới dành riêng cho extension vừa tạo:
source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'
platform :ios,'15.0'target 'Unity-iPhone' do use_frameworks! pod 'GoogleSignIn', '7.1.0' pod 'Firebase', '12.0.0' pod 'FirebaseMessaging', '12.0.0' pod 'FirebaseRemoteConfig', '12.0.0' pod 'AppsFlyerFramework', '6.17.7' pod 'JWT', '2.2.0' pod 'TikTokBusinessSDK', '1.5.0' pod 'FirebaseRemoteConfig', '12.0.0' pod 'FirebaseCrashlytics', '12.0.0'endtarget 'UnityFramework' do use_frameworks! pod 'GoogleSignIn', '7.1.0' pod 'Firebase', '12.0.0' pod 'FirebaseMessaging', '12.0.0' pod 'FirebaseRemoteConfig', '12.0.0' pod 'AppsFlyerFramework', '6.17.2' pod 'JWT', '2.2.0' pod 'TikTokBusinessSDK', '1.5.0' pod 'FirebaseRemoteConfig', '12.0.0' pod 'FirebaseCrashlytics', '12.0.0'end
target 'FirebaseNotificationServiceExtension' do use_frameworks! pod 'Firebase', '12.0.0' pod 'FirebaseMessaging', '12.0.0'end
post_install do |lib| lib.pods_project.targets.each do |target| if target.name == 'GoogleSignIn' || target.name == 'Firebase' || target.name == 'FirebaseRemoteConfig' || target.name == 'FirebaseMessaging' || target.name == 'AppsFlyerFramework' || target.name == 'JWT' || target.name == 'TikTokBusinessSDK' target.build_configurations.each do |config| config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES' end end
target.build_configurations.each do |config| config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0' end endend- Chạy các lệnh sau để cài đặt lại dependencies cho sạch:
pod cache clean --allpod deintegratepod install2.4. Cấu hình FirebaseNotificationServiceExtension
Phần tiêu đề “2.4. Cấu hình FirebaseNotificationServiceExtension”- Mở file
.xcworkspace(không mở.xcodeproj). - Sửa nội dung file
NotificationService.mcủa extension như sau:
#import "NotificationService.h"@import Firebase;@import FirebaseMessaging;@interface NotificationService ()@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;@end@implementation NotificationService- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; self.bestAttemptContent.title = [NSString stringWithFormat:@"%@", self.bestAttemptContent.title]; [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler];}- (void)serviceExtensionTimeWillExpire { [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:self.contentHandler];}@endSau bước này, cấu trúc project trên Xcode sẽ hiển thị như hình dưới:

Ngoài ra:
- Để bật đăng nhập bằng Apple, bạn cần bật capability Sign in with Apple cho target chính.
- Để nhận được push notification, bạn cần thêm capability Push Notifications, đồng thời bật Background Fetch và Remote Notifications trong mục Background Modes.
2.5. Thêm cấu hình Facebook vào Info.plist
Phần tiêu đề “2.5. Thêm cấu hình Facebook vào Info.plist”Sau khi đã có Facebook App ID và Facebook Client Token (liên hệ VTVLive để lấy thông tin cho ứng dụng của bạn), thêm đoạn cấu hình sau vào file Info.plist của project. Các giá trị bên dưới chỉ là ví dụ minh hoạ — hãy thay bằng thông tin thực tế của bạn:
<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLSchemes</key> <array> <string>fb24125138790480742</string> </array> </dict> <dict> <key>CFBundleURLSchemes</key> <array> <string>com.googleusercontent.apps.630962979382-dko6grp0h2m3qnka8tndbj20rtcm495f</string> </array> </dict> </array> <key>CFBundleVersion</key> <string>1.3.4</string> <key>FacebookAppID</key> <string>24125138790480742</string> <key>FacebookClientToken</key> <string>5a78f323394b24d1f97c425876c24aca</string> <key>FacebookDisplayName</key> <string>Sdk Test Facebook</string> <key>LSApplicationQueriesSchemes</key> <array> <string>fbapi</string> <string>fbapi20130214</string> <string>fbapi20130410</string> <string>fbapi20130702</string> <string>fbapi20131010</string> <string>fbapi20131219</string> <string>fbapi20140410</string> <string>fbapi20140116</string> <string>fbapi20150313</string> <string>fbapi20150629</string> <string>fbapi20160328</string> <string>fbauth</string> <string>fb-messenger-share-api</string> <string>fbauth2</string> <string>fbshareextension</string> </array> <key>NSAdvertisingAttributionReportEndpoint</key> <string>https://appsflyer-skadnetwork.com/</string> <key>NSUserTrackingUsageDescription</key> <string>This identifier will be used to deliver personalized ads to you.</string>2.6. Cấu hình Firebase Crashlytics
Phần tiêu đề “2.6. Cấu hình Firebase Crashlytics”Thêm một Run Script Phase vào Build Phases của target chính với nội dung sau, để Crashlytics tự động upload debug symbol (dSYM) mỗi khi build:

echo "Symbols Directory"echo "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}"#Firebase"${PODS_ROOT}/FirebaseCrashlytics/upload-symbols" -gsp "${PROJECT_DIR}/SdkExample/GoogleService-Info.plist" -p ios "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}"3. Tích hợp các hàm SDK
Phần tiêu đề “3. Tích hợp các hàm SDK”Phần này hướng dẫn cách gọi các hàm chính của SDK trong code, theo đúng thứ tự vòng đời sử dụng: khởi tạo → đăng nhập → các thao tác trong game → đăng xuất.
3.1. Khởi tạo SDK
Phần tiêu đề “3.1. Khởi tạo SDK”Trong file AppDelegate.h, import SDK và khai báo AppDelegate tuân thủ protocol SdkDelegate:
#import <UIKit/UIKit.h>#import <iTapSdk/iTapSdk.h>@interface AppDelegate : UIResponder <UIApplicationDelegate,SdkDelegate>
@property (strong, nonatomic) UIWindow *window;@endTrong file AppDelegate.m, khởi tạo SDK ngay khi app khởi động:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[Sdk sharedInstance] initWithDelegate:self application:application launchOptions:launchOptions isProdEnv:TRUE]; [[Sdk sharedInstance] setLocationFloatingButton:20 with:100]; [[Sdk sharedInstance] setGrantEnv:YES]; return YES;}- Trong lúc tích hợp và test, hãy đổi
isProdEnvsangFALSEđể chạy ở môi trường dev:
[[Sdk sharedInstance] initWithDelegate:self application:application launchOptions:launchOptions isProdEnv:FALSE];setGrantEnv:YES— hiển thị popup xin quyền (quảng cáo, tracking…) khi vào game. ĐặtsetGrantEnv:NOnếu không muốn hiển thị popup này.
3.2. Đăng nhập (Login)
Phần tiêu đề “3.2. Đăng nhập (Login)”Luôn gọi hàm login trước khi game bắt đầu. SDK sẽ tự hiển thị dialog đăng nhập nếu đây là lần đầu người dùng vào game, hoặc tự động refresh token nếu người dùng đã từng đăng nhập trước đó. Xem thêm sơ đồ luồng đăng nhập ở mục Flowchart bên dưới.
[[Sdk sharedInstance] login];
Nếu mọi thứ thành công, dialog đăng nhập sẽ hiển thị như hình trên.

Khi người dùng đăng nhập thành công, thông tin user sẽ được trả về qua hàm loginSuccess của SdkDelegate mà bạn đã đăng ký với SDK.
3.3. Đăng xuất (Logout)
Phần tiêu đề “3.3. Đăng xuất (Logout)”[[Sdk sharedInstance] logout];Khi đăng xuất thành công, SDK sẽ gọi lại hàm logout của SdkDelegate để thông báo cho game.
3.4. Lấy danh sách sản phẩm (List products)
Phần tiêu đề “3.4. Lấy danh sách sản phẩm (List products)”Lấy danh sách sản phẩm (gói nạp) từ store (App Store). Bạn nên gọi hàm này ngay sau khi đăng nhập thành công, vì cần dùng giá sản phẩm để gửi lên hệ thống tracking khi người dùng thanh toán.
-(void)loginSuccess:(User *)user withAccessToken:(NSString *)accessToken andRefreshToken:(NSString *)refreshToken{
[[Sdk sharedInstance] listProducts:^(NSError *error, NSArray<IAPProduct*> * _Nullable result) {
}]; [[Sdk sharedInstance] showOrHideFloatingButton];
}3.5. Đổi ngôn ngữ (Set Language)
Phần tiêu đề “3.5. Đổi ngôn ngữ (Set Language)”Gọi hàm sau để đổi ngôn ngữ hiển thị của SDK, với tham số tương ứng:
[[Sdk sharedInstance] setLanguage:@"vi"];Các mã ngôn ngữ được hỗ trợ:
| Mã | Ngôn ngữ |
|---|---|
vi | Tiếng Việt |
en | Tiếng Anh |
th | Tiếng Thái |
zh | Tiếng Trung |
SDK cũng cung cấp delegate getLangaugeSdk để game nhận thông báo mỗi khi ngôn ngữ được thay đổi.

3.6. Hiển thị đếm ngược (Show countdown)
Phần tiêu đề “3.6. Hiển thị đếm ngược (Show countdown)”Để hiển thị giao diện đếm ngược như hình dưới, gọi showCountdown sau khi đăng nhập hoàn tất, bên trong callback loginSuccess của AppDelegate:

-(void)loginSuccess:(User *)user withAccessToken:(NSString *)accessToken andRefreshToken:(NSString *)refreshToken{
[[Sdk sharedInstance] listProducts:^(NSError *error, NSArray<IAPProduct*> * _Nullable result) {
}]; [[Sdk sharedInstance] showOrHideFloatingButton]; [[Sdk sharedInstance] showCountdown];
}3.7. Nhận quà (Receive Gift)
Phần tiêu đề “3.7. Nhận quà (Receive Gift)”Khi người dùng bấm nút hộp quà (gift box), gọi hàm sau:
[[Sdk sharedInstance] showReceiveGift:@"1111111" withCharacterId:@"11112"];serverId: Id của server.characterId: Id của nhân vật (character).
3.8. Thanh toán (Make payment)
Phần tiêu đề “3.8. Thanh toán (Make payment)”[[Sdk sharedInstance] payProduct:itemProduct.productId forCharacterId:@"17782088632480" onServerId:@"s6003"];itemProduct.productId: lấy từ kết quả củalistProductsở mục 3.4.forCharacterId: Id nhân vật trong game.onServerId: Id server.
Kết quả thanh toán (thành công hoặc thất bại) sẽ được trả về tương ứng qua hàm didPurchaseSuccess hoặc didPurchaseFailed của SdkDelegate đã đăng ký với SDK.
3.9. Hiện/ẩn floating button (Show dashboard)
Phần tiêu đề “3.9. Hiện/ẩn floating button (Show dashboard)”[[Sdk sharedInstance] showOrHideFloatingButton];3.10. Ánh xạ thông tin nhân vật (Map User)
Phần tiêu đề “3.10. Ánh xạ thông tin nhân vật (Map User)”Gọi hàm này để gửi thông tin nhân vật hiện tại của người dùng lên SDK, phục vụ cho tracking và các tính năng liên quan đến nhân vật/server:

-(void) mappingUser:(NSString*)characterName characterId:(NSString*)characterId characterLevel:(NSString*)characterLevel serverName:(NSString*)serverName serverId:(NSString*)serverId;
// Ví dụ:[[SdkTracking sharedInstance] mappingUser: @"name" characterId : @1 characterLevel : @1 serverName : @"test" serverId : @"1"];characterName: Tên nhân vật.characterId: Id của nhân vật.characterLevel: Level hiện tại của nhân vật.serverName: Tên server.serverId: Id server.
4. Nhận quà (không cần tham số)
Phần tiêu đề “4. Nhận quà (không cần tham số)”SDK còn cung cấp một overload khác của hàm nhận quà, không cần truyền characterId/serverId — dùng khi bạn đã gọi mappingUser ở mục 3.10 trước đó, vì lúc này SDK đã tự biết thông tin nhân vật/server hiện tại.
Khi người dùng bấm nút hộp quà, gọi:
[[Sdk sharedInstance] showReceiveGift];
5. Hàm ghi log tracking (Log Function)
Phần tiêu đề “5. Hàm ghi log tracking (Log Function)”Các hàm dưới đây dùng để gửi sự kiện tracking về hệ thống, giúp đo lường hành vi người dùng trong toàn bộ vòng đời chơi game — từ mở app, tải patch, tạo nhân vật đến vào game.

Chú thích màu trong sơ đồ trên:
- Màu xám: các bước xử lý phía Game.
- Màu vàng: các điểm Game cần gọi hàm của SDK.
Vui lòng xem thêm mục “Login or Start Game” trong Flowchart để biết vị trí chính xác cần đặt các hàm log bên dưới trong luồng chạy của game.
-(void) logStartGameScreen;-(void) logEnterGamePlay;-(void) logStartTutorial;-(void) logCompleteTutorial;logEnterGamePlay
Phần tiêu đề “logEnterGamePlay”Tracking sự kiện người dùng vào màn hình gameplay, ngay sau khi bấm nút “Enter Game”.

logPatchStart — Bắt đầu tải tài nguyên (patch)
Phần tiêu đề “logPatchStart — Bắt đầu tải tài nguyên (patch)”-(void) logPatchStart: (NSNumber * _Nullable) sizeMB withVerrsion:(NSString * _Nullable)version withNetworkType: (NSString * _Nullable) networkType;
// sizeMB : dung lượng file patch cần tải, tính theo MB// version : phiên bản gói cài đặt
// Ví dụ:[[SdkTracking sharedInstance] logPatchStart: @"500" withVerrsion : @"1.0.1" withNetworkType : @"wifi"];logPatchSuccess — Tải tài nguyên hoàn tất
Phần tiêu đề “logPatchSuccess — Tải tài nguyên hoàn tất”-(void) logPatchSuccess: (NSNumber * _Nullable) durationSec withAvgSpeedKbps: (NSNumber * _Nullable) avgSpeedKbps withCdnNode :(NSString * _Nullable) cdnNode;
// durationSec : thời gian từ lúc bắt đầu tải đến khi tải xong (giây)// avgSpeedKbps : tốc độ tải trung bình (Kbps)// cdnNode : node CDN đã sử dụng để tải
// Ví dụ:[[SdkTracking sharedInstance] logPatchSuccess: @"500" withAvgSpeedKbps : @"111" avgSpeedKbps : @"450" withCdnNode: @"192.168.0.2"];logPatchError — Tải tài nguyên thất bại
Phần tiêu đề “logPatchError — Tải tài nguyên thất bại”Gọi khi việc tải tài nguyên bị gián đoạn hoặc thất bại.
-(void) logPatchError:(NSString * _Nullable) errorCode withRetryCount:(NSNumber * _Nullable) retryCount withFailedUrl:(NSString * _Nullable) failedUrl withCdnNode :(NSString * _Nullable) cdnNode;
// errorCode : mã lỗi// retryCount : số lần đã retry do lỗi// failedUrl : url bị lỗi khi tải// cdnNode : node CDN xảy ra lỗi
// Ví dụ:[[SdkTracking sharedInstance] logPatchError: @"500" withRetryCount: @1 withFailedUrl: @"https://cdn.game.com/patch/v120.zip" withCdnNode : @"192.168.0.2"];logExtractStarted — Bắt đầu giải nén tài nguyên
Phần tiêu đề “logExtractStarted — Bắt đầu giải nén tài nguyên”Gọi ngay sau khi tải xong, khi bắt đầu giải nén/cài đặt.
-(void) logExtractStarted:(NSString * _Nullable) totalSizeMb withTotalSize: (NSString * _Nullable) totalSize;
// totalSizeMb : dung lượng sau khi giải nén, tính theo MB// totalSize : số lượng file trong patch cần giải nén (bỏ qua nếu không lấy được)
// Ví dụ:[[SdkTracking sharedInstance] logExtractStarted: @"500" withTotalSize : @"400"];logExtractFinished — Giải nén hoàn tất
Phần tiêu đề “logExtractFinished — Giải nén hoàn tất”Gọi khi giải nén xong, đã sẵn sàng vào màn hình đăng nhập.
-(void) logExtractFinished:(NSNumber * _Nullable) durationSec withResultCode:(NSString * _Nullable) resultCode;
// durationSec : thời gian từ lúc bắt đầu giải nén đến khi hoàn tất (giây)// resultCode : mã kết quả — 0: OK, 1: Disk Full (hết dung lượng)
// Ví dụ:[[SdkTracking sharedInstance] logExtractFinished: @100 withResultCode : @"ok"];logCharCreateStart — Bắt đầu tạo nhân vật
Phần tiêu đề “logCharCreateStart — Bắt đầu tạo nhân vật”-(void) logCharCreateStart: (NSString * _Nullable) serverId;
// serverId : id server
// Ví dụ:[[SdkTracking sharedInstance] logCharCreateStart: @"100"];logCharCreateSuccess — Tạo nhân vật thành công
Phần tiêu đề “logCharCreateSuccess — Tạo nhân vật thành công”-(void) logCharCreateSuccess: (NSString * _Nullable) serverId withCharId:(NSString * _Nullable) charId withCharName:(NSString * _Nullable) charName withCharClass:(NSString * _Nullable) charClass withDurationSec: (NSNumber * _Nullable) durationSec;
// serverId : id server// charId : id nhân vật// charName : tên nhân vật// charClass : id lớp nhân vật (class_id)// durationSec : thời gian từ lúc bắt đầu tạo đến khi tạo thành công (giây)
// Ví dụ:[[SdkTracking sharedInstance] logCharCreateSuccess: @"500" withVerrsion : @"1.0.1" withNetworkType : @"wifi"];logCharCreateError — Tạo nhân vật thất bại
Phần tiêu đề “logCharCreateError — Tạo nhân vật thất bại”-(void) logCharCreateError:(NSString * _Nullable) errorCode withCharName:(NSString * _Nullable) charName withServerId:(NSString * _Nullable) serverId;
// errorCode : mã lỗi// charName : tên nhân vật// serverId : id server
// Ví dụ:[[SdkTracking sharedInstance] logCharCreateError: @"NAME_DUPLICATED" withCharName : @"test" withServerId :@"1"];logCharSelect — Chọn nhân vật
Phần tiêu đề “logCharSelect — Chọn nhân vật”Gọi hàm này ngay sau khi người dùng chọn server và nhân vật để vào game.
-(void) logCharSelect: (NSString * _Nullable) charId withCharacterName :(NSString* _Nonnull) characterName withCharLevel:(NSString * _Nullable) charLevel;
// charId : id nhân vật// characterName : tên nhân vật// charLevel : level nhân vật
// Ví dụ:[[SdkTracking sharedInstance] logCharSelect: @"1" withCharacterName : @"name " withCharLevel : @"1"];6. Cập nhật đồng bộ thông tin theo Nghị định 147
Phần tiêu đề “6. Cập nhật đồng bộ thông tin theo Nghị định 147”dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ BOOL needShow = [[Sdk sharedInstance] isNeedShowEventProfileSync]; if (needShow) { dispatch_async(dispatch_get_main_queue(), ^{ [[Sdk sharedInstance] showEventProfile]; // showEventProfile cần chạy trên main thread vì có UI }); }});- Nếu
isNeedShowEventProfile()trả vềtrue: mở màn hình đồng bộ thông tin (Event Profile) để người dùng cập nhật. - Nếu
isNeedShowEventProfile()trả vềfalse: cho phép người dùng vào game trực tiếp, không cần hiển thị màn hình này.
7. Gợi ý đánh giá ứng dụng (Review App)
Phần tiêu đề “7. Gợi ý đánh giá ứng dụng (Review App)”Dùng tính năng này để gợi ý người dùng đánh giá (review) ứng dụng ngay trong app, thông qua hộp thoại đánh giá chuẩn của Apple.
[[Sdk sharedInstance] requestReview];
8. Các class trong SDK
Phần tiêu đề “8. Các class trong SDK”IAPProduct — thông tin một sản phẩm (gói nạp):
| Thuộc tính | Mô tả |
|---|---|
productId | Id sản phẩm, trùng với product id khai báo trên App Store |
localizedPrice | Giá sản phẩm hiển thị theo locale của thiết bị |
appAmount | Số lượng gold/vàng người dùng nhận được khi mua gói này |
User — thông tin người dùng:
| Thuộc tính | Mô tả |
|---|---|
accountID | Id tài khoản người dùng |
accountName | Username của tài khoản |
Sdk — đối tượng SDK chính:
| Thành phần | Mô tả |
|---|---|
sharedInstance | Lấy singleton instance của SDK |
showOrHideFloatingButton() | Hiện hoặc ẩn floating button |
accessToken | Lấy access token sau khi đăng nhập thành công |
9. Sơ đồ luồng (Flowchart)
Phần tiêu đề “9. Sơ đồ luồng (Flowchart)”Sơ đồ tổng quan:

Sơ đồ trên mô tả luồng “Login or Start Game” — thứ tự các bước và vị trí cần gọi các hàm SDK/tracking đã giới thiệu ở các mục trên.
Sơ đồ luồng thanh toán (Payment):

10. Import certificate vào Keychain và build IPA
Phần tiêu đề “10. Import certificate vào Keychain và build IPA”Chúng tôi sẽ cung cấp cho bạn các certificate cần thiết để build file .ipa, bao gồm: apn_certificate, dev_certificate, dis_certificate, development provisioning profile và production provisioning profile.

Bước 1 — Import certificate vào Keychain: double-click (hoặc kéo thả) file .p12 để thêm vào Keychain Access.

Sau khi import thành công, bạn sẽ thấy certificate xuất hiện trong Keychain như hình dưới:

Bước 2 — Import provisioning profile vào Xcode: double-click file provisioning profile (hoặc kéo thả vào Xcode) để Xcode tự nhận diện.


Cách export file IPA để upload lên App Store
Phần tiêu đề “Cách export file IPA để upload lên App Store”Làm theo các bước sau trong Xcode:
- Chọn đích build là Any iOS Device (arm64) thay vì simulator.

- Vào menu Product > Archive để Xcode build và đóng gói archive.

- Sau khi archive xong, cửa sổ Organizer sẽ tự mở và hiển thị bản archive vừa tạo. Chọn Distribute App.

- Chọn phương thức phân phối phù hợp (ví dụ App Store Connect hoặc Ad Hoc) và làm theo các bước wizard của Xcode.

- Trước khi export, nhớ kiểm tra kỹ mục aps-environment trong entitlements phải là
production(không phảidevelopment), nếu không push notification sẽ không hoạt động trên bản release.

- Sau khi export xong, chọn Show in Finder để mở thư mục chứa file
.ipa.

- Chuột phải vào file
.ipa(hoặc file.xcarchive) và chọn Show Package Contents để lấy file dSYM bên trong nếu cần gửi debug symbol.


