ในเดือนมิถุนายน 2023 เมื่อ Apple ประกาศเปิดตัว Vision Pro ฉันมีความคิดที่จะใช้งานได้ดีบนชุดหูฟัง ซึ่งเป็นคอลเลกชันวิดีโอที่เล่นวนซ้ำไปพร้อมกับการใช้งานประจำวันของคุณ ฉันมีแอปที่สามารถทำสิ่งนี้ได้อยู่แล้ว ชื่อว่า Christmas Chill ซึ่งเป็นสิ่งที่ ฉันสร้างขึ้น เมื่อ Apple TV เครื่องแรกที่รองรับ App Store วางจำหน่าย แอปนี้มีคอลเลกชันวิดีโอที่เล่นวนซ้ำซึ่งคุณสามารถใช้เป็นฉากหลังในเทศกาลได้
ทุกปีในช่วงฤดูหนาว ฉันจะใช้เวลาสองสามวันในการปรับปรุงโครงการ เพิ่มเนื้อหาใหม่ และปรับปรุงฐานโค้ด การเปลี่ยนแปลงครั้งใหญ่ครั้งหนึ่งที่เกิดขึ้นกับโครงการเกิดขึ้นในเดือนธันวาคม 2023 เมื่อ UI ถูกย้ายจาก UIKit และ Storyboards ไปยัง SwiftUI
ส่วนใหญ่แล้วได้ย้ายข้อมูลไปแล้ว ฉันต้องการมุมมองที่รองรับ AVPlayer ซึ่งรวมอยู่ใน UIViewRepresentable ซึ่งเป็น API ที่ยอดเยี่ยมที่ให้การทำงานร่วมกันระหว่าง UIKit และ SwiftUI หากคุณต้องการ
ฉันค่อนข้างลังเลที่จะย้ายข้อมูลเร็วกว่านี้ เนื่องจากฉันเข้าใจ Declarative UI และแนวคิดของมันเป็นอย่างดีจากงานอื่นๆ ที่ใช้ React และ Jetpack Compose แต่สิ่งนั้นเปลี่ยนไปสำหรับฉันเมื่อ Apple แนะนำ Apple Vision Pro และรองรับ SwiftUI Christmas Chill เป็นโปรเจ็กต์ที่ดีที่ช่วยให้ความรู้ด้าน Apple Dev ของฉันทันสมัยอยู่เสมอ และฉันกระตือรือร้นที่จะได้รับประสบการณ์ในการขยายแอปไปยังอุปกรณ์ต่างๆ
เมื่อการโยกย้ายไปยัง SwiftUI for Christmas Chill ในปี 2023 เสร็จสิ้นแล้ว ฉันจึงตั้งเป้าที่จะเพิ่มการรองรับ Vision Pro ในปี 2024 ด้านล่างนี้คือขั้นตอนการดำเนินการและคำแนะนำของฉันหากคุณกำลังพยายามทำเช่นเดียวกันสำหรับแอปของคุณเอง
ในการเริ่มต้น โปรเจ็กต์จะต้องสามารถสร้างสำหรับ Vision Pro เป็นจุดหมายปลายทางได้ ซึ่งการทำเช่นนี้เป็นเรื่องง่ายอย่างน่าประหลาดใจ! ใน Xcode ให้เลือกไฟล์ . .xcodeproj
จากนั้นคลิกปุ่มบวกภายใต้ รายการดรอปดาวน์ปลายทางที่รองรับ
เมนูแบบดรอปดาวน์ของแพลตฟอร์ม Apple ที่มีอยู่ทั้งหมดจะปรากฏขึ้น เลื่อนเมาส์ไปเหนือแพลตฟอร์มที่ต้องการเพื่อเพิ่มเป็นจุดหมายปลายทาง ในกรณีนี้คือ Apple Vision จากนั้นคลิก Apple Vision ในส่วนที่เพิ่งปรากฏขึ้น
ป๊อปอัปขนาดเล็กจะปรากฏขึ้นเพื่อแจ้งให้คุณทราบถึงการเปลี่ยนแปลงที่ Xcode ต้องทำกับเป้าหมาย คลิกเปิด ใช้งาน
ขั้นตอนต่อไปคือสร้างแอปโดยใช้โปรแกรมจำลอง visionOS หากคุณมี Vision Pro อยู่ในมือ คุณสามารถดูคำแนะนำเกี่ยวกับวิธีการติดตั้งลงในอุปกรณ์ของคุณ ได้ที่นี่
ระหว่างการคอมไพล์ Xcode อาจพบข้อผิดพลาดของคอมไพเลอร์ และ/หรือแอปอาจขัดข้อง ซึ่งถือเป็นเรื่องปกติและควรอดทนรอ จากจุดนี้ คุณต้องแก้ไขข้อผิดพลาดในโปรเจ็กต์ของคุณจนกว่าแอปจะคอมไพล์ได้และไม่ทำงานขัดข้องอีกต่อไป
ในกรณีของฉัน ใช้เวลาประมาณ 30 นาที ซึ่งส่วนหนึ่งต้องขอบคุณการทำงานหนักในการย้ายแอปจาก UIKit ไปยัง SwiftUI ก่อนหน้านี้!
SwiftUI เป็นเฟรมเวิร์กแบบมัลติแพลตฟอร์ม ซึ่งหมายความว่าเพียงแค่คอมไพล์โค้ด SwiftUI สำหรับแพลตฟอร์มอื่น ก็จะเปลี่ยนรูปลักษณ์ของเฟรมเวิร์กได้ โดยคำนึงถึงรูปแบบของแพลตฟอร์มและวิธีการโต้ตอบต่างๆ
แม้ว่าสิ่งนี้จะช่วยให้พัฒนาได้รวดเร็วขึ้น แต่คุณอาจต้องการควบคุมการแสดงผลของแอปและใช้ประโยชน์จากจุดแข็งของแต่ละแพลตฟอร์ม ตัวอย่างที่ดีคือความสามารถ Immersion ของ Vision Pro SwiftUI จัดเตรียม API สำหรับคุณสมบัตินี้ผ่าน ImmersiveSpace ซึ่งเป็น API ที่มีให้เฉพาะใน visionOS เท่านั้น
หากคุณพยายามใช้ API นี้ในขณะคอมไพล์โปรเจ็กต์สำหรับ Apple TV, Xcode จะแสดงข้อผิดพลาดเพื่อแจ้งให้ทราบว่า API นี้ไม่พร้อมใช้งาน
แล้วทางแก้ไขเพื่อหลีกเลี่ยงสถานการณ์ดังกล่าวคืออะไร คำตอบคือการใช้ Conditional Compilation Blocks Compilation Blocks คือส่วนของโค้ดที่ให้คำแนะนำว่าเมื่อใดคอมไพเลอร์จึงควรคอมไพล์โค้ดภายในบล็อค
แม้ว่าจะรองรับเงื่อนไขต่างๆ มากมาย แต่สิ่งที่มีประโยชน์ที่สุดสำหรับความต้องการของเราคือการตรวจจับว่าโค้ดถูกคอมไพล์สำหรับแพลตฟอร์มใด คุณสามารถทำได้โดยใช้โค้ดเพียงไม่กี่บรรทัด:
var body: some Scene { #if os(tvOS) WindowGroup { HStack { Text("I am running on tvOS!") } } #elseif os(visionOS) ImmersiveSpace(id: "MyImmersiveSpace") { } #endif }
ฟีเจอร์ที่ดีอย่างหนึ่งที่ Xcode ทำเพื่อรองรับบล็อกการคอมไพล์แบบมีเงื่อนไขก็คือ การแสดงให้เห็นอย่างชัดเจนว่าโค้ดใดที่จะคอมไพล์ได้ขึ้นอยู่กับแพลตฟอร์มที่เลือกสำหรับการคอมไพล์ นอกจากนี้ Xcode ยังจะลดโค้ดที่ไม่ถูกคอมไพล์ลงเล็กน้อยด้วย
เคล็ดลับที่มีประโยชน์อย่างหนึ่งที่ฉันพบคือการใช้ขั้นตอนการสร้าง Compile Sources และ Copy Bundle Resources เป็นรูปแบบหนึ่งของการฉีดการอ้างอิง กระบวนการเหล่านี้จะทำงานเมื่อสร้างแอปและสามารถพบได้ภายใต้แท็บ Build Phases ใน Xcode Project
Compile Sources ทำหน้าที่คอมไพล์โค้ดต้นฉบับของคุณเป็นโค้ดเครื่อง ไม่ว่าจะเป็น Swift, Objective-C หรือแม้แต่ C/C++
Copy Bundle Resources คัดลอกทรัพยากรที่เกี่ยวข้องทั้งหมดสำหรับเป้าหมายแอปลงใน App Bundle ซึ่งเป็นคอนเทนเนอร์ สำหรับโค้ดและทรัพยากรทั้งหมดของแอป รวมถึงรูปภาพ วิดีโอ สตริงที่แปลเป็นภาษาท้องถิ่นได้ และอื่นๆ
ขั้นตอนการสร้างทั้งสองขั้นตอนนี้ทำให้แอปมีความยืดหยุ่นมากขึ้น เนื่องจากแต่ละเป้าหมายใหม่จะมีขั้นตอนการสร้างของตัวเอง รวมถึงขั้นตอนสองขั้นตอนข้างต้น แอป Whitelabel ที่ให้ธุรกิจปรับแต่งเนื้อหาของตนเองใช้เทคนิคนี้ รวมถึงเทคนิคอื่นๆ ด้วย
คุณอาจพบว่าคุณต้องการจัดเตรียมเนื้อหาที่แตกต่างกันสำหรับแอปของคุณเอง ขึ้นอยู่กับแพลตฟอร์มที่แอปนั้นทำงาน มาใช้ประโยชน์จากขั้นตอนการสร้างเหล่านี้โดยจัดเตรียมแหล่งเนื้อหาที่แตกต่างกันสองแหล่งเพื่อทำเช่นนั้น
ก่อนอื่น ให้ใช้โปรโตคอล Swift เพื่อจัดทำสัญญาที่คาดว่าจะได้รับการปฏิบัติตามโดยโครงสร้างหรือคลาส
protocol ContentManager { var content: [Content] { get } }
ต่อไปมาดูตัวดำเนินการโปรโตคอล 2 ตัวกัน ตัวแรกคือ:
class TargetAppAContentManager : ContentManager { var content: [Content] { return [ Content(name: TargetAppAContentIdentifier.videoOneName.rawValue, image: TargetAppAImagePreviewIdentifier.videoOnePreview.rawValue, video: TargetAppAImageVideoIdentifier.videoOneVideo.rawValue), Content(name: TargetAppAContentIdentifier.videoTwoName.rawValue, image: TargetAppAImagePreviewIdentifier.videoTwoPreview.rawValue, video: TargetAppAImageVideoIdentifier.videoTwoVideo.rawValue), Content(name: TargetAppAContentIdentifier.videoThreeName.rawValue, image: TargetAppAImagePreviewIdentifier.videoThreePreview.rawValue, video: TargetAppAImageVideoIdentifier.videoThreeVideo.rawValue), ] return contentToShow } }
TargetAppAContentManager
คือการใช้งานจริงที่ใช้ได้กับเป้าหมายแอปแรก โดยจัดเตรียมอาร์เรย์ของ Content
ซึ่งอ้างอิงถึงชื่อทรัพยากรที่พบในชุดแอปสำหรับเป้าหมาย
class TargetAppBContentManager : ContentManager { var content: [Content] { return [ Content(name: TargetAppBContentIdentifier.videoOneName.rawValue, image: TargetAppBImagePreviewIdentifier.videoOnePreview.rawValue, video: TargetAppBImageVideoIdentifier.videoOneVideo.rawValue), Content(name: TargetAppBContentIdentifier.videoTwoName.rawValue, image: TargetAppBImagePreviewIdentifier.videoTwoPreview.rawValue, video: TargetAppBImageVideoIdentifier.videoTwoVideo.rawValue), Content(name: TargetAppBContentIdentifier.videoThreeName.rawValue, image: TargetAppBImagePreviewIdentifier.videoThreePreview.rawValue, video: TargetAppBImageVideoIdentifier.videoThreeVideo.rawValue), ] } }
ถัดไปคือ TargetAppBContentManager
ซึ่งเป็นการใช้งานจริงสำหรับเป้าหมายแอปที่สอง ดูคล้ายกับการใช้งานครั้งแรกมาก ยกเว้นว่าตัวระบุของแอป B จะแตกต่างกัน
เมื่อสร้างการใช้งานทั้งสองแบบแล้ว ตอนนี้คุณสามารถอ้างถึงการใช้งานทั้งสองแบบโดยอ้อมในโค้ดของคุณได้โดยกำหนดประเภทของอ็อบเจ็กต์เป็น ContentManager
ลองดูตัวอย่าง ViewModel ด้านล่าง:
@Observable class VideoListViewModel { var contentManager: ContentManager init(contentManager: ContentManager) { self.contentManager = contentManager } }
ViewModel คาดหวังให้ ContentManager
ชนิดหนึ่งส่งผ่านตัวเริ่มต้น ViewModel สามารถส่งผ่าน ContentManager
ชนิดหนึ่งได้และยังคงทำงานตามที่คาดหวัง ซึ่งหมายความว่า ViewModel สามารถนำมาใช้ซ้ำในเป้าหมายแอปทั้งสองได้
สิ่งสุดท้ายที่ต้องทำคือตรวจสอบให้แน่ใจว่าได้เพิ่ม ContentManager ที่ถูกต้องลงในขั้นตอนการคอมไพล์แหล่งที่มา ในกรณีนี้ แอป A จะถูกส่งต่อ TargetAppAContentMananger
เป็นส่วนหนึ่งของแหล่งที่มา และแอป B จะถูกส่งต่อ TargetAppBContentManager
สิ่งสุดท้ายที่เหลือที่ต้องทำคือตรวจสอบให้แน่ใจว่าแต่ละ App Bundle มีทรัพยากรที่มีชื่อตรงกับตัวระบุที่ใช้โดยแอป วิธีง่ายๆ คือตรวจสอบขั้นตอนการสร้าง Copy Bundle Resources
ของเป้าหมายแอปแต่ละรายการและตรวจสอบให้แน่ใจว่าทรัพยากรได้รับการอ้างอิงโดยผู้จัดการเนื้อหา หากไม่เป็นเช่นนั้น ให้ลากทรัพยากรเหล่านั้นจากโปรเจ็กต์ Xcode ของคุณไปยังขั้นตอนคัดลอกทรัพยากร
การทดสอบนี้ต้องใช้เวลาและความระมัดระวังเล็กน้อย เนื่องจากคุณจะไม่ได้รับข้อผิดพลาดขณะคอมไพล์หากทรัพยากรที่อ้างถึงไม่มีอยู่ในบันเดิล ในระหว่างรันไทม์ คุณจะพบกับข้อผิดพลาด!
วิธีที่ดีในการทำให้การตรวจสอบเป็นแบบอัตโนมัติคือการเขียนการทดสอบยูนิตเพื่อยืนยันว่าทรัพยากรทั้งหมดที่ ContentManager
อ้างอิงถึงนั้นถูกเก็บไว้ในชุดข้อมูล หากการทดสอบล้มเหลวเมื่อเรียกใช้ แสดงว่าคุณมีทรัพยากรที่ขาดหายไปในชุดข้อมูล
หากคุณมาถึงจุดนี้แล้ว คุณน่าจะมีแนวคิดที่ดีว่าจะนำแอปของคุณไปยังแพลตฟอร์มอื่นๆ ของ Apple ได้อย่างไร
เพื่อสรุปโพสต์นี้ ฉันจะฝากเคล็ดลับและทรัพยากรสองสามอย่างที่ฉันแนะนำ:
หากจะเพิ่มการรองรับ Apple Vision ให้กับแอปที่มีอยู่ ก่อนอื่น ให้ย้ายโค้ดของคุณจาก UIKit ไปยัง SwiftUI ให้ได้มากที่สุด เมื่อได้เห็นความเร็วของแอปที่มีอยู่ที่ทำงานบน Vision Pro เมื่อย้ายไปยัง SwiftUI แล้ว แสดงว่าการพึ่งพาสิ่งนี้ได้นั้นมีประโยชน์
อ่านคำแนะนำของ Apple เกี่ยวกับ การนำแอปที่มีอยู่มาสู่ visionOS ซึ่งให้คำแนะนำและข้อเสนอแนะที่มีประโยชน์เกี่ยวกับวิธีการดำเนินการดังกล่าวและการใช้ประโยชน์จากคุณสมบัติของ visionOS
หากคุณกำลังคิดที่จะเริ่มต้นสร้างแอปมัลติแพลตฟอร์มใหม่ด้วยตัวเอง Xcode มีแท็บมัลติแพลตฟอร์ม ซึ่งมีเทมเพลตแอปให้เลือกใช้มากมาย นอกจากนี้ยังมี วิดีโอจาก WWDC 2022 เกี่ยวกับหัวข้อนี้ด้วย
หากคุณต้องการดูตัวอย่างแอปที่ใช้งานบนแพลตฟอร์มต่างๆ ขอแนะนำให้ลองดูแอปส่วนตัวของฉัน Christmas Chill และ Ocean Chill แอปทั้งสองนี้สามารถใช้งานบน tvOS และ Vision Pro ได้ โดยสร้างขึ้นจากโค้ดฐานเดียว (เร็วๆ นี้ tvOS จะรองรับ Ocean Chill!)