የመተግበሪያ ልማት በSwiftUI ላይ። ክፍል 1: የውሂብ ፍሰት እና Redux

የመተግበሪያ ልማት በSwiftUI ላይ። ክፍል 1: የውሂብ ፍሰት እና Redux

በWWDC 2019 የሕብረቱ ክፍለ ጊዜ ከተሳተፍኩ በኋላ፣ ወደ SwiftUI በጥልቀት ለመጥለቅ ወሰንኩ። ከእሱ ጋር በመስራት ብዙ ጊዜ አሳልፌያለሁ እና አሁን ለብዙ ተጠቃሚዎች ጠቃሚ ሊሆን የሚችል እውነተኛ መተግበሪያ ማዘጋጀት ጀመርኩ።

MovieSwiftUI ብዬ ጠራሁት - ይህ አዲስ እና አሮጌ ፊልሞችን ለመፈለግ እንዲሁም በስብስብ ውስጥ ለመሰብሰብ መተግበሪያ ነው TMDB ኤፒአይ. ምንም እንኳን ከረጅም ጊዜ በፊት ፊልሞችን ሁልጊዜ እወዳለሁ እና በዚህ መስክ ውስጥ የሚሰራ ኩባንያ ፈጠርኩኝ. ኩባንያው አሪፍ ተብሎ ሊጠራ አይችልም, ነገር ግን ማመልከቻው ነበር!

እኛ እናስታውስዎታለን- ለሁሉም የ "ሀብር" አንባቢዎች - የ "Habr" የማስተዋወቂያ ኮድን በመጠቀም በማንኛውም የ Skillbox ኮርስ ውስጥ ሲመዘገቡ የ 10 ሩብልስ ቅናሽ.

Skillbox ይመክራል፡ ትምህርታዊ የመስመር ላይ ኮርስ "ሙያ ጃቫ ገንቢ".

ስለዚህ MovieSwiftUI ምን ማድረግ ይችላል?

  • ከኤፒአይ ጋር ይገናኛል - ማንኛውም ዘመናዊ መተግበሪያ ማለት ይቻላል ይህን ያደርጋል።
  • በጥያቄዎች ላይ ያልተመሳሰለ ውሂብን ይጭናል እና JSONን በመጠቀም ወደ ስዊፍት ሞዴል ይተነዋል። ተቀባይነት ያለው.
  • በተጠየቀ ጊዜ የተጫኑ ምስሎችን ያሳያል እና ይሸፍኗቸዋል።
  • ይህ መተግበሪያ የiOS፣ iPadOS እና macOS ለእነዚህ ስርዓተ ክወናዎች ተጠቃሚዎች ምርጡን UX ያቀርባል።
  • ተጠቃሚው ውሂብ ማመንጨት እና የራሳቸውን የፊልም ዝርዝሮች መፍጠር ይችላል። አፕሊኬሽኑ የተጠቃሚውን ውሂብ ያስቀምጣል እና ወደነበረበት ይመልሳል።
  • እይታዎች፣ ክፍሎች እና ሞዴሎች የ Redux ስርዓተ-ጥለትን በመጠቀም በግልፅ ተለያይተዋል። እዚህ ያለው የውሂብ ፍሰት ባለአንድ አቅጣጫ ነው። ሙሉ በሙሉ መሸጎጫ፣ ወደነበረበት መመለስ እና ሊፃፍ ይችላል።
  • አፕሊኬሽኑ የSwiftUI፣ TabbedView፣ SegmentedControl፣ NavigationView፣ Form፣ Modal፣ ወዘተ መሰረታዊ ክፍሎችን ይጠቀማል። እንዲሁም ብጁ እይታዎችን፣ የእጅ ምልክቶችን፣ UI/UXን ያቀርባል።

የመተግበሪያ ልማት በSwiftUI ላይ። ክፍል 1: የውሂብ ፍሰት እና Redux
እንደ እውነቱ ከሆነ፣ እነማው ለስላሳ ነው፣ ጂአይኤፍ ትንሽ ገርሞታል።

በመተግበሪያው ላይ መሥራት ብዙ ልምድ ሰጠኝ እና በአጠቃላይ አዎንታዊ ተሞክሮ ነበር። ሙሉ በሙሉ የሚሰራ አፕሊኬሽን መፃፍ ችያለሁ፣ በሴፕቴምበር ላይ አሻሽየዋለሁ እና በAppStore ውስጥ አሳትመዋለሁ፣ በተመሳሳይ ጊዜ iOS 13 ከተለቀቀ በኋላ።

Redux፣ BindableObject እና EnvironmentObject

የመተግበሪያ ልማት በSwiftUI ላይ። ክፍል 1: የውሂብ ፍሰት እና Redux

ከሬዱክስ ጋር ለሁለት ዓመታት ያህል እየሠራሁ ነው፣ ስለዚህ በአንፃራዊነት በደንብ ጠንቅቄአለሁ። በተለይም እኔ በግንባሩ ውስጥ እጠቀማለሁ ምላሽ ድር ጣቢያ፣ እንዲሁም ቤተኛ iOS (ስዊፍት) እና አንድሮይድ (ኮትሊን) መተግበሪያዎችን ለማዘጋጀት።

