EAS Submit iOS: đưa app Expo lên App Store
Bởi Mehdi Azizi
Đăng ngày , 8 phút
EAS Submit gửi build iOS lên App Store Connect, build xuất hiện trong TestFlight. Bạn vẫn phải tự gửi xét duyệt. Kèm file eas.json mẫu và các lỗi thường gặp.
eas submit --platform ios gửi build iOS của bạn lên App Store Connect, và build sẽ xuất hiện trong TestFlight sau khi Apple xử lý xong. Lệnh này không gửi app của bạn đi xét duyệt: việc chọn build và gửi xét duyệt vẫn phải làm trong App Store Connect. Muốn tự động hóa, lệnh cần số định danh trang app của bạn (ascAppId) và một khóa API App Store Connect.
Mình phát hành các app iOS của mình, trong đó có Solys, TradaZen và Nolera, bằng Expo, EAS Build và EAS Submit. Hướng dẫn này bám theo tài liệu của Expo về việc gửi build iOS, mình đọc lại ngày 25/10/2026. Nếu không có Mac, toàn bộ quy trình nằm trong bài phát hành app iOS không cần Mac. Ở đây mình chỉ tập trung vào bước gửi build.
Những gì bạn cần trước lệnh đầu tiên
Expo liệt kê ba điều kiện:
- Một tài khoản Apple Developer trả phí. Giá và cách đăng ký có trong bài tài khoản nhà phát triển Apple.
- Một bundle identifier trong
ios.bundleIdentifier. Theo tài liệu tham chiếu cấu hình Expo, bạn tự đặt, nhưng nó phải là duy nhất trên App Store. - Đã cài EAS CLI, rồi chạy
eas loginbằng tài khoản Expo của bạn.
Bạn cũng cần một build production đã ký. EAS Build tạo build này bằng certificate và profile mà nó quản lý giúp bạn, như giải thích trong bài certificate và provisioning profile iOS.
Trang app trên App Store Connect: bạn tạo hay để EAS tạo?
EAS Submit gửi file binary tới một app phải có sẵn trong App Store Connect.
Bạn có thể tự tạo. Trang hỗ trợ của Apple mô tả cửa sổ New App: nền tảng, tên, ngôn ngữ chính, bundle identifier, SKU và quyền truy cập của người dùng. Bạn cần vai trò Account Holder, Admin hoặc App Manager.
EAS cũng có thể tạo nó ở lần gửi đầu tiên. Theo trang của Expo về TestFlight, eas submit sẽ tạo trang app khi bạn gửi build đầu tiên. Theo tài liệu tham chiếu eas.json, điền ascAppId sẽ bỏ qua bước này. Tên lấy từ appName, nếu không có thì lấy name trong cấu hình của bạn. Ngôn ngữ chính lấy từ language, mặc định là en-US. Nếu app của bạn ra mắt trước bằng tiếng Pháp, hãy điền language trước lần gửi đầu tiên này.
Sau đó, chép số định danh trang app vào ascAppId. Expo chỉ chỗ tìm: mở app trong App Store Connect, rồi vào App Information. Đó là trường Apple ID, nằm dưới General Information. Đây là một con số, không phải bundle identifier của bạn.
Vì sao nên dùng khóa API thay vì Apple ID
EAS Submit kết nối với Apple bằng Apple ID của bạn hoặc bằng một khóa API App Store Connect.
Apple ID vướng xác thực hai yếu tố, thứ mà Apple bắt buộc với mọi người dùng App Store Connect: phải có người xác nhận lần đăng nhập. Mật khẩu dành riêng cho app, truyền qua EXPO_APPLE_APP_SPECIFIC_PASSWORD, giúp tránh bước này, nhưng Expo đánh giá nó kém an toàn hơn khóa API, vốn là cách mặc định và được khuyên dùng. Khóa API cho phép xác thực mà không cần thao tác gì, nhờ đó tự động hóa được việc gửi build.
Có hai cách để có khóa:
- Để EAS quản lý. Chạy
eas credentials --platform ios, chọn profileproduction, đăng nhập tài khoản Apple, rồi chọn “App Store Connect: Manage your API Key”. - Dùng khóa của riêng bạn. Trong App Store Connect: Users and Access, tab Integrations, App Store Connect API, Team Keys, rồi Generate API Key. Bạn cần tài khoản Admin, và hướng dẫn của Expo cấp vai trò Admin cho khóa.
Với cách thứ hai, ghi lại Key ID, hiển thị trong danh sách khóa đang hoạt động, và Issuer ID, ở đầu trang Integrations. Quan trọng nhất là giữ file .p8: bạn chỉ tải được nó một lần, và Apple không giữ bản sao nào.
Theo Apple, một khóa Admin có thể tạo và xóa người dùng trong tài khoản của bạn. Apple yêu cầu không bao giờ để khóa trong kho mã nguồn.
File eas.json tối thiểu để sao chép
Mọi giá trị đều là giả: hãy thay bằng giá trị của bạn.
{
"cli": {
"appVersionSource": "remote"
},
"build": {
"production": {
"autoIncrement": true
}
},
"submit": {
"production": {
"ios": {
"ascAppId": "1234567890",
"appleTeamId": "ABCDE12345",
"ascApiKeyPath": "./keys/AuthKey_XXXXXXXXXX.p8",
"ascApiKeyIssuerId": "your-issuer-id",
"ascApiKeyId": "XXXXXXXXXX"
}
}
}
}
appVersionSourcevàautoIncrement: máy chủ EAS giữ số build và tăng nó sau mỗi build production. Expo khuyên dùng chế độ remote này từ EAS CLI 12.ascAppId: số định danh trang app của bạn. Thiếu nó, chế độ non-interactive sẽ thất bại.appleTeamId: định danh team Apple Developer của bạn.ascApiKeyPath,ascApiKeyIssuerId,ascApiKeyId: khóa API của bạn. Cả ba trường đều nhận biến môi trường. Thêm thư mụckeysvào.gitignore.
Trường appleId chỉ dùng khi bạn đăng nhập bằng Apple ID. Nếu EAS đã quản lý khóa của bạn, hãy bỏ luôn ba trường ascApiKey. Trên ba app của mình, đọc lại ngày 25/10, profile gửi production chỉ có ascAppId, cộng thêm appleTeamId với Solys: không có khóa nào trong file.
Chạy build, rồi gửi
eas build --platform ios --profile production
eas submit --platform ios
Không có tùy chọn nào khác, eas submit sẽ bắt bạn chọn build cần gửi và, ở lần chạy đầu, hỏi Apple ID của bạn. Profile gửi production được dùng mặc định nếu có. Tài liệu tham chiếu EAS CLI liệt kê các tùy chọn giúp bỏ qua bước chọn:
| Tùy chọn | Tác dụng |
|---|---|
--latest | Gửi build iOS mới nhất |
--id | Gửi một build EAS cụ thể |
--path | Gửi một file .ipa trên máy |
--url | Gửi một archive trực tuyến |
--groups | Thêm vào một nhóm nội bộ |
--what-to-test | Điền “What to test” |
Để chạy liền một mạch, thêm --auto-submit vào lệnh build. Theo trang của Expo về gửi tự động, build xong sẽ được chuyển tới EAS Submit với profile gửi cùng tên với profile build. Muốn chọn profile khác: --auto-submit-with-profile=ten-profile. Build đi tới TestFlight, không phải tới khâu xét duyệt.
Trong CI, thêm --non-interactive và một token EXPO_TOKEN. Khi đó lệnh không thể hỏi bạn gì: nó cần ascAppId trong profile và một khóa API, là khóa EAS đã gắn với app hoặc khóa của bạn trong ba trường ascApiKey.
Chuyện gì xảy ra sau khi gửi
1. Apple xử lý build. Build phải qua bước xử lý này mới hiện trong App Store Connect, và Apple gửi email cho bạn khi xong. Expo cho biết thường mất 10 đến 15 phút trước khi thấy build trong TestFlight.
2. Tuân thủ xuất khẩu. Trạng thái Missing Compliance nghĩa là còn thiếu khai báo của bạn về mã hóa. App Store Connect hỏi các câu này với mỗi phiên bản mới, trừ khi Info.plist đã trả lời sẵn. Với Expo, khóa tên là ios.config.usesNonExemptEncryption:
{
"expo": {
"ios": {
"config": {
"usesNonExemptEncryption": false
}
}
}
}
Theo Apple, false phù hợp nếu app của bạn, kể cả các thư viện, không dùng mã hóa nào hoặc chỉ dùng mã hóa được miễn trừ. Mã hóa của hệ thống, như HTTPS qua URLSession, thường được miễn. Chỉ khai false nếu điều đó đúng với app của bạn: bạn chịu trách nhiệm về khai báo này. Như mọi khóa trong Info.plist, nó cần một build mới.
3. TestFlight. Ở trạng thái Ready to Submit, theo Apple, build có thể đến tay tester nội bộ, qua xét duyệt beta cho tester bên ngoài, hoặc được gửi xét duyệt App Store. Trường groups trong profile sẽ thêm build vào các nhóm nội bộ của bạn. Mọi thứ còn lại có trong bài TestFlight để thử nghiệm app.
Gửi build không phải là gửi xét duyệt
Expo viết rõ: một build TestFlight không tự động được phát hành lên App Store. EAS Submit không điền trang app, cũng không thêm ảnh chụp màn hình cho bạn. Còn bốn thao tác trong App Store Connect, được mô tả trong trang hỗ trợ của Apple về gửi xét duyệt:
- Điền metadata bắt buộc của phiên bản.
- Chọn build trong mục Build của phiên bản.
- Bấm “Add for Review”. Phiên bản chuyển sang Ready for Review, nhưng chưa có gì được gửi đi xét duyệt.
- Bấm “Submit for Review”. Trạng thái chuyển sang In Review khi quá trình xét duyệt bắt đầu.
Các lỗi thường gặp và cách sửa
| Lỗi hiển thị | Nguyên nhân | Cách sửa |
|---|---|---|
| ITMS-90189 | Số build đã được dùng | autoIncrement |
| ITMS-90062 hoặc 90186 | Phiên bản đã đóng | Tăng version |
| “Set ascAppId” | Thiếu ascAppId trong CI | Thêm ascAppId |
| “API Keys cannot” | Chưa gắn khóa nào | eas credentials |
| ITMS-90683 | Thiếu purpose string | ios.infoPlist |
| ITMS-91053 | Manifest chưa đầy đủ | ios.privacyManifests |
| ITMS-90725 | SDK iOS quá cũ | Nâng SDK Expo |
| Missing Compliance | Chưa khai báo mã hóa | usesNonExemptEncryption |
Theo Expo, số build đã bị dùng là nguyên nhân phổ biến khiến việc gửi bị từ chối: mọi chi tiết có trong bài số build và phiên bản. Ba lỗi có hướng dẫn riêng: ITMS-90683 với Expo, ITMS-91053 với Expo và ITMS-90725.
Hai thông báo trong ngoặc kép đến từ EAS và được sửa trong cấu hình của EAS. Missing Compliance cũng có thể xử lý bằng cách trả lời các câu hỏi trong App Store Connect. Với tất cả các dòng còn lại, cách sửa nằm ở file binary: build lại, rồi gửi lại. Một bản cập nhật từ xa không thay đổi được Info.plist, manifest hay các con số.
Gửi app đầu tiên mà không phải mò mẫm
Lệnh này chỉ là một bước. iOS Kit xuất phát từ code nền của các app của mình, có sẵn cả gói đăng ký, và đi qua bốn bước đến App Store, theo nhịp của bạn.
Nếu bạn đang chuẩn bị lần ra mắt đầu tiên, làm bài chẩn đoán miễn phí: nó cho bạn biết điều gì thực sự đang cản bạn và gửi bạn checklist chống bị Apple từ chối của mình. Về trang app, ảnh chụp màn hình và khâu duyệt, trang phát hành ứng dụng lên App Store đi qua từng bước.
Câu hỏi thường gặp
Làm sao để đưa app Expo lên App Store?
Chạy một build production bằng EAS Build, rồi gửi nó bằng eas submit --platform ios. Sau khi Apple xử lý, build xuất hiện trong TestFlight. Tiếp đó, trong App Store Connect, điền trang app, chọn build này trong phiên bản, rồi bấm “Add for Review” và “Submit for Review”.
EAS Submit có gửi app của tôi đi Apple xét duyệt không?
Không. EAS Submit gửi file binary lên App Store Connect, và build xuất hiện trong TestFlight. Việc gửi xét duyệt làm thủ công trong App Store Connect, sau khi đã điền trang app và chọn build.
Tìm ascAppId của app ở đâu?
Trong App Store Connect, mở app của bạn, rồi vào App Information. Đó là trường Apple ID, nằm dưới General Information. Đây là một con số, đừng nhầm với bundle identifier của bạn.
Có cần Apple ID để dùng eas submit không?
Ở lần chạy đầu, lệnh sẽ hỏi Apple ID của bạn. Sau đó, với ascAppId trong profile và một khóa API App Store Connect, lệnh có thể chạy mà không cần thao tác, kể cả trong CI. Khóa API là cách mặc định và được Expo khuyên dùng.
Mất bao lâu để thấy build trong TestFlight?
Expo cho biết Apple thường xử lý mất 10 đến 15 phút và gửi email cho bạn khi xong. Nếu build kẹt ở trạng thái Missing Compliance, hãy trả lời các câu hỏi về mã hóa, rồi khai báo nó trong cấu hình cho các build sau.