บทความและข่าวสาร | Seven Peaks Insights

MVVM ใน iOS Swift: การใช้ model view ของ SwiftUI

 

Image-from-iOS-18-scaled-1

 

“การใช้งานยังคงเหมือนเดิม แต่เราไม่จำเป็นต้องเก็บ reference อีกต่อไป”

User interface ที่ดีเป็นสิ่งสำคัญในการช่วยเปลี่ยนผู้มีโอกาสเป็นลูกค้าให้กลายมาเป็นผู้ซื้อ เนื่องจากช่วยให้การโต้ตอบระหว่างผู้ใช้กับเว็บไซต์หรือแอปพลิเคชันของธุรกิจทำได้ง่ายดายยิ่งขึ้น

ในซีรีส์ “Swift UI Design” นี้ เราจะพูดถึงสาเหตุที่เราทำให้เราจำเป็นต้องมีแอนิเมชันในแอปพลิเคชันของเรา และแอนิเมชันในแอปพลิเคชัน front-end โดยเราจะมาพูดถึงพื้นฐานของภาพรวมของแอนิเมชันด้วย UI ก่อนที่เราจะพาไปยังหัวข้อขั้นสูงที่นำเสนอโดย Ton และ Andrei นักพัฒนา iOS อาวุโสของเรา ซึ่งจะพูดคุยเพิ่มเติมเกี่ยวกับ model view ของ SwiftUI

หากเราต้องการให้ผู้ใช้ชอบแอปฯ หรือซอฟต์แวร์ของเรา เราควรออกแบบสองสิ่งนี้ให้ทำงานเหมือนเป็นคนที่น่าคบหาคนหนึ่ง โดยต้องมีทั้งความน่าเคารพ เอื้อเฟื้อเผื่อแผ่ และยินดีที่จะช่วยเหลือ – อลัน คูเปอร์

เราจำเป็นต้องให้ข้อเสนอแนะแก่ผู้ใช้เกี่ยวกับสิ่งที่เกิดขึ้นในแอปพลิเคชัน เช่น การเลื่อนหรือแถบแสดงระยะเวลาในการโหลด โดยผู้ใช้จะได้รับข้อเสนอแนะเกี่ยวกับสิ่งที่เกิดขึ้นด้วย

โดยทั่วไปแล้วในระหว่างที่โปรแกรมกำลังดำเนินการทำอะไรสักอย่างอยู่นั้น ผู้ใช้ทั่วไปต้องอดทนรอ และการรวมแอนิเมชันที่น่าสนใจจะทำให้โปรแกรมดังกล่าวเป็นที่น่าจดจำยิ่งขึ้น หลักการนี้ไม่เพียงแต่ใช้ได้กับแอปพลิเคชัน iOS เท่านั้น แต่ยังใช้กับแอปพลิเคชัน front-end อื่นๆ เช่น เว็บแอปพลิเคชัน แอปพลิเคชันบน Android และแอปพลิเคชันบน Safari เป็นต้น

เฟรมเวิร์กของ front-end ส่วนใหญ่มีคุณสมบัติพื้นฐานที่เกี่ยวข้องกับแอนิเมชัน ซึ่งเกี่ยวข้องกับการเปลี่ยนแปลงในด้านการมองเห็น เช่น ตำแหน่ง มุม สี และอื่นๆ

SwiftUI นำเสนอแนวทางที่ตรงไปตรงมาและเชื่อถือได้ในการรวมแอนิเมชันเข้ากับโปรเจกต์ของคุณ โดยคุณสามารถใช้การ transform ได้หลากหลาย เช่น การ translate การปรับขนาด การหมุน หรือการเปลี่ยนสีพื้นหลังโดยใช้โค้ดเพียงแค่บรรทัดเดียว

การเปลี่ยนแปลงหรือรูปร่างที่มองเห็นได้ก็คือฟังก์ชันการจับเวลา ซึ่งจะควบคุมวิธีการแสดงสถานะสุดท้ายของ object บนหน้าจอ

มีฟังก์ชันจับเวลาหลักหลายฟังก์ชัน เช่น linear ซึ่งรักษาความเร็วคงที่ตลอดระยะเวลาที่แอนิเมชันมีการเคลื่อนไหว อีกทางเลือกหนึ่งคือ การค่อยๆ เข้า ซึ่งเริ่มช้าๆ แล้วเพิ่มความเร็วขึ้น และการค่อยๆ ออก ซึ่งช้าลงในตอนท้าย การรวมกันของความง่ายในการเข้าและการออกเรียกว่าความง่ายในการเอาออก นอกจากนี้ยังสามารถใช้ฟังก์ชัน spring timingได้ ซึ่งรวมเอาปัจจัยต่างๆ หรือพารามิเตอร์ เช่น ความเร็วการหน่วงหรือแรงเสียดทาน