የስዊፍት UI መተግበሪያን ለመገንባት ሬዱክስን እንደ የውሂብ ፍሰት አርክቴክቸር በመምረጥ ተቆጭቼ አላውቅም። በ UIKit መተግበሪያ ውስጥ Reduxን ሲጠቀሙ በጣም ፈታኝ የሆኑት ክፍሎች ከመደብሩ ጋር እየሰሩ እና ውሂብን በማግኘት እና በማውጣት እና በእርስዎ እይታዎች/አካላት ላይ በማሳየት ላይ ናቸው። ይህንን ለማድረግ, የግንኙነት አይነት (ReSwift እና ReKotlin በመጠቀም) አይነት ቤተ-መጽሐፍት መፍጠር ነበረብኝ. በደንብ ይሰራል፣ ግን በጣም ብዙ ኮድ። እንደ አለመታደል ሆኖ (ገና) ክፍት ምንጭ አይደለም።

መልካም ዜና! ከSwiftUI ጋር መጨነቅ ያለብዎት ብቸኛው ነገር - Redux ለመጠቀም ካሰቡ - መደብሮች፣ ግዛቶች እና መቀነሻዎች ናቸው። ከሱቁ ጋር ያለው መስተጋብር ሙሉ በሙሉ በSwiftUI እንክብካቤ ይደረግለታል @EnvironmentObject እናመሰግናለን። ስለዚህ፣ ማከማቻ በBindableObject ይጀምራል።

ቀላል የስዊፍት ጥቅል ፈጠርኩ ፣ SwiftUIFlux, ይህም 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)
    }
}

አንድ ድርጊት ባነሳህ ቁጥር የማርሽ ሳጥኑን ታነቃለህ። አሁን ባለው የመተግበሪያው ሁኔታ መሰረት ድርጊቶችን ይገመግማል. ከዚያም በድርጊት አይነት እና መረጃ መሰረት አዲስ የተሻሻለ ሁኔታ ይመልሳል.

ደህና፣ መደብሩ BindableObject ስለሆነ፣ በPassthroughSubject የቀረበ ንብረትን በመጠቀም እሴቱ ሲቀየር ለSwiftUI ያሳውቀዋል። ይህ የሆነበት ምክንያት BindableObject የአታሚ አይነት ማቅረብ ስላለበት ነው፣ ነገር ግን የፕሮቶኮሉ ትግበራ እሱን የማስተዳደር ሃላፊነት አለበት። በአጠቃላይ ይህ ከ 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())
    }
}

አፕሊኬሽኑ ሲጀምር ማከማቻው እንደ አካባቢ ነገር ይወጋዋል እና @EnvironmentObjectን በመጠቀም በማንኛውም እይታ ተደራሽ ይሆናል። የአፈጻጸም ቅጣት የለም ምክንያቱም የተገኙ ንብረቶች በፍጥነት ከመተግበሪያው ሁኔታ ስለሚመለሱ ወይም ስለሚሰላ።

የፊልም ፖስተር ከተቀየረ ከላይ ያለው ኮድ ምስሉን ይለውጠዋል.

እና ይሄ በእውነቱ በአንድ መስመር ብቻ ይከናወናል, በእነሱ እርዳታ እይታዎች ከግዛቱ ጋር የተገናኙ ናቸው. ከReSwift ጋር በ iOS ላይ ከሰሩ ወይም እንዲያውም ማገናኘት ከ React ጋር፣ የSwiftUIን አስማት ይገነዘባሉ።

አሁን ድርጊቱን ለማግበር እና አዲሱን ግዛት ለማተም መሞከር ይችላሉ. የበለጠ ውስብስብ ምሳሌ ይኸውና.

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 እየተጠቀምኩ ነው። ይህ በዝርዝሩ ውስጥ ያለው ረድፍ ለመሰረዝ የተለመደውን የ iOS ሸርተቴ እንዲያሳይ ያስችለዋል። ስለዚህ ተጠቃሚው የመሰረዝ አዝራሩን ሲነካው ተጓዳኝ እርምጃውን ያስነሳል እና ፊልሙን ከዝርዝሩ ያስወግዳል.

እንግዲህ፣ የዝርዝር ንብረቱ ከBindableObject ግዛት የተገኘ እና እንደ ኢንቫይሮንመንትኦብጀክት የተወጋ በመሆኑ፣ ስዊፍት UI ዝርዝሩን ያዘምናል ምክንያቱም 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
}

ከዚህ በላይ እንደተገለጸው አንድን ድርጊት ሲልኩ እና አዲስ ሁኔታ ሲመልሱ ቅነሳው ይከናወናል።

እስካሁን በዝርዝር አልገባም - SwiftUI እንዴት ማሳየት እንዳለበት ያውቃል። ይህንን በጥልቀት ለመረዳት, ጠቃሚ ነው በውሂብ ፍሰት ላይ የ WWDC ክፍለ ጊዜን ይመልከቱ በ SwiftUI ውስጥ። እንዲሁም ለምን እና መቼ እንደሚጠቀሙ በዝርዝር ያብራራል ሁኔታ፣ @Binding ፣ ObjectBinding እና EnvironmentObject።

Skillbox ይመክራል፡

ምንጭ: hab.com

አስተያየት ያክሉ