WWDC 2019 ááœáẠááŒááºáá±á¬ááºá
á¯á¡ááá·áº á¡á
ááºážá¡áá±ážááᯠáááºáá±á¬ááºááŒá®ážáá±á¬ááºá SwiftUI ááá¯á· áááºáááºáá²áá² á
á±á·á
á±á·áá¯ááŒáá·áºááẠáá¯á¶ážááŒááºáá²á·áááºá áá»áœááºáá±á¬áº áááºážááŸáá·áºá¡áá° á¡áá»áááºáá»á¬ážá
áœá¬ áá¯ááºáá¯á¶ážáá²á·ááŒá®áž ááá¯á¡áá« á¡áá¯á¶ážááŒá¯áá° á¡áá»á¬ážá¡ááŒá¬ážá¡ááœáẠá¡áá¯á¶ážáááºááá¯ááºáá±á¬ áááá·áº application ááᯠá
áááºáá®ááœááºáá±áá«ááŒá®á
á¡á²áá«ááᯠMovieSwiftUI ááá¯á· áá±á«áºááẠ- áá«á á¡áá
áºáá²á· áá¬ááºáá¬ážáá±á¬ááºážááœá±ááᯠááŸá¬ááœá±ááá¯á· á¡ááºááºáá
áºáá¯ááŒá
áºáááᯠáá°ááá¯á·ááᯠá¡áá¯á¶ážááŒá¯ááŒá®áž á
á¯á
ááºážááŸá¯áá
áºáá¯á¡áá±áá²á· á
á¯áá±á¬ááºážáá¬á
áá«ááá¯á·áááºážááá¯ááááá±ážááẠ"Habr" áá á¬áááºáá°á¡á¬ážáá¯á¶ážá¡ááœáẠ- "Habr" áááá¯ááá¯ážááŸááºážáá¯ááºááᯠá¡áá¯á¶ážááŒá¯á áááºááá·áº Skillbox áááºáááºážááœááºá á¬áááºážááœááºážááá·áºá¡áá« 10 áá°áááºáá»áŸá±á¬á·á á»á±ážá
Skillbox á០á¡ááŒá¶ááŒá¯áá¬ážáááº- ááá¬áá±ážá¡áœááºááá¯ááºážáááºáááºáž
"Profession Java developer" .
áá«ááᯠMovieSwiftUI á áá¬áá¯ááºááá¯ááºááá²á
- API ááŸáá·áº á¡ááŒááºá¡ááŸááºá¡áá»áá¯ážáááºáá±á¬ááºááẠ- áá±ááºáá®á¡ááá®áá±ážááŸááºážááá¯ááºážáá®ážáá«ážá áááºážááá¯áá¯ááºáá±á¬ááºáááºá
- áá±á¬ááºážááá¯ááŸá¯áá»á¬ážááœáẠá¡áá®á¡áá»áŸ áá±áá¬ááᯠáááºááŒá®áž JSON ááᯠá¡áá¯á¶ážááŒá¯á Swift áá±á¬áºáááºááá¯á· ááœá²ááŒááºážá
áááºááŒá¬áááºá
áá±á«ááºážááá¯á·ááááºá . - áá±á¬ááºážááá¯áá»ááºááœáẠáááºáá¬ážáá±á¬ áá¯á¶áá»á¬ážááᯠááŒááŒá®áž áááºážááá¯á·ááᯠáááºááŸáºáá¯ááºáá«á
- iOSá iPadOS ááŸáá·áº macOS á¡ááœáẠá€á¡ááºááºááẠဠOS áá»á¬ážá á¡áá¯á¶ážááŒá¯áá°áá»á¬ážá¡ááœáẠá¡áá±á¬ááºážáá¯á¶áž UX ááᯠáá±ážáá±á¬ááºáá«áááºá
- á¡áá¯á¶ážááŒá¯áá°ááẠáá±áá¬áá»á¬ážááᯠáááºáá®ážááá¯ááºááŒá®áž áááºážááá¯á·áááá¯ááºááá¯ááºáá¯ááºááŸááºá á¬áááºážáá»á¬ážááᯠáááºáá®ážááá¯ááºáááºá á¡ááá®áá±ážááŸááºážááẠáá¯á¶ážá áœá²áá°áá±áá¬ááᯠááááºážáááºážááŒá®áž ááŒááºáááºááá°áááºá
- Redux áá¯á¶á á¶ááᯠá¡áá¯á¶ážááŒá¯á ááŒáá·áºááŸá¯ááŸá¯áá»á¬ážá á¡á áááºá¡ááá¯ááºážáá»á¬ážááŸáá·áº áá±á¬áºáááºáá»á¬ážááᯠááŸááºážááŸááºážáááºážáááºáž ááœá²ááŒá¬ážáá¬ážáááºá á€áá±áá¬ááœáẠáá±áá¬á á®ážáááºážááŸá¯ááẠáá áºáááºáááºáááºážááŒá±á¬ááºážááŒá áºáááºá áááºážááᯠá¡ááŒáá·áºá¡á áááºááŸáºáá¯ááºááŒááºážá ááŒááºáááºááá°ááŒááºážááŸáá·áº áááºáá±ážááŒááºážááá¯á· ááŒá¯áá¯ááºááá¯ááºáááºá
- á¡ááá®áá±ážááŸááºážááẠSwiftUIá TabbedViewá SegmentedControlá NavigationViewá Formá Modal á áááºááá¯á·á á¡ááŒá±áᶠá¡á áááºá¡ááá¯ááºážáá»á¬ážááᯠá¡áá¯á¶ážááŒá¯áááºá áááºážááẠá áááºááŒáá¯ááºááŒáá·áºááŸá¯ááŸá¯áá»á¬ážá áááºáááºáá»á¬ážá UI/UX ááá¯á·ááá¯áááºáž áá¶á·ááá¯ážáá±ážáááºá
ááááºáá±á¬á· Animation á áá»á±á¬ááœá±á·áááºá GIF á áááºážáááºážáá±á¬á· áá¯ááºááŸá¯ááºááœá¬ážáááºá
á¡ááºááºááœááºá¡áá¯ááºáá¯ááºááŒááºážááẠáá»áœááºá¯ááºá¡á¬áž á¡ááœá±á·á¡ááŒá¯á¶áá»á¬ážá áœá¬áá±ážáá²á·ááŒá®áž ááŒá¯á¶áá¯á¶áááá«á áááºážááẠáá±á¬ááºážááœááºáá±á¬á¡ááœá±á·á¡ááŒá¯á¶ááŒá áºáááºá áá»áœááºá¯ááºááẠá¡ááŒáá·áºá¡á áá¯ááºáá±á¬ááºááá¯ááºáá±á¬ á¡ááá®áá±ážááŸááºážááᯠáá±ážáá¬ážááá¯ááºáá²á·ááŒá®ážá á ááºáááºáá¬áááœáẠiOS 13 ááœááºáá¬áááºááŸáá·áº áááŒáá¯ááºááẠáááºážááᯠAppStore ááœáẠááŒáŸáá·áºáááºáá«áááºá
Reduxá BindableObject ááŸáá·áº EnvironmentObject
Redux áá²á· ááœá²áá¯ááºáá¬áᬠááŸá
áºááŸá
áºáá±á¬ááºááŸáááŒá®ááá¯á· á¡áá±á¬áºáá±áž áá»áœááºážáá»ááºáá«áááºá á¡áá°ážáááŒáá·áºá áá»áœááºá¯ááºááẠáááºážááᯠfrontend ááœáẠá¡áá¯á¶ážááŒá¯áááºá
SwiftUI á¡ááá®áá±ážááŸááºážáá áºáá¯áááºáá±á¬ááºáááºá¡ááœáẠáá±áá¬á á®ážáááºážááŸá¯áááá¯áá¬á¡ááŒá ẠRedux ááá¯ááœá±ážáá»ááºáá²á·ááŒááºážááᯠáá»áœááºá¯ááºáááºáá±á¬á¡áá«á០áá±á¬ááºááááá«á UIKit á¡ááºááºááœáẠRedux ááá¯á¡áá¯á¶ážááŒá¯áá±á¬á¡áá« á¡áááºáá²áá¯á¶ážá¡ááá¯ááºážáá»á¬ážááẠá ááá¯ážááá¯ááºááŸáá·áºá¡áá¯ááºáá¯ááºááŒá®áž áá±áá¬ááá°ááŒááºážááŸáá·áº ááá°ááŒááºážááŸáá·áº áááºáá¡ááŒááºáá»á¬áž/á¡á áááºá¡ááá¯ááºážáá»á¬ážááᯠáá¯á¶áá±á¬áºááŒááºážáááºááŒá áºáááºá áá«ááá¯áá¯ááºááá¯á·á ReSwift áá²á· ReKotlin ááá¯á¡áá¯á¶ážááŒá¯ááŒá®áž áá»áááºáááºáááááá¬ááœá±áá²á· á á¬ááŒáá·áºááá¯ááºá¡áá»áá¯ážá¡á á¬ážáá áºáá¯ááᯠáááºáá®ážááá«áááºá áá±á¬ááºážáá±á¬ááºážá¡áá¯ááºáá¯ááºáá±ááá·áº code ááœá±áá±á¬áºáá±á¬áºáá»á¬ážáá«áááºá áá¶ááá±á¬ááºážá áœá¬áá²á áááºážááẠ(áááŸááá±áž) open source ááá¯ááºáá±ážáá«á
ááááºážáá±á¬ááºáž! Redux ááá¯á¡áá¯á¶ážááŒá¯áááºá á®á ááºáá¬ážáá«á SwiftUI ááŸáá·áºáááºáááºáá±á¬ áá áºáá¯áááºážáá±á¬á áá¯ážááááºá áá¬á¡áá¬áá»á¬ážááŸá¬ á ááá¯ážááá¯ááºáá»á¬ážá ááŒááºáááºáá»á¬ážááŸáá·áº áá»áŸá±á¬á·áá»áá°áá»á¬ážááŒá áºáááºá @EnvironmentObject á áá»á±ážáá°ážááŒá±á¬áá·áº á ááá¯ážááá¯ááºááŸáá·áº á¡ááŒááºá¡ááŸááºáá¯á¶á·ááŒááºááŸá¯ááᯠSwiftUI á០áá¯á¶ážáááá¯á áá¯ááºáá«áááºá ááá¯á·ááŒá±á¬áá·áº á ááá¯ážááá¯ááºááẠ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)
}
}
áá¯ááºáá±á¬ááºáá»ááºáá áºáá¯ááᯠá áááºááá¯ááºááá¯ááºážá áááºááẠáá®áá¬áá±á¬ááºá áºááᯠá áááºáá¯ááºáá±á¬ááºáááºá áááºážááẠáá»áŸá±á¬ááºááœáŸá¬á áááºááŸáá¡ááŒá±á¡áá±á¡á áá¯ááºáá±á¬ááºáá»ááºáá»á¬ážááᯠá¡áá²ááŒááºáááºááŒá áºáááºá ááá¯á·áá±á¬áẠáá¯ááºáá±á¬ááºáá»ááºá¡áá»áá¯ážá¡á á¬ážááŸáá·áº áá±áá¬ááŸáá·áºá¡áá® ááŒá¯ááŒááºáá¬ážáá±á¬ á¡ááŒá±á¡áá±á¡áá áºááᯠááŒááºáá±ážáá«áááºá
áá±á¬ááºážááŒá®á á ááá¯ážááẠBindableObject ááŒá áºáá±á¬ááŒá±á¬áá·áº PassthroughSubject ááŸáá±ážáá±á¬ willChange ááá¯ááºááá¯ááºááŸá¯ááᯠá¡áá¯á¶ážááŒá¯á áááºážááááºááá¯ážááŒá±á¬ááºážáá²áá±á¬á¡áá« SwiftUI á¡ááŒá±á¬ááºážááŒá¬ážáá«áááºá á¡áááºááŒá±á¬áá·áºááá¯áá±á¬áº BindableObject ááẠPublisherType ááᯠáá±ážáá±á¬ááºááááºááŒá áºááŒá®ážá ááá¯á·áá±á¬áº áááá¯ááá¯áá±á¬ á¡áá±á¬ááºá¡áááºáá±á¬áºááŸá¯ááẠáááºážááᯠá á®áá¶ááá·áºááœá²ááẠáá¬áááºááŸááááºá áá±áá¯áá»á¡á¬ážááŒáá·áºá á€á¡áá¬ááẠApple ááŸá¡ááœááºá¡á áœááºážáááºáá±á¬áááááá¬ááŒá áºáááºá ááá¯á·ááŒá±á¬áá·áºá áá¬ááá·áº rendering áááºáááºááŸá¯ááœááºá 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 ááᯠá¡áá¯á¶ážááŒá¯á áááºááá·áºááŒááºááœááºážááœááºáááᯠáááºáá±á¬ááºááŒáá·áºááŸá¯ááá¯ááºáá«áááºá á¡ááá®áá±ážááŸááºážá¡ááŒá±á¡áá±á០áááºážáááºáá¬áá±á¬ áá¯ááºááá¹áááá»á¬ážááᯠáá»ááºááŒááºá áœá¬ ááŒááºáááºááá°ááŒááºáž ááá¯á·ááá¯áẠááœááºáá»ááºááŒááºážááá¯á·ááŒá±á¬áá·áº á áœááºážáá±á¬ááºááẠááŒá áºáááºáá±ážááŒááºáž áááŸááá«á
áá¯ááºááŸááºááá¯á áá¬ááœáẠááŒá±á¬ááºážáá²áá«á á¡áá±á«áºááá¯ááºááẠáá¯á¶ááᯠááŒá±á¬ááºážáá²áá«áááºá
á¡ááŒááºáá»á¬ážááẠááá¯ááºáá¶áá±á¬áºááŸáá·áº áá»áááºáááºáá¬ážááá·áº á¡áá°á¡áá®ááŒáá·áº áááºážááᯠáá
áºááŒá±á¬ááºážáááºážááŒáá·áº á¡ááŸááºááááºáá¯ááºáá±á¬ááºáááºá á¡áááºá áááºááẠ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!]))
}
}
}
}
á¡áááºáá¯ááºááœáẠIP áá áºáá¯á á®á¡ááœáẠSwiftUI á០.onDelete áá¯ááºáá±á¬ááºáá»ááºááᯠá¡áá¯á¶ážááŒá¯áá±áá«áááºá áááºážááẠá á¬áááºážááŸáá¡áááºážá¡á¬áž áá»ááºááẠáá¯á¶ááŸáẠiOS ááœááºááœá²ááŸá¯ááᯠááŒáááœáá·áºááŒá¯áááºá ááá¯á·ááŒá±á¬áá·áº á¡áá¯á¶ážááŒá¯áá°ááẠáá»ááºáááºááá¯ááºááᯠáááá±á¬á¡áá«á áááºážááẠáááºááá¯ááºáá¬áá¯ááºáá±á¬ááºáá»ááºááᯠá¡á áá»áá¯ážááŒá®áž áá¯ááºááŸááºá á¬áááºážá០áááºááŸá¬ážáááºá
áá±á¬ááºážááŒá®á á á¬áááºážááá¯ááºááá¯ááºááŸá¯ááẠBindableObject á¡ááŒá±á¡áá±á០áááºážáááºáá¬ááŒá®áž EnvironmentObject á¡ááŒá Ạááá·áºááœááºážáá¬ážáá±á¬ááŒá±á¬áá·áº ForEach ááẠááœááºáá»ááºáá¬ážáá±á¬ áá¯ááºááŸááºáá»á¬ážááŸáá·áº áááºá ááºáá±áá±á¬ááŒá±á¬áá·áº SwiftUI ááẠá á¬áááºážááᯠá¡ááºááááºáá¯ááºáá«áááºá
á€áááºááŸá¬ 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 á០á¡ááŒá¶ááŒá¯áá¬ážáááº-
- áááºááœá±á·áááºáááºáž
"ááá¯ááá¯ááºáž Developer PRO" .- á¡áá¯á¶ážáá» á¡áœááºááá¯ááºážáááºáááºáž
"Python Data Analyst" .- ááŸá áºááŸá áºá á¬áááºááœá±á·áááºáááºáž
"áá»áœááºáá±á¬áºá PRO ááẠdeveloper áá áºáá±á¬ááºáá«" .
source: www.habr.com