แอนิเมชันทุกรูปแบบสามารถนำไปใช้ได้โดยใช้โปรโตคอลเดียวที่เรียกว่า “Animatable” มาดูกันว่าโปรโตคอลนี้เกี่ยวข้องกับอะไรในส่วนต่อไปนี้

“Animatable” เป็นโปรโตคอลในเฟรมเวิร์ก SwiftUI ที่มาพร้อมกับ “AnimatableData” ซึ่งเกี่ยวข้องกับการคำนวณเวกเตอร์ ส่วน vector arithmetic เป็นส่วนย่อยของ additive arithmetic ที่พบในไลบรารีมาตรฐาน

ด้วยการรวมการดำเนินการทางคณิตศาสตร์ object สามารถเพิ่มขีดความสามารถในการดำเนินการขั้นพื้นฐาน เช่น การบวกและการลบ นอกจากนี้ การขยายเมตริกดั้งเดิมทำให้ object สามารถรองรับการคูณได้ โดยพื้นฐานแล้ว ข้อมูลประเภทใดก็ตามที่สอดคล้องกับ vector arithmetic จะมีความสามารถในการปรับขนาดให้มากขึ้นหรือน้อยลงได้

เพื่อแสดงให้เห็นถึงการทำงานของ vector arithmetic เรามาเริ่มตรวจสอบการเปลี่ยนความทึบของสัญลักษณ์กัน

เราเริ่มต้นด้วยการกำหนดค่าสองค่าเพื่อระบุจุดเริ่มต้นและจุดสิ้นสุดของแอนิเมชัน ด้วยการใช้ vector arithmetic ทำให้เราสามารถปรับขนาดช่วงขึ้นและลงตามได้ตามความจำเป็น และแสดงสถานะสุดท้ายบนหน้าจอได้ในที่สุด

แม้ว่าเฟรมเวิร์กจะให้ข้อมูลเริ่มต้นกับเราเป็นจำนวนมาก แต่ในการใช้งานในชีวิตจริงนั้น เรามักต้องการแอนิเมชันที่มีความซับซ้อนมากขึ้น ซึ่งก็จำเป็นต้องปรับแต่งออกมาให้ดีด้วยความชำนาญเฉพาะตัว

ตัวอย่างเช่น เราจะแนะนำการประกอบอย่างง่ายเพื่อแสดงให้เห็นว่าแอนิเมชันแบบกำหนดเองสามารถปรับปรุงประสบการณ์ผู้ใช้ใน intuitive UI ให้ดีขึ้นได้อย่างไร

เรากำหนดข้อมูลแอนิเมชันตามจำนวนของ H ซึ่งทำให้เข้ากันได้กับแอนิเมชันโดยธรรมชาติ เนื่องจากต้องทำให้สอดคล้องกับ vector arithmetic ตามที่อธิบายไว้ก่อนหน้านี้

ด้วยการรวมเข้ากับฟังก์ชันการจับเวลา เราท้าทายสถานะสูงสุดของ VISTA นอกจากนี้ ผมได้รวมภาพอื่นที่แสดงช่วงเวลาและระยะเวลาของแอนิเมชัน และนั่นทำให้คุณรับรู้แอนิเมชันได้ชัดเจนยิ่งขึ้น 

Affine transformation เป็นแนวคิดที่หลายคนรู้จัก ไม่ว่าคุณจะเป็นนักพัฒนามากประสบการณ์ผู้มีความเชี่ยวชาญในภาษาโปรแกรม เป็นนักเรียนที่เคยพบการเปลี่ยนแปลงนี้ในระหว่างที่กำลังเรียนมัธยมหรือมหาวิทยาลัย หรือเพียงแค่มีความเข้าใจพื้นฐานเกี่ยวกับหัวข้อนี้

