การอัปโหลดภาพหน้าจอ App Store ด้วย App Store Connect API
appScreenshotSet สำหรับภาษาและอุปกรณ์ที่ถูกต้อง, จอง appScreenshot (Apple จะส่ง URL สำหรับอัปโหลดแบบแบ่งชิ้นที่เซ็นล่วงหน้ามาให้), PUT ไบต์ของไฟล์ แล้ว PATCH การจองด้วย uploaded: true พร้อม MD5 ของไฟล์ การยืนยันตัวตนใช้ JWT อายุ 20 นาทีที่เซ็นด้วยคีย์ .p8 ของคุณ ไม่มีขั้นตอนไหนยาก แต่ทุกขั้นตอนล้วนจุกจิก นี่คือลำดับขั้นตอนของจริง — และทางลัดถ้าคุณไม่อยากสร้างมันเองเอกสารของ Apple สำหรับเรื่องนี้ถูกต้องแต่แทบอ่านไม่รู้เรื่อง — ขั้นตอนกระจายอยู่ในหน้าเอกสารอ้างอิงนับสิบหน้าโดยไม่มีตัวอย่างที่ครบถ้วนสักตัวอย่าง บทความนี้คือการเดินผ่านขั้นตอนทั้งหมดตามลำดับ พร้อมชื่อ endpoint และฟิลด์ที่แม่นยำ เขียนขึ้นสำหรับคนที่กำลังตัดสินใจว่าจะสร้างระบบเชื่อมต่อเอง หรือใช้เครื่องมือที่มีระบบนี้พร้อมอยู่แล้ว
โมเดลอ็อบเจกต์ที่คุณกำลังอัปโหลดเข้าไป
ภาพหน้าจอไม่ได้ผูกกับแอปของคุณโดยตรง แต่ห้อยอยู่กับลำดับชั้นของอ็อบเจกต์ ซึ่งคุณต้องไล่จากบนลงล่าง:
- appStoreVersion — เวอร์ชันที่คุณกำลังแก้ไข (ต้องอยู่ในสถานะแก้ไขได้)
- appStoreVersionLocalization — หนึ่งรายการต่อหนึ่งภาษาบน App Store (en-US, de-DE, ja และอื่น ๆ)
- appScreenshotSet — หนึ่งรายการต่อ ประเภทหน้าจอ ภายในแต่ละภาษา (iPhone 6.7 นิ้ว, iPad 13 นิ้ว ฯลฯ)
- appScreenshot — รูปแต่ละรูป สูงสุด 10 รูปต่อชุด
ดังนั้นรายชื่อแอปที่แปลครบทุกภาษาใน 50 ภาษา ครอบคลุมขนาด iPhone สองแบบและ iPad หนึ่งแบบ จะเท่ากับ 50 × 3 ชุด แต่ละชุดมีภาพหน้าจอได้สูงสุด 10 รูป การคูณเลขจำนวนนี้แหละคือเหตุผลที่คนส่วนใหญ่เลือกทำระบบอัตโนมัติแทนการคลิกผ่านหน้าเว็บทีละขั้น
ขั้นตอนที่ 0 — JWT อายุ 20 นาที
ทุกคำขอ ยกเว้นการอัปโหลดไบต์เอง ต้องมีส่วนหัว Authorization: Bearer ที่บรรจุ JSON Web Token ซึ่งคุณเซ็นเอง คุณต้องมีสามอย่างจาก App Store Connect ได้แก่ issuer ID, key ID และไฟล์คีย์ส่วนตัว .p8 ที่จะดาวน์โหลดได้ครั้งเดียวตอนสร้างคีย์ (Apple จะไม่แสดงให้เห็นอีกเลย)
โทเค็นถูกเซ็นด้วย ES256 — ECDSA บนเส้นโค้ง P-256 ร่วมกับ SHA-256 — โดยใช้คีย์ .p8 นั้น ส่วนหัวบรรจุ kid ส่วน payload บรรจุ issuer เป็น iss, audience เป็นสตริงคงที่ appstoreconnect-v1 และ exp ที่ห่างจาก iat ไม่เกิน 20 นาที Apple จะปฏิเสธโทเค็นที่มีอายุนานกว่านี้ ดังนั้นคุณต้องสร้างใหม่บ่อย ๆ — สร้างโทเค็นใหม่ต่อหนึ่งชุดงานแทนที่จะพยายามใช้อันเดียวค้างไว้
เซ็นโทเค็นนี้ด้วยไลบรารี JWT ที่รองรับ ES256 ตัวไหนก็ได้ (jsonwebtoken, PyJWT หรือของสแตกที่คุณใช้) ส่งเนื้อหาไฟล์ .p8 เป็นคีย์ แล้วใส่ผลลัพธ์ลงในส่วนหัว Authorization ของทุกคำขอด้านล่าง
ขั้นตอนที่ 1 — สร้างชุดภาพหน้าจอ
ชุดหนึ่งผูกกับหนึ่งภาษาและหนึ่งประเภทหน้าจอ คุณ POST ไปที่ /v1/appScreenshotSets พร้อม screenshotDisplayType ใน attributes และความสัมพันธ์ที่ชี้ไปยัง appStoreVersionLocalization ที่ต้องการกรอกข้อมูล
ประเภทหน้าจอที่พบบ่อย: APP_IPHONE_67 (6.7 นิ้ว, 1290 × 2796) ซึ่งเป็นขนาด iPhone ตัวใหญ่ปัจจุบัน และ APP_IPAD_PRO_3GEN_129 (12.9 นิ้ว, 2048 × 2732) สิ่งที่มีประโยชน์ที่ Apple ทำอยู่ตอนนี้คือ ภาพหน้าจอของ iPhone ขนาดใหญ่ที่สุดจะถูกนำไปใช้ซ้ำกับ iPhone รุ่นเล็กกว่า และของ iPad ขนาดใหญ่ที่สุดก็ใช้ซ้ำกับ iPad รุ่นเล็กกว่า — ในทางปฏิบัติคุณจึงมักต้องการแค่หนึ่งชุด iPhone และหนึ่งชุด iPad ต่อภาษา ไม่ใช่ต่ออุปกรณ์จริงแต่ละรุ่น การตอบกลับจะให้ id ของชุดที่คุณต้องนำไปใช้ในคำขอถัดไป หากมีชุดสำหรับภาษา/ประเภทนั้นอยู่แล้ว ให้ใช้ซ้ำแทนการสร้างซ้ำซ้อน
ขั้นตอนที่ 2 — จองภาพหน้าจอ
คุณไม่ได้อัปโหลดไฟล์ในครั้งเดียว ก่อนอื่นต้อง จอง ไฟล์นั้นก่อน: บอก Apple ถึงชื่อไฟล์และขนาดไบต์ที่แน่นอน แล้วระบบจะส่งแผนการอัปโหลดกลับมาให้ POST ไปที่ /v1/appScreenshots พร้อม attribute fileName และ fileSize รวมทั้งความสัมพันธ์ไปยังชุดจากขั้นตอนที่ 1
ส่วนที่น่าสนใจคือการตอบกลับ ภายใน attributes ของภาพหน้าจอใหม่ คุณจะได้ uploadOperations — อาร์เรย์ที่ระบุวิธีส่งไบต์อย่างละเอียด สำหรับไฟล์เล็กจะมีการดำเนินการเดียว ส่วนไฟล์ใหญ่ Apple จะแบ่งเป็นหลายส่วน แต่ละรายการจะให้ method (PUT), url ที่เซ็นล่วงหน้า, offset และ length ของไบต์ที่ต้องส่ง และ requestHeaders ที่ต้องแนบไปด้วย
ขั้นตอนที่ 3 — PUT ไบต์ของไฟล์
สำหรับแต่ละการดำเนินการ อ่านส่วนของไฟล์ตั้งแต่ offset ไปเป็นจำนวน length ไบต์ แล้ว PUT ไปยัง url ที่ให้มาพร้อม requestHeaders ตามที่ Apple ระบุไว้เป๊ะ ๆ URL เหล่านี้เซ็นล่วงหน้าไว้แล้ว ดังนั้น ห้าม ส่ง JWT ของคุณในคำขอนี้ — การใส่ส่วนหัว Authorization อาจทำให้คำขอที่เซ็นไว้เสียหายได้ การดำเนินการหลายรายการสามารถทำงานพร้อมกันได้ แต่ Apple มีการจำกัดอัตราสำหรับการอัปโหลดที่ถี่เกินไป จึงควรครอบด้วยการลองใหม่แบบ backoff แทนที่จะยิงทุกอย่างพร้อมกัน
ขั้นตอนที่ 4 — commit ด้วย uploaded: true และค่า MD5
การอัปโหลดไบต์เพียงอย่างเดียวไม่มีผลอะไร จนกว่าคุณจะบอก Apple ว่าการจองนี้เสร็จสมบูรณ์แล้ว PATCH ภาพหน้าจอกลับไปพร้อม uploaded: true และ sourceFileChecksum — ค่า MD5 ของไฟล์ ทั้งไฟล์ ในรูปแบบเลขฐานสิบหกตัวพิมพ์เล็ก นี่คือการตรวจสอบความถูกต้องของ Apple: หากค่าเช็คซัมไม่ตรงกับสิ่งที่อัปโหลดไป การประมวลผลจะล้มเหลว
หลังจาก PATCH แล้ว ไฟล์จะเข้าสู่กระบวนการประมวลผลฝั่ง Apple ให้ตรวจสอบ assetDeliveryState ของภาพหน้าจอซ้ำ ๆ จนกว่าจะถึงสถานะเสร็จสมบูรณ์ — และอ่าน errors หากล้มเหลว เพราะนั่นคือจุดที่ปัญหาขนาดผิดหรือมีช่องอัลฟาจะปรากฏให้เห็น การ PUT และ PATCH ทั้งคู่อาจสำเร็จได้ แต่ภาพหน้าจอก็ยังถูกปฏิเสธในภายหลังระหว่างการประมวลผลได้ อย่าคิดว่าสถานะ 2xx ตอน commit หมายความว่าเสร็จแล้ว ให้จับตาดูสถานะการส่งมอบต่อไป
ขั้นตอนที่ 5 — กำหนดลำดับการแสดงผล
ภาพหน้าจอจะถูกส่งกลับมาตามลำดับที่ถูกสร้างขึ้น ซึ่งมักไม่ใช่ลำดับที่คุณต้องการให้แสดง ลำดับของชุดเป็นความสัมพันธ์แยกต่างหาก PATCH /v1/appScreenshotSets/{id}/relationships/appScreenshots พร้อมอาร์เรย์ของรหัสภาพหน้าจอ — ลำดับในอาร์เรย์คือลำดับการแสดงผลบนสโตร์
ข้อจำกัดที่ Apple บังคับใช้จริง
- รองรับเฉพาะ PNG หรือ JPEG ห้ามใช้ HEIC หรือ WebP หากตัวเรนเดอร์ของคุณส่งออกเป็นรูปแบบอื่น ให้แปลงก่อน
- ห้ามมีช่องอัลฟา ต้องทำภาพโปร่งใสให้เป็นพื้นหลังทึบและส่งออกเป็น RGB ช่องอัลฟาที่หลงเหลืออยู่คือหนึ่งในสาเหตุการถูกปฏิเสธแบบเงียบ ๆ ที่พบบ่อยที่สุดตอนประมวลผล
- ขนาดต้องตรงเป๊ะตามประเภทหน้าจอ ภาพต้องมีขนาดพิกเซลตรงกับประเภทหน้าจอนั้นเป๊ะ ๆ ไม่ผิดเพี้ยนแม้เพียงเล็กน้อย ไม่มีการขยายภาพ ไฟล์ขนาด 1290 × 2796 ต้องอยู่ในชุด
APP_IPHONE_67เท่านั้น - สูงสุด 10 รูปต่อชุด ภาพหน้าจอสูงสุดสิบรูปต่อภาษาต่อประเภทหน้าจอ
- แก้ไขได้เฉพาะเวอร์ชันที่ยังแก้ไขได้ คุณสามารถแก้ไขชุดได้เฉพาะเวอร์ชันที่อยู่ในสถานะแก้ไขได้เท่านั้น เวอร์ชันที่อยู่ระหว่างการตรวจสอบแล้วจะถูกล็อก
ทำไมมันจึงยุ่งยากกว่าที่เห็น
แต่ละคำขอนั้นง่ายในตัวเอง แต่ต้นทุนอยู่ที่ทุกสิ่งรอบ ๆ มัน: การเซ็นและหมุนเวียนโทเค็นอายุ 20 นาที การคำนวณ MD5 การแบ่งไฟล์ให้ตรงกับ uploadOperations การจัดการการลองใหม่เมื่ออัปโหลดค้าง การตรวจสอบสถานะการส่งมอบ การแมปภาษาของคุณเข้ากับรหัสภาษาของ Apple และการทำทั้งหมดนี้ซ้ำมากกว่า 150 ครั้งสำหรับรายชื่อแอปที่แปลครบทุกภาษา การทำให้ระบบนี้เสถียรคือช่วงสุดสัปดาห์เต็ม ๆ ของงานยืนยันตัวตน อัปโหลดแบบแบ่งชิ้น และการจัดการข้อผิดพลาด — แล้วมันก็ยังเป็นภาระที่ต้องดูแลต่อทุกครั้งที่ Apple เพิ่มประเภทหน้าจอใหม่
หากคุณต้องการขั้นตอนนี้โดยไม่ต้องเขียนเอง คำสั่ง deliver ของ Fastlane ห่อหุ้ม API ตัวเดียวกันนี้ไว้ และเป็นแนวทางโอเพนซอร์สมาตรฐาน มันแก้ปัญหาการอัปโหลด แต่ไม่ได้ออกแบบภาพหน้าจอหรือเขียนคำบรรยายให้
…หรือข้ามทั้งหมดนี้ไปเลย
API ทำได้แค่ย้ายภาพที่เสร็จสมบูรณ์แล้ว แต่ยังต้องมีบางอย่างออกแบบภาพสไลด์ เขียนคำบรรยาย และแปลเป็นแต่ละภาษา — ซึ่งเป็นส่วนที่กินเวลาจริง ๆ Mokbi ทำสิ่งนี้ให้ในวันนี้: ออกแบบภาพหน้าจอ ร่างข้อความรายชื่อแอป และแปลทั้งหมดเป็น 50 ภาษา จากนั้นส่งออกทุกภาพในขนาดที่ตรงตามที่ API นี้กำหนดสำหรับแต่ละประเภทหน้าจอเป๊ะ ๆ — PNG, RGB, ไม่มีอัลฟา, ขนาดถูกต้อง — เพื่อให้การอัปโหลดไม่สะดุดตอนขั้นตอนตรวจสอบสถานะการส่งมอบ
จากนั้นมันเผยแพร่ให้เลย สำหรับ App Store, Mokbi รันขั้นตอน reserve-and-commit นี้เบื้องหลังจริง ๆ — อัปโหลดภาพหน้าจอและข้อมูลเมทาดาทาของคุณ แล้วจัดเตรียมเวอร์ชันให้พร้อมสำหรับคุณกดส่ง โดยคุณไม่ต้องแตะ JWT หรือการ PUT แบบแบ่งชิ้นเลย Apple ยังคงต้องให้คุณกด Submit ขั้นสุดท้ายและผ่านการตรวจสอบเอง แต่ทุกอย่างก่อนหน้านั้นถูกจัดการให้แล้ว สำหรับ Google Play มันจะเผยแพร่ตรงเข้าสโตร์เลย ไม่ว่าทางไหนคุณก็ได้ผลลัพธ์เดียวกับ API โดยไม่ต้องเขียนโค้ดเอง พร้อมงานออกแบบ คำบรรยาย และการแปล 50 ภาษาที่เสร็จเรียบร้อยแล้ว