SwiftUI рдореЗрдВ рд╕реНрдЯреЛрд░ рдСрдмреНрдЬреЗрдХреНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдореЙрдбрд▓рд┐рдВрдЧ рд╕реНрдЯреЗрдЯ

рдЗрд╕ рд╣рдлреНрддреЗ рдореИрдВ рд╕реНрд╡рд┐рдлреНрдЯрдпреВрдЖрдИ рдореЗрдВ рдПрдХ рдбреЗрдЯрд╛ рд▓реЗрдпрд░ рдореЙрдбрд▓рд┐рдВрдЧ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВред рдореИрдВрдиреЗ рдЕрдкрдиреЗ рдкрд╣рд▓реЗ рд╣реА рдЖрд╡реЗрджрди рдкрд░ рдХрд╛рдо рдкреВрд░рд╛ рдХрд░ рд▓рд┐рдпрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рдореИрдВ рдХреЗрд╡рд▓ рд╕реНрд╡рд┐рдлреНрдЯрдпреВрдЖрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдмрдирд╛рддрд╛ рд╣реВрдВред рдЕрдм рдореИрдВ рдЙрди рд╕реНрдЯреЛрд░ рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдореЙрдбрд▓ рдкрд░рдд рдмрдирд╛рдиреЗ рдХрд╛ рддрд░реАрдХрд╛ рд╕рд╛рдЭрд╛ рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ рдЬреЛ рдореИрдВрдиреЗ рдиреИрдкрдмреЙрдЯ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рддреЗ рд╕рдордп рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдереЗред

рд╕реНрдЯреЛрд░ рдСрдмреНрдЬреЗрдХреНрдЯ


рд╕реНрдЯреЛрд░ рдСрдмреНрдЬреЗрдХреНрдЯ рд░рд╛рдЬреНрдп рдХреЛ рдмрдирд╛рдП рд░рдЦрдиреЗ рдФрд░ рдЗрд╕реЗ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╛рд░реНрд░рд╡рд╛рдИ рдкреНрд░рджрд╛рди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реИрдВред рдЖрдкрдХреЗ рдкрд╛рд╕ рдЬрд┐рддрдиреА рдЬрд╝рд░реВрд░рдд рд╣реЛ рдЙрддрдиреЗ рд╕реНрдЯреЛрд░ рдСрдмреНрдЬреЗрдХреНрдЯ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ, рдЕрдзрд┐рдорд╛рдирддрдГ рд╡реЗ рдЖрдкрдХреЗ рдЖрд╡реЗрджрди рдХреА рд╕реНрдерд┐рддрд┐ рдХреЗ рдПрдХ рдЫреЛрдЯреЗ рд╕реЗ рд╣рд┐рд╕реНрд╕реЗ рдХреЗ рд▓рд┐рдП рд╕рд░рд▓ рдФрд░ рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реИрдВред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдХрд╕реНрдЯрдо рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдХреЛ рдмрдЪрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдкрдХреЗ рдкрд╛рд╕ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдФрд░ рдЯреЛрдбрд╕реНрдЯреЛрд░ рдХреА рд╕реНрдерд┐рддрд┐ рдХреЛ рдмрдЪрд╛рдиреЗ рдХреЗ рд▓рд┐рдП SettingsStore рд╣реЛ рд╕рдХрддрд╛ рд╣реИред

рдПрдХ рд╕реНрдЯреЛрд░ рдСрдмреНрдЬреЗрдХреНрдЯ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рдПрдХ рд╡рд░реНрдЧ рдмрдирд╛рдирд╛ рд╣реЛрдЧрд╛ рдЬреЛ рдСрдмреНрдЬрд░реНрд╡реЗрдмрд▓ рдУрдмреНрдЬреЗрдХреНрдЯ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреЗ рдЕрдиреБрд░реВрдк рд╣реЛред рдУрдмреНрдЬрд░реНрд╡реЗрдмрд▓ рдУрдмреНрдЬреЗрдХреНрдЯ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рд╕реНрд╡рд┐рдлреНрдЯрдпреВрдЖрдИ рдХреЛ рдбреЗрдЯрд╛ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХрд╛ рдЕрд╡рд▓реЛрдХрди рдФрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рдУрдмреНрдЬрд░реНрд╡реЗрдмрд▓ рдУрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдзрд┐рдХ рдЬрд╛рдирдиреЗ рдХреЗ рд▓рд┐рдП, " рд╕реНрд╡рд┐рдлреНрдЯрдпреВрдЖрдИ рдореЗрдВ рдбреЗрдЯрд╛ рдкреНрд░рд╡рд╛рд╣ рдХрд╛ рдкреНрд░рдмрдВрдзрди " рд▓реЗрдЦ рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓реЗрдВред рдЖрдЗрдП SettingsStore рдСрдмреНрдЬреЗрдХреНрдЯ рдХрд╛ рдПрдХ рд╕рд░рд▓ рдЙрджрд╛рд╣рд░рдг рджреЗрдЦреЗрдВред