Object ของผมมีการใช้เอฟเฟกต์เป็นจำนวนมาก ซึ่งสามารถควบคุมได้ด้วยปุ่มเดียวเพื่อเริ่มแอนิเมชัน อย่างไรก็ตาม สิ่งนี้ไม่ได้เป็นแอนิเมชันตามมาตรฐาน ดังนั้น หากคุณเป็นนักพัฒนา iOS คุณอาจทราบว่าการแปลง CG affine เป็นวิธีที่เหมาะสมในการสร้างภาพเคลื่อนไหวด้วย UI kit

เราจะเปลี่ยนไปใช้โปรโตคอลอื่นที่เรียกว่า geometry effects ซึ่งเป็นไปตามมาตรฐานแอนิเมชันของ UI ด้วย geometry effect เราสามารถใช้การแปลงการฉายภาพได้อย่างง่ายดายและรวมเข้ากับการแปลง CG affine พร้อมกับ translation value

เราสามารถใช้การ transform เมตริกอย่างง่าย เช่น การ translate การปรับขนาด หรือการหมุน ซึ่งสามารถรวมกันเพื่อสร้างการ transform ที่ต้องการ โดยทั่วไป สถานะสุดท้ายของ object บนหน้าจอสามารถทำได้โดยการคูณสถานะดั้งเดิมด้วย formation matrix ที่รวมตัวกันอย่างหนาแน่น

คุณสามารถใช้การ transform หลายรายการในลำดับที่ต้องการได้ เช่น translate หลายครั้ง ตามด้วยการปรับขนาด หมุน ปรับขนาด และ translate อีกครั้ง ซึ่งการแปลงผลลัพธ์จะแตกต่างออกไปหากคุณสลับลำดับการหมุนและการปรับขนาด หรือการ translate และการหมุน

ลำดับที่คุณรวบรวมการแปลงอาจมีผลกระทบอย่างมากต่อสถานะสุดท้ายของ object ตัวอย่างเช่น หากคุณใช้การแปลตามด้วยมาตราส่วน สถานะสุดท้ายจะแตกต่างจากที่ได้รับจากการใช้มาตราส่วนตามด้วยการแปล ในบางกรณี คุณอาจโชคดีและได้รับสถานะสุดท้ายเดียวกันโดยการใช้การแปลและการหมุนเวียน แต่วิธีที่คุณได้รับจะแตกต่างกันขึ้นอยู่กับลำดับที่คุณใช้ในการแปลงนั่นเอง

Protocol ที่ได้รับการแนะนำรวมเอามาตรฐานแอนิเมชันเข้ากับบรรทัดฐานการดู และทำให้เกิดโปรโตคอลแอนิเมชันขึ้นมา

เมื่อเรารวมคำศัพท์โปรโตคอลสองคำ เราจะได้องค์ประกอบของโปรโตคอล โดยเราจะไปยังชุดสัญลักษณ์อื่น ซึ่งจะแสดงให้เห็นว่าแอนิเมชันและรูปแบบการดูสามารถทำงานร่วมกันได้อย่างมีประสิทธิภาพมากขนาดไหน

ในบริบทนี้ ผมจะกำหนดให้แอนิเมชันประเภทหนึ่งเป็นตัวบ่งชี้ความคืบหน้า เนื่องจากมันสอดคล้องกับบรรทัดฐานของมุมมอง นอกจากนี้ เรายังระบุประเภทแอนิเมชันที่แตกต่างกันสองประเภท – ประเภทหนึ่งสำหรับการเคลื่อนที่เป็นวงกลม และอีกประเภทหนึ่งสำหรับการเลื่อนไปมา

ผมได้ออกแบบไวต์บอร์ดเพื่อให้แน่ใจว่าทุกสิ่งเป็นไปตามมาตรฐานแอนิเมชัน ซึ่งจำเป็นสำหรับบรรทัดฐานแอนิเมชัน ดังนั้น ผมจึงทำการแยกไวต์บอร์ดออกเป็นสองส่วน ส่วนแรกสำหรับส่วนที่มีการเคลื่อนไหว ซึ่งเป็นไปตามมาตรฐานของแอนิเมชัน และอีกส่วนสำหรับเนื้อหาของมุมมอง ซึ่งแสดงถึงความคืบหน้าให้เราได้ติดตาม

เราสามารถรับ input ที่ไม่มีการควบคุมโดยใช้ sap ที่ขึ้นอยู่กับสไตล์ จากนั้นเราจะสามารถคลิปเพื่อแก้ไขอินพุตประเภทใดก็ได้ ตราบใดที่ยังเป็นมุมมองของ view model ใน SwiftUI ซึ่งหมายความว่าสามารถเป็นได้ทั้งรูปภาพ ข้อความ สี SAP หรือมุมมองที่กำหนดเองใดๆ ก็ได้ และเรายังสามารถแก้ไขได้ด้วยวิธีเดียวกัน

