Automate iOS screenshot with clean status bar and upload result to firebase hosting 🤖
เชื่อว่าหลายๆท่านคงจำเป็นที่จะต้องจับภาพหน้าจอตัวอย่างของ application เพื่อที่จะนำไปอัพโหลดให้ iTunes Connect ถ้าหากจะต้องปล่อยฟีเจอร์ใหม่ทุกๆ 1–2 เดือน ก็คงจะไม่มีปัญหาถ้าจะทำใหม่ทุกครั้ง เพราะแต่ละครั้งคงจะใช้เวลาอย่างน้อย 1 ชม. สำหรับ(3–5 หน้า) เพียงขนาดหน้าจอเดียว และมีเพียงไม่กี่ภาษา(ไทย/English) แต่ถ้าหากมีจำนวนหน้าเพิ่มขึ้น หลายขนาดหน้าจอ หรือ ภาษาที่รองรับมากขึ้น หรือ ฟีเจอร์ใหม่ปล่อยบ่อยขึ้น เช่น 5 หน้าจอ 3 ขนาด 3 ภาษา ทุกๆ 2 สัปดาห์ คงจะเหนื่อยไม่น้อยที่จะต้องมาจับภาพหน้าจอ manual แบบนี้ ทุก 2 สัปดาห์ ต้องจับภาพหน้าจอ 45 ครั้ง (5*3*3) ซึ่งใช้เวลาประมาณ 2–3 ชั่วโมง ต่อครั้งในการปล่อย application
Solution
วันนี้จะมาแนะนำ Fastlane, UITest, SimulatorStatusMagic เพื่อทำ screenshot และอัพโหลดเพื่อส่งงานด้วย firebase hosting (ผมไม่ใช้ระบบอัพโหลดเข้า iTunes Connect อัตโนมัติ เพราะต้องให้เพื่อนร่วมงานตรวจสอบก่อนครับ) ถ้าไม่ต้องการ upload ก็ข้ามช่วง firebase ไปได้เลยครับ
Fastlane + UITest
fastlane เป็นเครื่องมือสารพัดประโยชน์สำหรับนักพัฒนา iOS และ Android application โดยเราจะพูดถึงแค่ส่วน fastlane snapshot ถ้าหากว่ายังไม่ได้ติดตั้ง fastlane ให้พิมพ์ตามนี้
# install fastlane
$ [sudo] gem install fastlane -NV # init fastlane (choose custom lane)
$ fastlane init
เริ่มใช้ฟีเจอร์ snapshot
$ fastlane snapshot init
สร้าง UI Test สำหรับ snapshot (File >> New >> Target >> UI Test)
ลากไฟล์ fastlane/SnapshotHelper.swift
ไปยัง Snapshot group ใน Xcode
สร้าง scheme Snapshot
ตั้งค่าให้เป็น share scheme (Manage Schemes >> check box shared) และตั้งค่า Build (Test/Run)
เปลี่ยนชื่อไฟล์ Snapshot.swift
>> SnapshotTest.swift
และแก้ไขไฟล์เพื่อเพิ่ม test สำหรับ Snapshot ในส่วนของ setUp
จะถูกเรียกทุกครั้งก่อนที่รันเทส และ tearDown
จะถูกเรียกทุกครั้งหลังที่รันเทสครับ และ ส่วนเทสของเราจะอยู่ที่ testSnapshotSearchFlow
ส่วนที่ขาดไม่ได้ก็คือ snapshot(“NAME”)
ซึ่งจะเป็นคำสั่งที่ให้เริ่มจับภาพหน้าจอ ในที่นี้ผมจะไม่พูดถึงการเขียน UITest ให้กดปุ่มหรือพิมพ์ นะครับ โดยจะต้องเขียนขั้นตอนต่างๆไว้ใน setupNAMEScreen()
เพื่อให้สามารถกดปุ่มไปหน้าที่ต้องการ หรือ พิมพ์ในช่อง TextField ต่างๆ
class SnapshotTest: XCTestCase {
var app: XCUIApplication!
override func setUp() {
app = XCUIApplication()
setupSnapshot(app)
app.launch()
super.setUp()
}
override func tearDown() {
app = nil
super.tearDown()
}
func testSnapshotSearchFlow() {
setupHomeScreen()
snapshot("Home")
setupCarTypeScreen()
snapshot("CarType")
backToHomeFromCarTypeScreen()
setupSearchScreen()
snapshot("Search")
setupCarListScreen()
snapshot("CarList")
}
}
แก้ไขไฟล์ fastlane/Snapfile
เพื่อตั้งค่า devices, languages, scheme(ตั้งหน้าที่ต้องการ ซึ่งต้องตรงกับที่ใช้ใน UI test ด้านบน) ซึ่งในที่นี้ผมเลือก 4 ขนาดหน้าจอ 1 ภาษา และ 4 หน้าจอ (Home, CarType, Search, CarList) ครับ
devices([
"iPhone SE",
"iPhone 8",
"iPhone 8 Plus",
"iPhone X"
])
languages([
"en-US"
])
scheme("Home")
scheme("CarType")
scheme("Search")
scheme("CarList")
แก้ไขไฟล์ fastlane/Fastfile
โดยให้ใส่ไว้ภายใต้ platform :ios do
ซึ่ง PROJECT-NAME.xcworkspace และ SNAPSHOT-SCHEME-NAME จะต้องเปลี่ยนตามชื่อไฟล์ และ scheme
lane :screenshots do
capture_screenshots(workspace: "PROJECT-NAME.xcworkspace", scheme: "SNAPSHOT-SCHEME-NAME")
end
ทดลองคำสั่ง fastlane screenshots
ก็จะได้ภาพ snapshot ตามที่เราต้องการ ใน directory fastlane/screenshots
Customize status bar
ถ้าต้องการให้ status bar มีเวลาที่คงที่และ ต้องการซ่อน icon ต่างๆที่ไม่จำเป็นออกไป สามารถใช้ SimulatorStatusMagic ช่วยได้ เริ่มต้นด้วยการติดตั้ง pod ซึ่งต้องระบุชื่อ scheme ที่จะใช้ตามนี้ครับ target ‘SCHEME-NAME’ do
target 'Snapshot' do
pod 'SimulatorStatusMagic'
end
แก้ไขไฟล์ SnapshotTest.swift ในส่วนของ setUp
tearDown
และ import
library ใหม่เข้ามา
import SimulatorStatusMagic
class SnapshotTest: XCTestCase {
var app: XCUIApplication! override func setUp() {
cleanStatusBar()
app = XCUIApplication()
setupSnapshot(app)
app.launch()
super.setUp()
}
override func tearDown() {
SDStatusBarManager.sharedInstance().disableOverrides()
app = nil
super.tearDown()
}
}
ตั้งค่า status bar (ลบชื่อ carrier, ตั้งเวลา, ซ่อนบลูทูธ, ซ่อนข้อมูลแบต)
extension SnapshotTest {private func cleanStatusBar() {
SDStatusBarManager.sharedInstance().carrierName = ""
SDStatusBarManager.sharedInstance().timeString = "10:09"
SDStatusBarManager.sharedInstance().bluetoothState = .hidden
SDStatusBarManager.sharedInstance().batteryDetailEnabled = false
SDStatusBarManager.sharedInstance().enableOverrides()
}
}
Firebase hosting (snapshot files)
ติดตั้งและ login
$ npm install -g firebase-tools
$ firebase login
เข้าไปที่ fastlane
directory เปลี่ยนชื่อ file screenshots.html เป็น index.html และตั้งค่า firebase โดยเลือก Hosting และโปรเจคที่ต้องการ หรือจะสร้างใหม่ก็ได้ครับ
$ cd fastlane
$ mv screenshots/screenshots.html screenshots/index.html
$ firebase init
และให้ config ตามนี้ครับ
What do you want to use as your public directory? screenshotsConfigure as a single-page app (rewrite all urls to /index.html)? YesFile screenshots/index.html already exists. Overwrite? No
ทดลอง ด้วยคำสั่ง $ firebase serve
และ deploy ด้วย $ firebase deploy
ครับ
แก้ไข fastlane/Fastfile
เพื่อให้ zip ผลลัพธ์ ชื่อ results.zip และ auto deploy หลังจาก generate snapshot เรียบร้อย
lane :screenshots do
capture_screenshots(workspace: "PROJECT-NAME.xcworkspace", scheme: "SNAPSHOT-SCHEME-NAME")
sh "mv screenshots/screenshots.html screenshots/index.html"
sh "zip -r results.zip screenshots/* -x *.html* -x *.DS_Store*"
sh "mv results.zip screenshots/results.zip"
sh "firebase deploy"
end
เสร็จแล้วครับ ลอง fastlane screenshots
อีกครั้ง ถ้าผ่านก็ฉลองได้เลย 🍻 ผลลัพธ์ของผมอยู่ที่นี่
บทความที่ผมใช้ในการศึกษา fastlane snapshot และ SimulatorStatusMagic