import Foundation import Combine final class SettingsStore: ObservableObject { let objectWillChange = PassthroughSubject<Void, Never>() @UserDefault(Constants.UserDefaults.sleepGoal, defaultValue: 8.0) var sleepGoal: Double @UserDefault(Constants.UserDefaults.notifications, defaultValue: true) var isNotificationsEnabled: Bool private var didChangeCancellable: AnyCancellable? override init() { super.init() didChangeCancellable = NotificationCenter.default .publisher(for: UserDefaults.didChangeNotification) .map { _ in () } .receive(on: DispatchQueue.main) .subscribe(objectWillChange) } } 

рдЙрдкрд░реЛрдХреНрдд рдХреЛрдб рдЙрджрд╛рд╣рд░рдг рдореЗрдВ, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ SettingsStore рд╡рд░реНрдЧ рд╣реИ, рдЬреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рддрдХ рдкрд╣реБрдВрдЪ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред рдЬрдм рднреА рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдмрджрд▓рддрд╛ рд╣реИ, рд╣рдо рд╕реНрд╡рд┐рдлреНрдЯрдпреВрдЖрдИ рдХреЛ рд╕реВрдЪрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП didChangeNotification рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред

рд╡рд┐рд╕реНрддрд╛рд░рд┐рдд рдЙрдкрдпреЛрдЧ


рдЪрд▓реЛ рдПрдХ рд╕рд╛рдзрд╛рд░рдг рдЯреЛрдбреЛ рдРрдк рдмрдирд╛рдХрд░ рд╕реНрдЯреЛрд░ рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рдПрдХ рдФрд░ рдЙрдкрдпреЛрдЧ рдХреЛ рджреЗрдЦреЗрдВред рд╣рдореЗрдВ рдПрдХ рд╕реНрдЯреЛрд░ рдСрдмреНрдЬреЗрдХреНрдЯ рдмрдирд╛рдиреЗ рдХреА рдЬрд╝рд░реВрд░рдд рд╣реИ рдЬреЛ рдХрд╛рд░реНрдпреЛрдВ рдХреА рдПрдХ рд╕реВрдЪреА рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЙрдиреНрд╣реЗрдВ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╛рд░реНрд░рд╡рд╛рдИ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЙрдиреНрд╣реЗрдВ рд╣рдЯрд╛рдиреЗ рдФрд░ рдЙрдиреНрд╣реЗрдВ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд░рдирд╛ред

 import Foundation import Combine struct Todo: Identifiable, Hashable { let id = UUID() var title: String var date: Date var isDone: Bool var priority: Int } final class TodosStore: ObservableObject { @Published var todos: [Todo] = [] func orderByDate() { todos.sort { $0.date < $1.date } } func orderByPriority() { todos.sort { $0.priority > $1.priority } } func removeCompleted() { todos.removeAll { $0.isDone } } } 

рдПрдХ рдЯреЛрдбреЛрд╕рд╕реНрдЯреЛрд░ рдХреНрд▓рд╛рд╕ рд╣реИ рдЬреЛ рдСрдмреНрдЬрд░реНрд╡реЗрдмрд▓рдСрдмреНрдЬреЗрдХреНрдЯ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреЗ рдЕрдиреБрд░реВрдк рд╣реИред рдЯреЛрдбреЛрд╕рд╕реНрдЯреЛрд░ рдЕрдкрдиреЗ рд░рд╛рдЬреНрдп рдХреЛ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдИ рдХреНрд░рд┐рдпрд╛рдПрдВ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ, рд╣рдо рдЗрди рддрд░реАрдХреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдЕрдкрдиреЗ рд╡рд┐рдЪрд╛рд░реЛрдВ рд╕реЗ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ, SwiftUI рд╣рд░ рдмрд╛рд░ @Published рдлрд╝реАрд▓реНрдб рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЛ рджреЗрдЦрдиреЗ рдХрд╛ рдЕрджреНрдпрддрди рдХрд░рддрд╛ рд╣реИред рдпрд╣реА рдХрд╛рд░рдг рд╣реИ рдХрд┐ рдЯреЛрдбреЛ рддрддреНрд╡реЛрдВ рдХреА рд╕рд░рдгреА рдХреЛ @ рдкреНрд░рдХрд╛рд╢рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ ред рдЬреИрд╕реЗ рд╣реА рд╣рдо рдЗрд╕ рдРрд░реЗ рд╕реЗ рддрддреНрд╡реЛрдВ рдХреЛ рдЬреЛрдбрд╝рддреЗ рдпрд╛ рд╣рдЯрд╛рддреЗ рд╣реИрдВ, SwiftUI, TodosStore рдХреЗ рд╕рдмреНрд╕рдХреНрд░рд╛рдЗрдмреНрдб рд╡реНрдпреВ рдХреЛ рдЕрдкрдбреЗрдЯ рдХрд░ рджреЗрдЧрд╛ ред