เริ่มแรก เราเลือก SF su casa และเลือกใช้แอนิเมชันแบบวงกลม จากนั้นเราก็ใช้แอนิเมชันเดียวกันนี้กับรูปภาพ ข้อความ และสี่เหลี่ยมผืนผ้าอื่น ด้วยการรวมสี่เหลี่ยมผืนผ้าเข้ากับส่วนอื่นๆ เท่านี้เราก็สามารถสร้างเอฟเฟกต์การหมุนที่ทำงานได้อย่างราบรื่นแล้ว

ไม่ว่าคุณจะต้องการใช้การเอียงกับแกน X หรือ Y หรือใช้เอฟเฟกต์พิเศษตามการแปลงเมตริก คุณก็สามารถใช้เอฟเฟกต์เหล่านี้ได้

สุดท้ายนี้ หากคุณต้องการใช้แอนิเมชันแบบกำหนดเองที่สามารถใช้กับมุมมองใดก็ได้ หรืออย่างน้อยชุดของมุมมองหรือประเภทของ object ก็ให้พิจารณาใช้ motif ที่เคลื่อนไหวได้ โดยโค้ดสำหรับสิ่งนี้สามารถหาได้ในลิงก์ GitHub

เริ่มต้นด้วยการพูดคุยเกี่ยวกับ Property Viper เพราะเราจะสร้าง property wrapper สำหรับ Swift UI และ MVVM ใน iOS Swift

Property หรือ property wrapper ได้มีการเริ่มใช้งานใน UI และ Viper เวอร์ชัน 5.1 โดยภาษาของ Apple เป็นคุณสมบัติที่มีค่าและ property wrapper ซึ่งทำหน้าที่เป็นเลเยอร์ของการแยกระหว่างรหัสที่จัดการกับปัญหาที่เกิดขึ้น

ขั้นตอนแรกในการสร้าง property wrapper คือการกำหนดคีย์เวิร์ดของ wrapper ที่ด้านบน ตามด้วยชื่อคุณสมบัติและค่าต่างๆ ซึ่งค่าของ wrapper ได้รับการปกป้องโดยค่า property value ในตัวอย่างนี้ เราจะสร้าง property wrapper ที่เรียกว่า “project” และใช้เพื่อกำหนดเงื่อนไขสำหรับคุณสมบัติ “หมายเลขโทรศัพท์” เช่น ตรวจสอบให้แน่ใจว่ามีอักขระ 10 ตัวและตามด้วยคุณสมบัติ “project” คุณสามารถสมมติว่า property wrapper เป็นแกนที่ช่วยให้คุณจัดการคุณสมบัติด้วยวิธีใดวิธีหนึ่ง

State เป็น property wrapper ที่จัดทำโดย Apple ซึ่งทำให้เราสามารถยืนยันคุณสมบัติที่ชื่อว่า body ในมุมมองของเรา โดยเราควรใช้ state เฉพาะในมุมมองและตั้งค่าเป็นส่วนตัวเพื่อให้แน่ใจว่ามีการห่อหุ้มเอาไว้

เมื่อพิจารณาอย่างง่ายๆ แล้ว property wrapper ช่วยให้เราสามารถยืนยันค่าที่มาจากที่อื่นได้ และ wrapper ถัดไปที่เราจะพูดถึงคือ EnvironmentObject

Environment Object property ช่วยให้เราสร้างมุมมองที่สามารถแชร์ข้อมูลได้ ซึ่งมีประโยชน์มากเมื่อคุณต้องการแชร์ object ให้กับผู้ใช้ในหลายๆ ส่วนของแอปฯ ของคุณ ตัวอย่างเช่น สำหรับการตั้งค่าแอปฯ หรือผู้ใช้ซึ่ง Environment Object นั้นเป็นอะไรที่ยอดเยี่ยมมาก

เราสร้างคลาสการตั้งค่าผู้ใช้ที่สอดคล้องกับโปรโตคอล ObservableObject เพื่อกำหนดคุณสมบัติ จากนั้นจึงสร้างอินสแตนซ์ของคลาสนี้ภายในแอปฯ ของเรา

