หลังจากเข้าร่วมเซสชั่น State of the Union ที่ WWDC 2019 ฉันตัดสินใจเจาะลึกเกี่ยวกับ SwiftUI ฉันใช้เวลาทำงานกับมันมามาก และตอนนี้ได้เริ่มพัฒนาแอปพลิเคชันจริงที่เป็นประโยชน์กับผู้ใช้ในวงกว้างแล้ว
ฉันเรียกมันว่า MovieSwiftUI - นี่คือแอปสำหรับค้นหาภาพยนตร์ใหม่และเก่ารวมถึงรวบรวมไว้ในคอลเลกชันโดยใช้
เราเตือนคุณ: สำหรับผู้อ่าน "Habr" ทุกคน - ส่วนลด 10 rubles เมื่อลงทะเบียนในหลักสูตร Skillbox ใด ๆ โดยใช้รหัสส่งเสริมการขาย "Habr"
Skillbox แนะนำ: หลักสูตรออนไลน์เพื่อการศึกษา
"นักพัฒนาจาวามืออาชีพ" .
MovieSwiftUI สามารถทำอะไรได้บ้าง?
- โต้ตอบกับ API - แอปพลิเคชันสมัยใหม่เกือบทุกตัวทำเช่นนี้
- โหลดข้อมูลแบบอะซิงโครนัสตามคำขอและแยกวิเคราะห์ JSON ลงในโมเดล Swift โดยใช้
เข้ารหัสได้ . - แสดงภาพที่โหลดตามคำขอและแคชไว้
- แอพสำหรับ iOS, iPadOS และ macOS นี้มอบ UX ที่ดีที่สุดสำหรับผู้ใช้ OS เหล่านี้
- ผู้ใช้สามารถสร้างข้อมูลและสร้างรายการภาพยนตร์ของตนเองได้ แอปพลิเคชันบันทึกและกู้คืนข้อมูลผู้ใช้
- มุมมอง ส่วนประกอบ และแบบจำลองจะถูกแยกออกจากกันอย่างชัดเจนโดยใช้รูปแบบ Redux การไหลของข้อมูลที่นี่เป็นแบบทิศทางเดียว สามารถแคช กู้คืน และเขียนทับได้อย่างสมบูรณ์
- แอปพลิเคชันใช้ส่วนประกอบพื้นฐานของ SwiftUI, TabbedView, SegmentedControl, NavigationView, Form, Modal ฯลฯ นอกจากนี้ยังมีมุมมอง ท่าทาง UI/UX แบบกำหนดเองอีกด้วย
ที่จริงแล้วแอนิเมชั่นนั้นราบรื่น GIF กลับกลายเป็นกระตุกเล็กน้อย
การทำงานกับแอปทำให้ฉันได้รับประสบการณ์มากมาย และโดยรวมแล้วถือเป็นประสบการณ์ที่ดี ฉันสามารถเขียนแอปพลิเคชันที่มีฟังก์ชันการทำงานเต็มรูปแบบได้ ในเดือนกันยายนฉันจะปรับปรุงและเผยแพร่ใน AppStore พร้อมกับการเปิดตัว iOS 13
Redux, BindableObject และ EnvironmentObject
ฉันทำงานกับ Redux มาประมาณสองปีแล้ว ดังนั้นฉันจึงค่อนข้างเชี่ยวชาญเรื่องนี้ โดยเฉพาะผมใช้มันในส่วนหน้าสำหรับ
ฉันไม่เคยเสียใจที่เลือก Redux เป็นสถาปัตยกรรมการไหลของข้อมูลสำหรับการสร้างแอปพลิเคชัน SwiftUI ส่วนที่ท้าทายที่สุดเมื่อใช้ Redux ในแอป UIKit คือการทำงานร่วมกับร้านค้าและรับและดึงข้อมูลและแมปกับมุมมอง/ส่วนประกอบของคุณ ในการทำเช่นนี้ ฉันต้องสร้างไลบรารีตัวเชื่อมต่อชนิดหนึ่ง (โดยใช้ ReSwift และ ReKotlin) ใช้งานได้ดีแต่โค้ดค่อนข้างเยอะ น่าเสียดายที่มันยังไม่ใช่โอเพ่นซอร์ส (ยัง)
ข่าวดี! สิ่งเดียวที่ต้องกังวลกับ SwiftUI - หากคุณวางแผนที่จะใช้ Redux - คือร้านค้า รัฐ และตัวลดขนาด การโต้ตอบกับร้านค้าได้รับการดูแลอย่างสมบูรณ์โดย SwiftUI ต้องขอบคุณ @EnvironmentObject ดังนั้น store เริ่มต้นด้วย BindableObject
ฉันสร้างแพ็คเกจ Swift ง่ายๆ
มันทำงานอย่างไร
final public class Store<State: FluxState>: BindableObject {
public let willChange = PassthroughSubject<Void, Never>()
private(set) public var state: State
private func _dispatch(action: Action) {
willChange.send()
state = reducer(state, action)
}
}
ทุกครั้งที่คุณกระตุ้นการทำงาน คุณจะเปิดใช้งานกระปุกเกียร์ โดยจะประเมินการดำเนินการตามสถานะปัจจุบันของแอปพลิเคชัน จากนั้นจะส่งคืนสถานะที่แก้ไขใหม่ตามประเภทการดำเนินการและข้อมูล
เนื่องจาก store เป็น BindableObject จึงจะแจ้งเตือน SwiftUI เมื่อค่าของมันเปลี่ยนแปลงโดยใช้คุณสมบัติ willChange ที่จัดทำโดย PassthroughSubject เนื่องจาก BindableObject ต้องจัดเตรียม PublisherType แต่การนำโปรโตคอลไปใช้มีหน้าที่ในการจัดการ โดยรวมแล้วนี่เป็นเครื่องมือที่ทรงพลังมากจาก Apple ดังนั้นในรอบการเรนเดอร์ถัดไป SwiftUI จะช่วยเรนเดอร์เนื้อหาของมุมมองตามการเปลี่ยนแปลงสถานะ
จริงๆ แล้วนี่คือหัวใจและความมหัศจรรย์ของ SwiftUI ในตอนนี้ ในมุมมองใดก็ตามที่สมัครรับข้อมูลสถานะ มุมมองจะถูกเรนเดอร์ตามข้อมูลที่ได้รับจากรัฐและสิ่งที่เปลี่ยนแปลงไป
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let controller = UIHostingController(rootView: HomeView().environmentObject(store))
window.rootViewController = controller
self.window = window
window.makeKeyAndVisible()
}
}
}
struct CustomListCoverRow : View {
@EnvironmentObject var store: Store<AppState>
let movieId: Int
var movie: Movie! {
return store.state.moviesState.movies[movieId]
}
var body: some View {
HStack(alignment: .center, spacing: 0) {
Image(movie.poster)
}.listRowInsets(EdgeInsets())
}
}
Store จะถูกแทรกเป็น EnvironmentObject เมื่อแอปพลิเคชันเริ่มทำงาน และสามารถเข้าถึงได้จากมุมมองใดก็ได้โดยใช้ @EnvironmentObject ไม่มีการลงโทษด้านประสิทธิภาพเนื่องจากคุณสมบัติที่ได้รับจะถูกดึงหรือคำนวณอย่างรวดเร็วจากสถานะแอปพลิเคชัน
โค้ดด้านบนจะเปลี่ยนรูปภาพหากโปสเตอร์ภาพยนตร์เปลี่ยนแปลง
และจริงๆ แล้วสิ่งนี้ทำได้โดยใช้เพียงบรรทัดเดียว โดยมีมุมมองที่เชื่อมโยงกับรัฐช่วย หากคุณเคยทำงานกับ ReSwift บน iOS หรือแม้แต่
ตอนนี้คุณสามารถลองเปิดใช้งานการดำเนินการและเผยแพร่สถานะใหม่ได้ นี่เป็นตัวอย่างที่ซับซ้อนมากขึ้น
struct CustomListDetail : View {
@EnvironmentObject var store: Store<AppState>
let listId: Int
var list: CustomList {
store.state.moviesState.customLists[listId]!
}
var movies: [Int] {
list.movies.sortedMoviesIds(by: .byReleaseDate, state: store.state)
}
var body: some View {
List {
ForEach(movies) { movie in
NavigationLink(destination: MovieDetail(movieId: movie).environmentObject(self.store)) {
MovieRow(movieId: movie, displayListImage: false)
}
}.onDelete { (index) in
self.store.dispatch(action: MoviesActions.RemoveMovieFromCustomList(list: self.listId, movie: self.movies[index.first!]))
}
}
}
}
ในโค้ดด้านบน ฉันใช้การดำเนินการ .onDelete จาก SwiftUI สำหรับแต่ละ IP ซึ่งจะทำให้แถวในรายการแสดงการปัด iOS ปกติเพื่อลบ ดังนั้นเมื่อผู้ใช้แตะปุ่มลบ ระบบจะเรียกใช้การกระทำที่เกี่ยวข้องและลบภาพยนตร์ออกจากรายการ
เนื่องจากคุณสมบัติของรายการนั้นได้มาจากสถานะ BindableObject และถูกแทรกเป็น EnvironmentObject ดังนั้น SwiftUI จึงอัปเดตรายการเนื่องจาก ForEach เชื่อมโยงกับคุณสมบัติที่คำนวณของภาพยนตร์
นี่คือส่วนหนึ่งของตัวลด MoviesState:
func moviesStateReducer(state: MoviesState, action: Action) -> MoviesState {
var state = state
switch action {
// other actions.
case let action as MoviesActions.AddMovieToCustomList:
state.customLists[action.list]?.movies.append(action.movie)
case let action as MoviesActions.RemoveMovieFromCustomList:
state.customLists[action.list]?.movies.removeAll{ $0 == action.movie }
default:
break
}
return state
}
ตัวลดจะถูกดำเนินการเมื่อคุณส่งการดำเนินการและส่งคืนสถานะใหม่ตามที่ระบุไว้ข้างต้น
ฉันจะยังไม่ลงรายละเอียด - SwiftUI รู้ได้อย่างไรว่าจะแสดงอะไร เพื่อให้เข้าใจสิ่งนี้อย่างลึกซึ้งยิ่งขึ้นมันก็คุ้มค่า
Skillbox แนะนำ:
- หลักสูตรภาคปฏิบัติ
"นักพัฒนามือถือ PRO" .- สมัครเรียนออนไลน์
“นักวิเคราะห์ข้อมูลหลาม” .- หลักสูตรภาคปฏิบัติสองปี
"ฉันเป็นนักพัฒนาเว็บ PRO" .
ที่มา: will.com