рдЕрдм рдЖрдк рдПрдХ рджреГрд╢реНрдп рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рдХрд╛рд░реНрдпреЛрдВ рдХреА рд╕реВрдЪреА рдФрд░ рдРрд╕реЗ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдкреВрд░рд╛ рдХрд░рддрд╛ рд╣реИ рдЬреИрд╕реЗ рдХрд┐ рдХрд╛рд░реНрдп рдХреЛ рдкреВрд░рд╛ рдХрд░рдирд╛, рд╣рдЯрд╛рдирд╛ рдФрд░ рдЙрд╕ рдХрд╛рд░реНрдп рдХреЛ рдмрджрд▓рдирд╛ рдЬрд┐рд╕рдореЗрдВ рдХрд╛рд░реНрдп рдкреНрд░рджрд░реНрд╢рд┐рдд рд╣реЛрддреЗ рд╣реИрдВред рдЖрдЗрдП рдПрдХ рджреГрд╢реНрдп рдмрдирд╛рдХрд░ рд╢реБрд░реВ рдХрд░реЗрдВ рдЬреЛ рдХрд╛рд░реНрдп рдХреЗ рд╢реАрд░реНрд╖рдХ рдХреЛ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдХрд╛рд░реНрдп рдХреЛ рдкреВрд░рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕реНрд╡рд┐рдЪ рджрд┐рдЦрд╛рддрд╛ рд╣реИред

 import SwiftUI struct TodoItemView: View { let todo: Binding<Todo> var body: some View { HStack { Toggle(isOn: todo.isDone) { Text(todo.title.wrappedValue) .strikethrough(todo.isDone.wrappedValue) } } } } 

рдЙрдкрд░реЛрдХреНрдд рдЙрджрд╛рд╣рд░рдг рдореЗрдВ, рдмрд╛рдЗрдВрдбрд┐рдВрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рдПрдХ рд╕рдВрджрд░реНрдн рдкреНрд░рджрд╛рди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП рдПрдХ рдореВрд▓реНрдп рдкреНрд░рдХрд╛рд░ рддрдХ рдкрд╣реБрдВрдЪред рджреВрд╕рд░реЗ рд╢рдмреНрджреЛрдВ рдореЗрдВ, рдЕрдиреБрджрд╛рди рдЯреВрдбреВ рддрддреНрд╡ рддрдХ рдкрд╣реБрдВрдЪ рд▓рд┐рдЦрддрд╛ рд╣реИред TodoItemView рдЯреЛрдбреЛ рд╕рдВрд░рдЪрдирд╛ рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕рдореЗрдВ рдмрд╛рдЗрдВрдбрд┐рдВрдЧ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЯреЛрдбреЛрд╕реНрдЯреЛрд░ рддрдХ рдкрд╣реБрдВрдЪ рд╣реИред

 import SwiftUI struct TodosView: View { @EnvironmentObject var store: TodosStore @State private var draft: String = "" var body: some View { NavigationView { List { TextField("Type something...", text: $draft, onCommit: addTodo) ForEach(store.todos.indexed(), id: \.1.id) { index, _ in TodoItemView(todo: self.$store.todos[index]) } .onDelete(perform: delete) .onMove(perform: move) } .navigationBarItems(trailing: EditButton()) .navigationBarTitle("Todos") } } private func delete(_ indexes: IndexSet) { store.todos.remove(atOffsets: indexes) } private func move(_ indexes: IndexSet, to offset: Int) { store.todos.move(fromOffsets: indexes, toOffset: offset) } private func addTodo() { let newTodo = Todo(title: draft, date: Date(), isDone: false, priority: 0) store.todos.insert(newTodo, at: 0) draft = "" } } 