มาดำดิ่งสู่โปรเจกต์โดยใช้ MVVM ใน iOS Swift กันดีกว่า โดยผมใช้ EnvironmentObject เพื่อเข้าถึงคุณสมบัติของคลาสการตั้งค่า ObservableObject และสร้างอินสแตนซ์ของมันที่นี่ ต่อมาผมก็ส่งต่อไปยังโปรเจกต์อื่นๆ ในบางส่วนของแอปฯ โดยใช้ EnvironmentObject

ลำดับต่อไป ผมจะแสดงมุมมองการตั้งค่าผู้ใช้ โดยในการตั้งค่าผู้ใช้และมุมมองการตั้งค่าโปรเจกต์จะแสดงข้อความ ‘Hello, Dawn’ ซึ่งจะปรากฏขึ้นแบบนี้

แม้ว่าวิธีการนี้อาจดูตรงไปตรงมา แต่ก็มีปัญหาที่อาจเกิดขึ้นกับประสิทธิภาพและการจัดการหน่วยความจำที่ต้องพิจารณา แต่ไม่ต้องกังวลไป เพราะผมจะอธิบายให้คุณฟังเพื่อหลีกเลี่ยงข้อผิดพลาดใดๆ โปรดทราบว่าการแก้ไขปัญหาเหล่านี้อาจต้องใช้เวลาและความพยายามเพิ่มขึ้นในส่วนของคุณ

เราใช้ view model ของ SwiftUI นี้ในโปรเจกต์ที่ใช้งานจริง และผมสามารถพูดได้อย่างมั่นใจว่านี่เป็นแนวทางที่เชื่อถือได้ในการเริ่มสร้างแอปพลิเคชันของคุณ

ผมจะหารือเกี่ยวกับแนวทางที่เราใช้เพื่อให้บรรลุเป้าหมายนี้ ซึ่งมีสามหัวข้อหลักที่ผมอยากจะอธิบายให้คุณเข้าใจ ประการแรก มันเกี่ยวกับ published object และ observable object โดยทั่วไป ประการที่สอง ผมจะพูดถึง navigation link ซึ่งเราใช้กันอย่างกว้างขวาง

ขั้นแรก เรามาพูดถึง observable object กันก่อน โดยทั่วไปแล้ว observable object ก็คือ object ที่ส่งสตรีมของ object ที่จะเปลี่ยนเหตุการณ์ ซึ่งจะส่งสัญญาณการอัปเดตไปยังมุมมองที่ใช้

ความแตกต่างระหว่าง observable object และ state object ก็คือ state object จะได้รับการจัดการโดย Swift UI ซึ่งสร้างขึ้นเมื่อมีการนำเสนอมุมมอง และถูกจัดเก็บcache ทำให้ไม่จำเป็นต้องจัดการหน่วยความจำแต่อย่างใด

ในตัวอย่างนี้ เราสามารถเห็นตัวจับเวลาอย่างง่ายที่เริ่มทำงานทุกๆ วินาที และไม่มี published property ดังนั้นจึงเป็นเพียง normal property เมื่อ observable object เปลี่ยนคุณสมบัติใดๆ ที่ถูกทำเครื่องหมายว่าเผยแพร่แล้ว object นั้นจะเปลี่ยนไป 

ความท้าทายอย่างแรกที่คุณอาจพบเมื่อสร้างสถาปัตยกรรมของคุณเองคือ ต้องแน่ใจว่าแอปพลิเคชันของคุณทำงานได้อย่างมีประสิทธิภาพและไม่ประสบปัญหาหน่วยความจำรั่วหรือมีปัญหาเกี่ยวกับหน่วยความจำอื่นๆ ซึ่งหมายความว่าคุณต้องแน่ใจว่าตัวกล้องของคุณมีน้ำหนักเบาที่สุดเท่าที่จะเป็นไปได้

ดังนั้น หากคุณจำเป็นต้องทำการคำนวณที่ใช้เวลานานกว่าปกติ โปรดทราบว่าสิ่งเหล่านี้สามารถส่งสัญญาณอยู่เบื้องหลังได้หลายครั้ง แม้ว่ามุมมองจะไม่แสดงบนหน้าจอในขณะนี้ก็ตาม ซึ่งอาจทำให้เกิดปัญหาด้านประสิทธิภาพและการรั่วไหลของหน่วยความจำที่อาจเกิดขึ้น นอกจากนี้ เมื่อทำงานกับ observable object คุณสมบัติใดๆ ที่ทำเครื่องหมายเป็น `@Published` จะทำให้มุมมองอัปเดตทุกครั้งที่คุณสมบัติเปลี่ยนแปลง ตัวอย่างเช่น ในข้อมูลโค้ดนี้ คุณสมบัติ “รูปภาพ” จะได้รับการอัปเดตทุกครั้งที่สร้างมุมมอง

