บทเรียนการเขียน SwiftUI สำหรับการพัฒนาแอปพลิเคชัน iOS หรือ iPhone ดึงรูปภาพจาก JSON มาแสดงด้วย SDWebImageSwiftUI เบื้องต้น
บทเรียนก่อนหน้า SwiftUI:
- เขียนแอป iPhone: การใช้งาน SwiftUI
- เขียนแอป iPhone ด้วย SwiftUI มาดู List และ Navigation
- เขียนแอป iPhone การเรียก JSON Web API ด้วย SwiftUI
สำหรับนักพัฒนาแอปพลิเคชันของเรา เวลาดึงข้อมูลจาก JSON หรือเว็บเซอร์วิส (Web Services) หลายครั้งที่เราจำเป็นต้อง Feed เนื้อหามาแสดงเหมือนในบทความก่อนหน้า เขียนแอป iPhone การเรียก JSON Web API ด้วย SwiftUI ซึ่งเราไม่ได้พูดถึง ส่วนประกอบหลักคือรูปภาพ หรือวิดีโอ การเขียนแอปพลิเคชันจำเป็นต้องทำฟังก์ชันสำหรับการเก็บแคช Cache รูปภาพที่โหลดมาจาก JSON ออนไลน์
ทางเลือกสำหรับสาย SwiftUI หรือ iOS Developer นั้นก็คงจะต้องเลือก Library ยอดฮิต อย่าง SDWebImage ที่มีฟังก์ชันจัดการ Cache Image ที่โหลดจาก JSON Web Services ได้ซึ่งตอนนี้ SDWebImage เองก็มี Library ของ SwiftUI โดยเฉพาะคือ SDWebImageSwiftUI
git repo: https://github.com/SDWebImage/SDWebImageSwiftUI
มาเริ่มเขียนโปรแกรมกันดีกว่า:
ตัวอย่างนี้เราจะใช้ Web Services ของผมเองที่ทำเล่น https://enet5-7f9f6.firebaseio.com/Books.json
เป็นหน้าหนังสือจากเว็บไซต์ https://www.creativethailand.org/magazine ซึ่งโครงสร้าง JSON เป็นดังนี้:
[ { "id" :0, "title": "Life After Covid-19", "thumbnail": "https://www.creativethailand.org/admin/public/uploads/images/2563/06/cover/MagazineCover_185.jpg" }, { "id" :1, "title": "Creativity Must Go On", "thumbnail": "https://www.creativethailand.org/admin/public/uploads/images/2563/05/cover/MagazineCover_184.jpg" }, { "id" :2, "title": "Living with COVID-19", "thumbnail": "https://www.creativethailand.org/admin/public/uploads/images/2563/04/cover/MagazineCover_182.jpg" }, { "id" :3, "title": "Look Isan Now", "thumbnail": "https://www.creativethailand.org/admin/public/uploads/images/2563/03/cover/MagazineCover_180.jpg" }, { "id" :4, "title": "VERTICAL LIVING: WHY DO WE ALWAYS LIVE HIGHER?", "thumbnail": "https://www.creativethailand.org/admin/public/uploads/images/2563/02/cover/MagazineCover_181.jpg" }, { "id" :5, "title": "THE FUTURE OF WORK IS NOW", "thumbnail": "https://www.creativethailand.org/admin/public/uploads/images/2563/01/cover/MagazineCover_177.jpg" } ]
ทำการ New Single View Application บน XCode ขึ้นมาเลย:
ตั้งชื่อแนวๆ เช็คว่าเป็น SwiftUI หรือเปล่า
ทีนี้เราต้องเพิ่ม Library เข้าไปยัง Project โดย XCode 11 ขึ้นมา(ตอนนี้ใช้ 11.5) จะมีวิธีการที่เรียกว่า App integration โดยโหลด Package Dependencies มาที่แอปเราได้เลยตรงๆ ให้ทำวิธีนี้ (ส่วนใครใช้ cocoapods สบายง่ายๆ)
คลิกที่ XCode Project หลังจากนั้นสังเกตที่ตำแหน่ง General เลื่อนไปที่ Frameworks, Libraries, and Embedded Content หาเครื่องหมาย +
คลิกแล้วจะพบหน้าต่างดังภาพด้านล่างนี้ครับ ให้คลิกที่ Add Other เลือก Add Package Dependency…
เมื่อคลิกแล้วจะมีช่องกรอก Package Dependencies ให้เราใส่ URL: https://github.com/SDWebImage/SDWebImageSwiftUI.git
ลงไปบนช่อง
เมือ่เสร็จแล้วกด Next รอระบบประมวลผลประมาณนึง จะมีหน้าต่างบอกให้เลือก ผมเลือก Version เป็น Up to Next Major เลยอัพเด็ตดี
เมื่อ Library พร้อมใช้มันจะขึ้นหน้าจอนี้
ตรวจสอบดูอีกรอบ ถ้าเป็นแบบภาพข้าวล่างคือโอเค ส่วนของ XCode Project เราพร้อมใช้งานแล้ว
ไปที่ ContentView.swift เขียนโปรแกรมกันให้ประกาศตัวแปร import บน Header เพิ่มคือ:
import SDWebImageSwiftUI
สร้าง Class ใหม่ขึ้นมาดังนี้:
class datas : ObservableObject{ @Published var jsonData = [datatype]() }
หลังจากนั้นกำหนด ฟังก์ชัน Struct ใหม่เป็นการกำหนด DataType ให้กับ Key JSON:
struct datatype : Identifiable, Decodable { var id : Int var thumbnail : String var title : String }
เราจะเก็บค่า Key ของ id, title และ thumbnail ตาม Data Type ของมัน ไปที่ struct ContentView() เขียนคำสั่งต่อไปนี้:
struct ContentView: View { @ObservedObject var getData = datas() var body: some View { //Text("Hello, World!") NavigationView { List(getData.jsonData){ i in ListRow(name: i.title, thumbnail: i.thumbnail) } .navigationBarTitle("Books") } } }
เราจะทำการสร้าง NavigationView โดยไปเรียก ListRow เป็นเหมือน Class นึงที่จัดการ Binding ข้อมูลของการส่ง title และ Thumbnail มาแสดงผลแถวแต่ละแถว ผ่าน List ที่ดึงจาก jsonData ใน Class ชื่อ datas ที่เราสร้างขึ้น ดังนั้นเราต้องไปเพิ่ม คำสั่งใน Class ของ datas()
init(){ let session = URLSession(configuration: .default) session.dataTask(with: URL(string:"https://enet5-7f9f6.firebaseio.com/Books.json")!){ (data, _, _) in do{ let fetch = try JSONDecoder().decode([datatype].self, from: data!) DispatchQueue.main.async { self.jsonData = fetch } }catch{ print(error.localizedDescription) } }.resume() }
เป็นคำสั่งในการเรียก API ของ URL ที่เป็น Webservices หรือ JSON มาทำการ Decode JSON และ Fetch ข้อมูลเก็บในตัวแปล jsonData
เพิ่ม struct หรือฟังก์ชันในการจัดการ แถว หรือ Row ในรายการของ List กันหน่อยชื่อ ListRow() ตามที่เราตั้งชื่อใน NavigationView ข้างต้น ผมจะใช้ HStack เพื่อเรียงการแสดงผลเป็นแนว Horizontal รูปทางซ้าย ชื่อหนังสือทางขวา
struct ListRow : View { var name : String var thumbnail : String var body : some View{ HStack{ AnimatedImage(url: URL(string: thumbnail)) .resizable() .transition(.fade) .scaledToFit() .frame(width: 100, height: 200) Text(name).fontWeight(.regular).padding(.leading, 10) } } }
อ่ะมาดูกันว่าไฟล์ ContentView.swft ทั้งหมดเป็นยังไง:
// // ContentView.swift // SDWebSwiftUI // // Created by Banyapon Poolsawas on 3/6/2563 BE. // Copyright © 2563 Banyapon Poolsawas. All rights reserved. // import SwiftUI import SDWebImageSwiftUI struct ContentView: View { //3 @ObservedObject var getData = datas() var body: some View { //Text("Hello, World!") //4 NavigationView { List(getData.jsonData){ i in ListRow(name: i.title, thumbnail: i.thumbnail) } .navigationBarTitle("Books") } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } //1 class datas : ObservableObject{ @Published var jsonData = [datatype]() //5 init(){ let session = URLSession(configuration: .default) session.dataTask(with: URL(string:"https://enet5-7f9f6.firebaseio.com/Books.json")!){ (data, _, _) in do{ let fetch = try JSONDecoder().decode([datatype].self, from: data!) DispatchQueue.main.async { self.jsonData = fetch } }catch{ print(error.localizedDescription) } }.resume() } } //2 struct datatype : Identifiable, Decodable { var id : Int var thumbnail : String var title : String } //6 struct ListRow : View { var name : String var thumbnail : String var body : some View{ HStack{ AnimatedImage(url: URL(string: thumbnail)) .resizable() .transition(.fade) .scaledToFit() .frame(width: 100, height: 200) Text(name).fontWeight(.regular).padding(.leading, 10) } } }
ทดสอบโดยการรันผ่าน Simulator กันดูหน่อย
ลองเอาไปฝึกดูนะครับ สรุปคือ SwiftUI เป็นภาษาที่ยังไม่ค่อย Smooth และหาแนวทางตัวเองไม่ชัดเท่าไร อาจจะต้องรออีกสักพักถึงจะเสถียรครับ