рдЕрдм рд╣рдорд╛рд░реЗ рдкрд╛рд╕ TodosView , рдПрдХ рддрддреНрд╡ рд╣реИ рдЬреЛ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реВрдЪреА рдШрдЯрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред рд╕реВрдЪреА рдШрдЯрдХ рдкреБрдирд░реНрд▓реЗрдЦрди рдФрд░ рд╡рд┐рд▓реЛрдкрди рднреА рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред рдПрдХ рдФрд░ рджрд┐рд▓рдЪрд╕реНрдк рдмрд╛рдд рдЕрдиреБрдХреНрд░рдорд┐рдд () рдлрд╝рдВрдХреНрд╢рди рд╣реИред рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рдЕрдкрдиреЗ рд╕реВрдЪрдХрд╛рдВрдХреЛрдВ рдХреЗ рд╕рд╛рде рддрддреНрд╡реЛрдВ рдХрд╛ рдПрдХ рд╕рдВрдЧреНрд░рд╣ рд▓реМрдЯрд╛рддрд╛ рд╣реИред рд╣рдо рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдмрд╛рдЗрдВрдбрд┐рдВрдЧ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕реНрдЯреЛрд░ рдореЗрдВ рд╡рд╕реНрддреБрдУрдВ рддрдХ рдкрд╣реБрдВрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рдХрд░рддреЗ рд╣реИрдВред рдпрд╣рд╛рдБ рдЗрд╕ рд╡рд┐рд╕реНрддрд╛рд░ рдХрд╛ рдкреВрд░рд╛ рд╕реНрд░реЛрдд рд╣реИред

 import Foundation struct IndexedCollection<Base: RandomAccessCollection>: RandomAccessCollection { typealias Index = Base.Index typealias Element = (index: Index, element: Base.Element) let base: Base var startIndex: Index { base.startIndex } var endIndex: Index { base.endIndex } func index(after i: Index) -> Index { base.index(after: i) } func index(before i: Index) -> Index { base.index(before: i) } func index(_ i: Index, offsetBy distance: Int) -> Index { base.index(i, offsetBy: distance) } subscript(position: Index) -> Element { (index: position, element: base[position]) } } extension RandomAccessCollection { func indexed() -> IndexedCollection<Self> { IndexedCollection(base: self) } } 

рд╕реНрдЯреЛрд░ рд╡рд╕реНрддреБрдУрдВ рдХреЗ рднрдВрдбрд╛рд░рдг рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рд╡рд░рдг рдПрдХ рдЖрджрд░реНрд╢ рдЙрдореНрдореАрджрд╡рд╛рд░ рд╣реИред рдкрд░реНрдпрд╛рд╡рд░рдг рдЙрдиреНрд╣реЗрдВ init рдкрджреНрдзрддрд┐ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕реНрдкрд╖реНрдЯ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рдмрд┐рдирд╛ рдХрдИ рд╡рд┐рдЪрд╛рд░реЛрдВ рдХреЗ рдмреАрдЪ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░ рд╕рдХрддрд╛ рд╣реИред SwiftUI рдореЗрдВ рдкрд░реНрдпрд╛рд╡рд░рдг рдХреЗ рд▓рд╛рднреЛрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдзрд┐рдХ рдЬрд╛рдирдиреЗ рдХреЗ рд▓рд┐рдП, " SwiftUI рдореЗрдВ рдкрд░реНрдпрд╛рд╡рд░рдг рд╕реБрд╡рд┐рдзрд╛рдПрдБ " рд▓реЗрдЦ рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓реЗрдВред

рд╕рдм-рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ

рдирд┐рд╖реНрдХрд░реНрд╖


рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рдХрдИ рд╕реНрдЯреЛрд░ рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреА рд╕реНрдерд┐рддрд┐ рдХреЛ рдореЙрдбрд▓ рдХрд░рдиреЗ рдХреЗ рддрд░реАрдХреЗ рдкрд░ рдЪрд░реНрдЪрд╛ рдХреА рдЧрдИред рдореБрдЭреЗ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЗрд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреА рд╕рд╛рджрдЧреА рдкрд╕рдВрдж рд╣реИ рдФрд░ рдЕрдзрд┐рдХ рд╕реНрдЯреЛрд░ рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдЬреЛрдбрд╝рдХрд░ рдЕрдкрдиреЗ рдЖрд╡реЗрджрди рдХреЛ рд╕реНрдХреЗрд▓ рдХрд░рдирд╛ рдХрд┐рддрдирд╛ рдЖрд╕рд╛рди рд╣реИред рдореБрдЭреЗ рдЙрдореНрдореАрдж рд╣реИ рдХрд┐ рдЖрдкрдХреЛ рдпрд╣ рд▓реЗрдЦ рдЕрдЪреНрдЫрд╛ рд▓рдЧрд╛ рд╣реЛрдЧрд╛ред

Source: https://habr.com/ru/post/hi467675/


All Articles