หากคุณเลือกที่จะทำตามแนวทางนี้ ผมจะสาธิตวิธีแก้ปัญหาบางอย่างที่ค้นพบเพื่อจัดการกับปัญหาเหล่านี้ อย่างไรก็ตาม ก่อนที่เราจะดำเนินการต่อ เราจะหารือเกี่ยวกับ navigation link ซึ่งใช้กันทั่วไปใน SwiftUI และอาจนำไปสู่ปัญหาที่คล้ายกันได้

ปัญหาอย่างหนึ่งในการสร้าง navigation link ก็คือคุณต้องระบุมุมมองปลายทาง และปลายทางนี้เป็นส่วนหนึ่งของมุมมองของคุณ ซึ่งหมายความว่าทุกครั้งที่มีการเรียกเนื้อหาของมุมมองของคุณ แต่ละมุมมองปลายทางเหล่านี้ (เช่น child ที่หนึ่ง child ที่สอง child ที่สาม) จะถูกสร้างขึ้นด้วย และแต่ละมุมมองจะสร้างมุมมอง child ของตัวเอง

ปัญหาหนึ่งที่คุณอาจพบคือคุณต้องระบุปลายทาง และปลายทางนี้จะได้รับการประเมินทุกครั้งที่เรียกคอนเทนต์ ซึ่งหมายความว่าแต่ละมุมมองปลายทาง (child หนึ่ง child สอง child สาม) จะถูกสร้างขึ้น นอกจากนี้ แต่ละรายการยังสร้างมุมมองย่อยซึ่งจะสร้าง view model ของ SwiftUI ดังนั้น เมื่อมีการเรียกใช้คอนเทนต์ มุมมองย่อยทั้งสามจะถูกสร้างขึ้นและ view model ก็จะถูกสร้างขึ้นมาใหม่

ในตัวอย่างนี้ เมื่อเราคลิกที่ lazy view จะไม่มีอะไรเกิดขึ้น แต่เมื่อเราไปที่ child ที่ 1 มันจะสร้าง child ขึ้นมา จากนั้น child นั้นจะสร้าง child อีกคน ส่งผลให้มีการสร้าง view model หลายรายการ ซึ่งสิ่งนี้เกิดขึ้นเนื่องจากวิธีสร้างมุมมองนั่นเอง

แทนที่จะสร้าง child view เราสามารถใช้ lazy view wrapper ได้ นี่เป็นการปิดการสร้างโปรเจกต์ของเรา และเมื่อ SwiftUI แสดงผล มุมมองจะถูกสร้างขึ้นในเวลานั้น เป็น wrapper ง่ายๆ ที่สามารถช่วยหลีกเลี่ยงการสร้างมุมมองที่ไม่จำเป็นได้

อีกวิธีหนึ่งคือการสร้าง wrapper สำหรับ lazy navigation link ด้วยวิธีนี้ เมื่อใช้ lazy navigation link เริ่มต้น มุมมองย่อยทั้งหมดจะถูกสร้างขึ้น ณ จุดนั้น wrapper จะทำการปิดเพื่อสร้างมุมมองและ SwiftUI ก็จะสร้างมุมมองเมื่อจำเป็นต้องแสดงผลเท่านั้น

ความท้าทายอีกประการหนึ่งที่เราพบคือการจัดการการนำทางในลำดับที่ลึกขึ้น เมื่อผู้ใช้คลิก “ถัดไป” เราก็แค่เปลี่ยน binding property ให้เป็น “true” เพื่อให้บรรลุเป้าหมายนี้ เราจะสร้างมุมมองและส่งพร็อปเพอร์ตี้เพื่อแสดงมุมมองถัดไป

