स्विफ्टयूआई पर एप्लिकेशन विकास। भाग 1: डेटाफ़्लो और रिडक्स
WWDC 2019 में स्टेट ऑफ द यूनियन सत्र में भाग लेने के बाद, मैंने स्विफ्टयूआई में गहराई से उतरने का फैसला किया। मैंने इसके साथ काम करने में बहुत समय बिताया है और अब एक वास्तविक एप्लिकेशन विकसित करना शुरू कर दिया है जो उपयोगकर्ताओं की एक विस्तृत श्रृंखला के लिए उपयोगी हो सकता है।
मैंने इसे MovieSwiftUI कहा - यह नई और पुरानी फिल्मों को खोजने के साथ-साथ उन्हें एक संग्रह में एकत्रित करने के लिए एक ऐप है टीएमडीबी एपीआई. मुझे फिल्में हमेशा से पसंद रही हैं और मैंने इस क्षेत्र में काम करने वाली एक कंपनी भी बनाई है, हालांकि काफी समय पहले। कंपनी को शायद ही अच्छा कहा जा सकता था, लेकिन एप्लिकेशन था!
अनुस्मारक:"हैबर" के सभी पाठकों के लिए - "हैबर" प्रचार कोड का उपयोग करके किसी भी स्किलबॉक्स पाठ्यक्रम में नामांकन करते समय 10 रूबल की छूट।
स्किलबॉक्स अनुशंसा करता है: शैक्षिक ऑनलाइन पाठ्यक्रम "पेशा जावा डेवलपर".
तो MovieSwiftUI क्या कर सकता है?
एपीआई के साथ इंटरैक्ट करता है - लगभग कोई भी आधुनिक एप्लिकेशन ऐसा करता है।
अनुरोधों पर एसिंक्रोनस डेटा लोड करता है और JSON का उपयोग करके स्विफ्ट मॉडल में पार्स करता है कोड करने योग्य.
अनुरोध पर लोड की गई छवियां दिखाता है और उन्हें कैश करता है।
iOS, iPadOS और macOS के लिए यह ऐप इन OSes के उपयोगकर्ताओं के लिए सर्वोत्तम UX प्रदान करता है।
उपयोगकर्ता डेटा उत्पन्न कर सकता है और अपनी स्वयं की मूवी सूची बना सकता है। एप्लिकेशन उपयोगकर्ता डेटा को सहेजता और पुनर्स्थापित करता है।
Redux पैटर्न का उपयोग करके दृश्य, घटकों और मॉडलों को स्पष्ट रूप से अलग किया जाता है। यहां डेटा प्रवाह यूनिडायरेक्शनल है। इसे पूरी तरह से कैश किया जा सकता है, पुनर्स्थापित किया जा सकता है और अधिलेखित किया जा सकता है।
एप्लिकेशन स्विफ्टयूआई, टैब्ड व्यू, सेगमेंटेड कंट्रोल, नेविगेशन व्यू, फॉर्म, मॉडल इत्यादि के बुनियादी घटकों का उपयोग करता है। यह कस्टम व्यू, जेस्चर, यूआई/यूएक्स भी प्रदान करता है।
वास्तव में, एनीमेशन सहज है, GIF थोड़ा झटकेदार निकला
ऐप पर काम करने से मुझे काफी अनुभव मिला और कुल मिलाकर यह एक सकारात्मक अनुभव था। मैं एक पूरी तरह कार्यात्मक एप्लिकेशन लिखने में सक्षम था, सितंबर में मैं इसे सुधारूंगा और आईओएस 13 की रिलीज के साथ-साथ इसे ऐपस्टोर में प्रकाशित करूंगा।
Redux, बाइंडेबलऑब्जेक्ट और एनवायरनमेंटऑब्जेक्ट
मैं लगभग दो वर्षों से Redux के साथ काम कर रहा हूँ, इसलिए मैं इसमें अपेक्षाकृत अच्छी तरह से पारंगत हूँ। विशेष रूप से, मैं इसका उपयोग फ्रंटएंड में करता हूं प्रतिक्रिया वेबसाइट, साथ ही देशी iOS (स्विफ्ट) और Android (कोटलिन) एप्लिकेशन विकसित करने के लिए।
मुझे स्विफ्टयूआई एप्लिकेशन के निर्माण के लिए डेटा प्रवाह आर्किटेक्चर के रूप में रेडक्स को चुनने का कभी अफसोस नहीं हुआ। UIKit ऐप में Redux का उपयोग करते समय सबसे चुनौतीपूर्ण भाग स्टोर के साथ काम करना और डेटा प्राप्त करना और पुनर्प्राप्त करना और इसे अपने विचारों/घटकों में मैप करना है। ऐसा करने के लिए, मुझे कनेक्टर्स की एक प्रकार की लाइब्रेरी बनानी पड़ी (ReSwift और ReKotlin का उपयोग करके)। अच्छा काम करता है, लेकिन काफी कोड है। दुर्भाग्य से, यह (अभी तक) खुला स्रोत नहीं है।
अच्छी खबर! स्विफ्टयूआई के बारे में चिंता करने वाली एकमात्र चीजें - यदि आप Redux का उपयोग करने की योजना बना रहे हैं - स्टोर, राज्य और रिड्यूसर हैं। @EnvironmentObject की बदौलत स्विफ्टयूआई द्वारा स्टोर के साथ इंटरेक्शन का पूरा ध्यान रखा जाता है। तो, स्टोर एक बाइंडेबलऑब्जेक्ट से शुरू होता है।
मैंने एक साधारण स्विफ्ट पैकेज बनाया, स्विफ्टयूआईफ्लक्स, जो Redux का बुनियादी उपयोग प्रदान करता है। मेरे मामले में यह MovieSwiftUI का हिस्सा है। मैं भी चरण-दर-चरण ट्यूटोरियल लिखा, जो आपको इस घटक का उपयोग करने में मदद करेगा।
यह कैसे काम करता है?
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)
}
}
हर बार जब आप कोई कार्रवाई शुरू करते हैं, तो आप गियरबॉक्स को सक्रिय करते हैं। यह एप्लिकेशन की वर्तमान स्थिति के अनुसार कार्यों का मूल्यांकन करेगा। इसके बाद यह क्रिया प्रकार और डेटा के अनुसार एक नई संशोधित स्थिति लौटाएगा।
खैर, चूंकि स्टोर एक बाइंडेबलऑब्जेक्ट है, इसलिए जब इसका मूल्य पासथ्रूसब्जेक्ट द्वारा प्रदान की गई विलचेंज प्रॉपर्टी का उपयोग करके बदलता है तो यह स्विफ्टयूआई को सूचित करेगा। ऐसा इसलिए है क्योंकि बाइंडेबलऑब्जेक्ट को एक प्रकाशक प्रकार प्रदान करना होगा, लेकिन प्रोटोकॉल कार्यान्वयन इसे प्रबंधित करने के लिए ज़िम्मेदार है। कुल मिलाकर, यह Apple का एक बहुत शक्तिशाली टूल है। तदनुसार, अगले रेंडरिंग चक्र में, स्विफ्टयूआई राज्य परिवर्तन के अनुसार दृश्यों के मुख्य भाग को प्रस्तुत करने में मदद करेगा।
दरअसल, यह स्विफ्टयूआई का सार और जादू है। अब, किसी राज्य की सदस्यता लेने वाले किसी भी दृश्य में, राज्य से क्या डेटा प्राप्त हुआ है और क्या बदल गया है, उसके अनुसार दृश्य प्रस्तुत किया जाएगा।
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())
}
}
जब एप्लिकेशन प्रारंभ होता है तो स्टोर को एक एनवायरनमेंटऑब्जेक्ट के रूप में इंजेक्ट किया जाता है और फिर @EnvironmentObject का उपयोग करके किसी भी दृश्य में पहुंच योग्य होता है। कोई प्रदर्शन दंड नहीं है क्योंकि व्युत्पन्न संपत्तियों को एप्लिकेशन स्थिति से तुरंत पुनर्प्राप्त या गणना की जाती है।
यदि मूवी का पोस्टर बदलता है तो उपरोक्त कोड छवि को बदल देता है।
और ये दरअसल सिर्फ एक लाइन से किया जाता है, जिसकी मदद से विचारों को राज्य से जोड़ा जाता है. यदि आपने iOS या यहां तक कि ReSwift के साथ काम किया है कनेक्ट रिएक्ट के साथ, आप स्विफ्टयूआई के जादू को समझेंगे।
अब आप कार्रवाई को सक्रिय करने और नई स्थिति प्रकाशित करने का प्रयास कर सकते हैं। यहाँ एक अधिक जटिल उदाहरण है.
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 कार्रवाई का उपयोग कर रहा हूं। यह सूची में पंक्ति को हटाने के लिए सामान्य iOS स्वाइप प्रदर्शित करने की अनुमति देता है। इसलिए जब उपयोगकर्ता डिलीट बटन को छूता है, तो यह संबंधित कार्रवाई को ट्रिगर करता है और फिल्म को सूची से हटा देता है।
खैर, चूंकि सूची संपत्ति बाइंडेबलऑब्जेक्ट स्थिति से ली गई है और इसे एनवायरनमेंटऑब्जेक्ट के रूप में इंजेक्ट किया गया है, स्विफ्टयूआई सूची को अपडेट करता है क्योंकि ForEach फिल्मों की गणना की गई संपत्ति से जुड़ा हुआ है।
यहां मूवीस्टेट रिड्यूसर का हिस्सा है:
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
}
जब आप कोई कार्रवाई भेजते हैं और एक नई स्थिति लौटाते हैं, तो रिड्यूसर निष्पादित होता है, जैसा कि ऊपर बताया गया है।
मैं अभी विस्तार में नहीं जाऊंगा - स्विफ्टयूआई वास्तव में कैसे जानता है कि क्या प्रदर्शित करना है। इसे और गहराई से समझना सार्थक है डेटा प्रवाह पर WWDC सत्र देखें स्विफ्टयूआई में। इसमें यह भी विस्तार से बताया गया है कि क्यों और कब उपयोग करना है राज्य, @बाइंडिंग, ऑब्जेक्टबाइंडिंग और एनवायरनमेंटऑब्जेक्ट।