Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng đội của bạn đã dành ba tháng xây dựng một ứng dụng, QA đã test kỹ từng màn hình, mọi test case đều pass, mọi bug ưu tiên cao đều đóng. Team ăn mừng, submit lên App Store lúc 11 giờ đêm để kịp campaign marketing sáng hôm sau. Đến 8 giờ sáng, Apple gửi email: "App Rejected — Guideline 5.1.1 — Data Collection and Storage". App bị từ chối không phải vì một bug chức năng nào cả, mà vì phần khai báo quyền riêng tư (App Privacy) không khớp với hành vi thật của app. Campaign delay một tuần, sếp marketing đứng ngồi không yên.
Đây là một tình huống cực kỳ phổ biến, và nó cho thấy một điều mà nhiều QA mới vào nghề chưa nhận ra: quá trình submit lên store là một phần của testing, không phải việc hành chính của developer. Với mobile QA, "app hoạt động tốt" chưa đủ — app còn phải qua được cửa kiểm duyệt của Apple App Store và Google Play. Đây là lớp gate cuối cùng trước khi tới tay người dùng, và nó có bộ tiêu chí riêng, thường bị bỏ sót vì nó nằm ngoài code.
Trong bài này, chúng ta tập trung riêng vào checklist submit lên App Store (chủ yếu là iOS App Store Connect) — những mục QA cần kiểm tra và xác nhận trước khi bấm nút "Submit for Review". Chúng ta sẽ không bàn về việc phân phối bản beta (đó là bài TestFlight & Internal Track), cũng không đi sâu vào crash reporting hay ký bản build — bài này thuần túy là danh sách kiểm tra để app được duyệt ngay lần đầu, tránh vòng lặp reject–sửa–resubmit tốn thời gian.
Khái niệm cốt lõi
Điểm mấu chốt cần hiểu: khi submit, ngoài file binary (.ipa) của app, bạn còn phải khai báo một loạt metadata và tuyên bố tuân thủ (compliance declarations) trên App Store Connect. Rất nhiều lần app bị reject không phải vì code, mà vì những khai báo này sai, thiếu, hoặc không khớp với hành vi thực tế. QA có vai trò đối chiếu "những gì app làm thật" với "những gì team khai báo".
1. App Privacy details — khai báo thu thập dữ liệu
Từ tháng 12/2020, Apple bắt buộc mọi app phải khai báo Privacy Nutrition Label — một bảng liệt kê rõ app thu thập loại dữ liệu nào (email, vị trí, danh bạ, định danh thiết bị, hành vi sử dụng...), dùng cho mục đích gì (analytics, quảng cáo, chức năng app), và dữ liệu đó có được liên kết với danh tính người dùng hay dùng để theo dõi (tracking) hay không.
Trách nhiệm của QA ở đây rất thực tế: bạn phải bắt được network traffic thật của app (dùng Charles Proxy, Proxyman hoặc mitmproxy) và đối chiếu xem app thực sự gửi những gì lên server và các SDK bên thứ ba. Nếu app tích hợp Facebook SDK, Firebase Analytics, AppsFlyer... thì các SDK đó âm thầm thu thập device identifier, IP, sự kiện — và tất cả phải được khai báo. Apple reviewer sẽ chạy app, quan sát traffic, và đối chiếu với nhãn Privacy. Không khớp = reject.
2. App Tracking Transparency (ATT)
Nếu app theo dõi người dùng qua các app/website khác (ví dụ dùng IDFA cho quảng cáo, hoặc AppsFlyer để đo attribution), app bắt buộc phải hiển thị prompt ATT ("Cho phép [App] theo dõi hoạt động của bạn...") và chỉ được tracking sau khi người dùng bấm "Allow". QA cần verify: prompt có hiện đúng chưa, nếu người dùng chọn "Ask App Not to Track" thì app có ngừng gửi IDFA thật không (kiểm bằng proxy). Đây là một trong những lý do reject số một của các app Việt Nam có gắn quảng cáo hoặc đo lường marketing.
3. Export Compliance — khai báo mã hóa
Mỗi lần submit, App Store Connect hỏi: "Does your app use encryption?" Gần như mọi app hiện đại đều dùng HTTPS, nên câu trả lời thường là "Yes". Nhưng phần lớn app chỉ dùng mã hóa tiêu chuẩn (HTTPS/TLS) nên được miễn trừ theo diện exempt. Để tránh bị hỏi lại mỗi lần build, team thường thêm key ITSAppUsesNonExemptEncryption = false vào file Info.plist. QA nên kiểm tra key này có mặt để mỗi bản build không bị chặn ở bước export compliance. Nếu app dùng mã hóa tùy chỉnh, phi tiêu chuẩn (hiếm), thì cần khai báo và có thể phải nộp giấy tờ pháp lý — nhưng đó là ngoại lệ.
4. Age Rating — phân loại độ tuổi
Bạn phải trả lời một bộ câu hỏi để Apple gán nhãn độ tuổi (4+, 9+, 12+, 17+). Các câu hỏi liên quan tới bạo lực, nội dung người lớn, cờ bạc, nội dung do người dùng tạo (UGC), truy cập web không lọc... QA cần đối chiếu: nếu app có webview mở web tự do, hoặc có chat/bình luận giữa người dùng, thì age rating và phần khai báo phải phản ánh đúng. Khai thấp hơn thực tế là lý do reject.
5. Metadata & tài sản hiển thị
- Screenshots đúng kích thước cho từng loại thiết bị (iPhone 6.7", 6.5", iPad...), và phản ánh đúng app hiện tại — không được dùng ảnh của tính năng chưa có.
- App icon không trùng, không gây nhầm với app khác.
- Mô tả, keyword, tên app không chứa tên thương hiệu đối thủ, không hứa hẹn tính năng không có.
- Support URL và Privacy Policy URL phải sống, mở được, và trang privacy policy phải khớp với nhãn App Privacy.
6. Demo account & Review Notes
Nếu app có màn hình đăng nhập, bạn bắt buộc cung cấp tài khoản demo (username/password) trong phần "App Review Information" để reviewer đăng nhập được. Đây là lỗi kinh điển: reviewer không đăng nhập được → reject Guideline 2.1. Với app có OTP qua SMS Việt Nam, cần chuẩn bị tài khoản test bypass OTP hoặc ghi rõ hướng dẫn trong Review Notes.
Tình huống thực tế
Ví dụ 1 — App gọi xe bị reject vì Privacy Label lệch với SDK
Một startup gọi xe ở TP.HCM (gọi tên là "GoRide") submit phiên bản 2.0. App tích hợp Google Maps SDK, Firebase, và AppsFlyer để đo hiệu quả quảng cáo. Team dev khai App Privacy chỉ gồm "Location" và "Email", vì họ nghĩ đó là dữ liệu app chủ động dùng.
Apple reject với Guideline 5.1.1. Lý do: reviewer bắt được app gửi device identifier và coarse location lên AppsFlyer để phục vụ attribution — thứ này thuộc diện "Data Used to Track You" nhưng không được khai. Bạn QA của team sau đó bật Proxyman lên, chạy lại toàn bộ flow onboarding, và liệt kê được 4 endpoint bên thứ ba đang nhận dữ liệu mà nhãn Privacy chưa hề nhắc tới.
Bài học: QA không thể tin lời khai của dev về dữ liệu. Phải quan sát traffic thật. Với mỗi SDK marketing/analytics, hãy giả định nó thu thập identifier và tracking cho tới khi chứng minh ngược lại. Từ sau vụ này, GoRide thêm một bước bắt buộc trong checklist submit: "Audit network traffic vs. Privacy Label".
Ví dụ 2 — App fintech bị reject vì reviewer không đăng nhập được
Một ví điện tử (giả định là "PayViet") submit bản cập nhật. App yêu cầu đăng nhập bằng số điện thoại Việt Nam + OTP SMS. Team quên rằng reviewer của Apple ngồi ở Mỹ, không có số điện thoại Việt Nam để nhận OTP. Họ để trống phần demo account vì "app này ai cũng đăng ký được mà".
Kết quả: reject Guideline 2.1 — "We were unable to sign in to review your app". Mất trọn 26 giờ chờ review chỉ để nhận một lời từ chối vì lý do hoàn toàn tránh được.
Lần resubmit, QA chuẩn bị một tài khoản test có OTP cố định (server cấu hình mã 000000 cho số test), và ghi rõ trong Review Notes: "Test account: +84... / OTP luôn là 000000". App được duyệt sau 18 giờ.
Bài học: Mọi app có màn hình login phải có đường vào cho reviewer. Đặc biệt với đặc thù Việt Nam (OTP SMS, xác thực số nội địa), QA phải chủ động nghĩ tới "reviewer nước ngoài sẽ vào bằng cách nào?". Checklist submit phải có mục "Demo account hoạt động + hướng dẫn login trong Review Notes".
Ví dụ 3 — App tin tức bị reject vì Age Rating và webview
Một app đọc báo tổng hợp mở link bài viết trong webview không giới hạn — người dùng có thể lướt tới bất kỳ trang web nào. Team khai Age Rating là 4+. Apple reject theo Guideline 2.3.6 / age rating vì app cho phép truy cập web không lọc, nội dung không kiểm soát được, phải là 17+ hoặc phải có bộ lọc nội dung.
Đội QA họp lại và quyết định: thay vì nâng lên 17+ (làm giảm lượng tải), họ giới hạn webview chỉ mở các domain trong whitelist, và thêm nút "mở bằng Safari" cho link ngoài. Sau đó khai lại age rating cho đúng.
Bài học: Age rating không phải là con số tùy chọn cho đẹp — nó phải khớp với khả năng thực tế của app. Bất cứ khi nào app có webview mở tự do, UGC, hay chat, QA phải rà lại phần khai báo phân loại độ tuổi.
Hướng dẫn từng bước
Đây là checklist QA thực hiện trước khi bấm Submit for Review. Hãy biến nó thành một document dùng lại cho mọi lần release.
Bước 1 — Rà soát binary & thông tin build. Xác nhận đúng số version/build number, build được ký đúng distribution profile, chạy được trên thiết bị thật (không chỉ simulator). Đảm bảo không còn URL môi trường test (staging/dev) bị hardcode trong bản production.
Bước 2 — Audit network traffic để đối chiếu App Privacy. Bật Proxyman/Charles, chạy toàn bộ các flow chính (onboarding, login, thanh toán, đăng bài...). Ghi lại mọi endpoint bên thứ ba nhận dữ liệu. Lập bảng: dữ liệu gì → gửi đi đâu → mục đích gì → có tracking không. Đối chiếu với nhãn App Privacy trên App Store Connect. Mọi mục lệch phải sửa.
Bước 3 — Verify ATT prompt (nếu có tracking). Nếu bảng ở bước 2 có tracking: kiểm prompt ATT hiện đúng, và khi người dùng từ chối thì app ngừng gửi IDFA (verify lại bằng proxy). Nếu không có tracking, đảm bảo app không hiện prompt ATT thừa (cũng bị reject).
Bước 4 — Kiểm Export Compliance.
Xác nhận ITSAppUsesNonExemptEncryption có trong Info.plist (thường = false với app dùng HTTPS chuẩn), để tránh bị chặn ở bước export compliance mỗi lần build.
Bước 5 — Đối chiếu Age Rating. Rà webview tự do, UGC, chat, cờ bạc, nội dung nhạy cảm. Trả lời bộ câu hỏi age rating cho đúng với hành vi thật của app.
Bước 6 — Kiểm metadata & tài sản. Screenshot đúng kích thước & đúng phiên bản; icon hợp lệ; mô tả/keyword không vi phạm; Support URL và Privacy Policy URL mở được và nội dung khớp.
Bước 7 — Chuẩn bị Review Information. Demo account hoạt động (test ngay bằng thiết bị sạch), OTP/2FA có đường vòng cho reviewer, ghi Review Notes rõ ràng bằng tiếng Anh, kèm hướng dẫn cho các tính năng ẩn hoặc cần điều kiện đặc biệt.
Bước 8 — Kiểm các mục hay bị bỏ sót. Sign in with Apple (nếu app có login mạng xã hội bên thứ ba, Apple thường yêu cầu có thêm Sign in with Apple), link tới Terms/EULA cho app có subscription, và deeplink/universal link hoạt động nếu marketing dùng chúng.
Bước 9 — Ký xác nhận (sign-off). QA ký vào checklist, ghi ngày, số build. Chỉ khi mọi mục xanh mới submit.
Lỗi thường gặp & mẹo
- Tin lời khai của dev về dữ liệu thu thập. Đây là lỗi phổ biến nhất. SDK bên thứ ba thu thập nhiều hơn dev nghĩ. Luôn verify bằng proxy.
- Quên demo account, hoặc để demo account hết hạn / bị khóa. Test tài khoản demo trên một thiết bị vừa cài mới, ngay trước khi submit.
- Screenshot lệch với app thật (chụp từ bản thiết kế Figma hoặc từ tính năng chưa release). Reviewer coi đây là quảng cáo sai.
- Hiện ATT prompt trong khi app không hề tracking, hoặc ngược lại — tracking mà không hỏi. Cả hai đều reject.
- Privacy Policy URL trỏ tới trang 404 hoặc trang chung chung không nói về dữ liệu app thu thập.
- Mẹo: Lưu checklist này thành template (Google Sheet/Notion) với cột "Pass/Fail/Notes" và gán người phụ trách từng mục. Release nào cũng copy ra một bản mới.
- Mẹo: Đọc trước "Common App Rejections" trên trang developer của Apple — Apple công khai các lý do reject phổ biến nhất, cập nhật thường xuyên.
- Mẹo: Với app Việt Nam, luôn thêm ghi chú về OTP/số điện thoại nội địa trong Review Notes — đây là điểm mù mà đội nào cũng dễ vấp lần đầu.
Bài tập thực hành
- Lập bảng đối chiếu Privacy. Chọn một app bạn đang test (hoặc một app bất kỳ có dùng analytics). Bật Charles/Proxyman, chạy 3 flow chính, và lập bảng: mỗi endpoint bên thứ ba → dữ liệu gửi đi → phân loại (functionality / analytics / tracking). So sánh với nhãn App Privacy của app đó trên App Store.
- Viết Review Notes. Giả sử app có login OTP SMS Việt Nam và một tính năng chỉ mở khi tài khoản đã KYC. Viết đoạn Review Information hoàn chỉnh bằng tiếng Anh: demo account, cách nhận OTP, cách vào tính năng ẩn.
- Rà age rating. Cho một app tin tức có webview mở link ngoài tự do. Liệt kê các câu hỏi age rating bị ảnh hưởng, và đề xuất 2 phương án kỹ thuật để giữ được rating thấp mà vẫn hợp lệ.
- Xây template checklist. Chuyển 9 bước ở phần hướng dẫn thành một checklist có thể tái sử dụng (Sheet/Notion) với cột trạng thái và người phụ trách. Đây sẽ là tài sản QA dùng cho mọi lần release.
Tóm tắt
App Store submission là lớp gate cuối cùng trước người dùng, và nó có bộ tiêu chí riêng nằm ngoài code mà QA phải làm chủ. Bốn trụ cột dễ khiến app bị reject là: App Privacy (khai báo thu thập dữ liệu phải khớp traffic thật, verify bằng proxy), Export Compliance (thường chỉ cần khai đúng key encryption), Age Rating (phải khớp khả năng thật của app, đặc biệt với webview/UGC/chat), và Demo account + Review Notes (đường vào cho reviewer, cực kỳ quan trọng với đặc thù OTP Việt Nam). Ba tình huống thực tế cho thấy: hầu hết reject đều tránh được nếu QA có một checklist kỷ luật và chịu khó đối chiếu "app làm thật" với "team khai báo". Hãy biến checklist 9 bước ở trên thành template dùng lại — mỗi lần được duyệt ngay lần đầu là bạn tiết kiệm cho team cả tuần chờ đợi và giữ được lịch release.