เมื่อจัดการการนำทางเชิงลึก เรามักพบกับความท้าทายอีกประการหนึ่ง ซึ่งการคลิก “ถัดไป” จะเปลี่ยน binding property เป็น “true” ดังนั้นเราจึงสร้างมุมมองและส่งคุณสมบัติเพื่อแสดงต่อไป อย่างไรก็ตาม ปัญหาคือ navigation link ในมุมมองเดียวเปลี่ยนค่านั้นกลับเป็น “false” ทุกครั้งที่หายไป ดังนั้น ทันทีที่เราคลิก “ถัดไป” และเปลี่ยนคุณสมบัติ บางอย่างที่อยู่ภายในจะพังทันที

เพื่อแก้ไขปัญหานี้ เราพบวิธีแก้ปัญหาง่ายๆ ในการจัดการกับมัน โดยเราทำการสร้างมุมมองทั้งหมดล่วงหน้าและจัดการภายใน จากนั้นเราเริ่มต้นด้วยมุมมองซ้อนกันบนหน้าจอแรก ซึ่งจะแสดงมุมมองหลักของเราและ navigation link ไปยังมุมมองถัดไป ซึ่งมุมมองที่ตามมาทั้งหมดยังมี state property เดียวกันที่จะแสดงในส่วนต่อไป

เราพบว่ามีหลายกรณีที่แอปพลิเคชันของเรามีโฟลว์แบบนี้ ดังนั้นเราจึงสร้างมุมมอง rotor navigation แบบกำหนดเองที่จำลองกระบวนการนี้ขึ้นมา โดยมุมมองนี้มีการไล่เลียงระดับที่ลดหลั่นกันมา ทำให้คุณสามารถปิดตัว view builder ของคุณได้ ดังที่แสดงในตัวอย่างนี้

เราย้ายคุณสมบัติ `isPresented` ไว้ใน `RouterNavigationView` ของเรา ซึ่งเราสร้าง navigation view ด้วยตัวสร้างมุมมองที่ต้องการ และนำเสนอคุณสมบัติ `isPresented` ที่เราส่งต่อไปยังมุมมองของเรา ซึ่งจำเป็นต้องเปลี่ยนคุณสมบัติเพื่อไปยังขั้นตอนถัดไป ดังนั้น หน้าจอแรกของ `RouterNavigationView` ของเราจึงมีปุ่มอยู่ตรงนั้นทันทีที่เราคลิกถัดไปก็จะเปลี่ยนคุณสมบัตินี้

เมื่อเราคลิกถัดไป คุณสมบัติจะถูกส่งต่อไปยัง disclosure ซึ่งเป็นมุมมองย่อยของเรา ด้วยวิธีนี้ เราไม่ได้เปลี่ยนเพียงแค่คุณสมบัติที่นำเสนอ แต่ยังรวมถึงคุณสมบัติอื่นๆ ที่เราต้องการแก้ไขด้วย

ในการสร้าง observable object ด้วยพารามิเตอร์ สิ่งสำคัญคือต้องพิจารณาการแคช object เมื่อใช้ state object SwiftUI จะจัดการให้คุณโดยอัตโนมัติ อย่างไรก็ตาม เมื่อทำงานกับ observable object พร้อมพารามิเตอร์ เราจำเป็นต้องจัดการแคชด้วยตนเอง

วิธีหนึ่งในการทำเช่นนี้ก็คือการ singleton object ที่เก็บข้อมูลที่แคชไว้ จากนั้นเราสามารถส่ง object เป็นพารามิเตอร์ไปยัง observable object ของเรา เมื่อเริ่ม observable object จะสามารถตรวจสอบข้อมูลที่เก็บไว้และใช้ได้ถ้าหากมี หากไม่มีก็สามารถดึงข้อมูลและจัดเก็บไว้ในวัตถุแคชเพื่อใช้ในอนาคตได้ด้วย

อีกวิธีหนึ่งคือการใช้ตัวเริ่มต้นแบบกำหนดเองสำหรับ observable object ซึ่งใช้พารามิเตอร์ที่จำเป็น จากนั้นเราสามารถจัดเก็บพารามิเตอร์เหล่านี้เป็นคุณสมบัติของ observable object และใช้เพื่อดึงข้อมูลเมื่อจำเป็น วิธีการนี้ช่วยให้เราสามารถสร้างอินสแตนซ์หลายรายการของ observable object ด้วยค่าพารามิเตอร์ที่แตกต่างกัน

โดยรวมแล้ว การสร้าง observable object ด้วยพารามิเตอร์จำเป็นต้องพิจารณาอย่างรอบคอบเกี่ยวกับกลยุทธ์การแคชและการดึงข้อมูล สิ่งสำคัญคือต้องแน่ใจว่าต้องสามารถดึงข้อมูลได้อย่างมีประสิทธิภาพและเมื่อจำเป็นเท่านั้น เพื่อหลีกเลี่ยงค่าใช้จ่ายที่ไม่จำเป็น

ในตัวอย่างนี้ เป้าหมายคือการดึงข้อมูลบางส่วนโดยใช้ API จากฐานข้อมูล หรือแหล่งข้อมูลอื่นๆ โดยการระบุ ID

เมื่อคุณใช้ observable object มันจะไม่มีตัวเลือกอีกต่อไป สิ่งนี้นำเราไปสู่ปัญหาก่อนหน้านี้ซึ่ง view model ทั้งหมดของคุณจะถูกสร้างขึ้นทุกครั้งผ่าน view model ของ SwiftUI เมื่อคุณใช้ navigation view หรือสร้างอย่างน้อยที่สุด มันจะสร้างโมเดลใหม่และเมื่อมีบางอย่างเปลี่ยนแปลงภายใน object นี้ view model จะอัปเดตและเรียกคอนเทนต์นี้ เมื่อร่างกายนี้ถูกเรียกทุกครั้ง จะไม่แคช detail view นี่เป็นคำอธิบายง่ายๆ ว่า detail view ไม่ถูกแคชไว้อย่างไร

เมื่อโมเดลใหม่ถูกสร้างขึ้นและมีการเปลี่ยนแปลงบางอย่างภายในโมเดลมุมมอง body จะถูกเรียกครั้งแล้วครั้งเล่า ซึ่งทำให้มุมมองรายละเอียดไม่ถูกแคช ซึ่งเป็นปัญหาง่ายๆ ที่พบได้บ่อย

มุมมองมี view model ของตัวเองที่รับพารามิเตอร์จากภายนอก

เพื่อแก้ไขปัญหานี้ คุณสามารถลองแคชค่าก่อนหน้าที่ไม่มีรายละเอียดใดๆ อยู่ ซึ่งวิธีหนึ่งในการบรรลุเป้าหมายนี้ก็คือการสร้าง “view model provider

หากคุณมี table view ที่มีมุมมองย่อยที่แตกต่างกัน 100 มุมมอง ทุกครั้งที่คุณพิมพ์และทำการเปลี่ยนแปลง object จะถูกสร้างขึ้นใหม่ทุกครั้งโดยไม่มี view model provider อย่างไรก็ตาม หากคุณใช้ view model provider และเปิดเอาต์พุต คุณจะเห็นว่ามีการใช้ object ที่แคชไว้ทุกครั้งที่คุณพิมพ์

เมื่อคุณได้ทำการพัฒนาสถาปัตยกรรมใน SwiftUI คุณจะไม่สามารถใช้ความรู้ของสถาปัตยกรรม UIKit ในโปรเจกต์ของคุณได้

สิ่งสำคัญคือต้องคำนึงถึงประเด็นทั้งหมดที่กล่าวถึงและสร้างสถาปัตยกรรมที่คำนึงถึงข้อมูลนี้เมื่อทำงานกับโปรเจกต์ SwiftUI ของคุณเอง

ในขณะที่ยังคงมีการสำรวจวิธีที่ดีที่สุดในการนำ MVVM ไปใช้งานใน iOS Swift หรือสถาปัตยกรรมอื่นๆ ใน view model ของ SwiftUI อย่างต่อเนื่อง ปรากฏว่าการสร้างมุมมองใน SwiftUI นั้นง่ายกว่าใน UI kit มาก อย่างไรก็ตาม อาจเป็นเรื่องท้าทายเมื่อทำงานกับโมเดลข้อมูล และสิ่งสำคัญคือต้องตระหนักถึงกระบวนการพื้นฐานทั้งหมดที่ view model ของ SwiftUI ดำเนินการ

ดังนั้นจึงจำเป็นอย่างยิ่งที่จะต้องพิจารณาปัจจัยเหล่านี้ทั้งหมดและออกแบบสถาปัตยกรรมของคุณตามนั้น เพื่อให้แน่ใจว่าประสบการณ์การพัฒนาใน SwiftUI จะราบรื่นและมีประสิทธิภาพ

อยากพัฒนาแอป iOS ใช่ไหม?
มาพูดคุยกับทีมของเราเพื่อดูว่าเราสามารถช่วยคุณได้อย่างไร
ปรึกษาเหล่า iOS developer ของเรา