{"id":445600,"date":"2025-01-22T09:02:21","date_gmt":"2025-01-22T09:02:21","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=445600"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=445600","title":{"rendered":"<span>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0434\u043b\u044f SwiftUI \u2014 VSURF<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><strong>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0441 \u0432\u0430\u043c\u0438 \u041d\u0438\u043a\u0438\u0442\u0430, iOS Team Lead \u0432 Surf. \u041d\u0438\u043a\u0438\u0442\u0430 \u043e\u0431\u044a\u044f\u0441\u043d\u0438\u0442, \u043f\u043e\u0447\u0435\u043c\u0443 \u043c\u044b \u0432 Surf \u0440\u0435\u0448\u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0430 SwiftUI.\u00a0<\/strong><\/p>\n<p>SwiftUI \u0444\u0443\u043d\u0434\u0430\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 UIKit. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u043d \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0441\u0432\u043e\u0435\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u043a \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435. \u0412\u0441\u0435\u043c \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0435 MVP, MVVM, \u043d\u0430\u0448\u0430 <a href=\"https:\/\/surfstudio.github.io\/Surf-iOS-Developers\/ru\/Docs\/tech-stack\/architectures\/Surf_MVP.html\"><u>SurfMVP<\/u><\/a> \u0438 \u043f\u0440\u043e\u0447\u0438\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u044b \u0432 \u0447\u0438\u0441\u0442\u043e\u043c \u0432\u0438\u0434\u0435 \u043d\u0435 \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u043f\u043e\u0434 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 SwiftUI.\u00a0<\/p>\n<h3>\u0417\u0430\u0447\u0435\u043c \u043d\u0443\u0436\u043d\u0430 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430<\/h3>\n<h4>\u0427\u0442\u043e \u044d\u0442\u043e \u0442\u0430\u043a\u043e\u0435<\/h4>\n<p>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u2014 \u044d\u0442\u043e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438. \u0421\u043e\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u043e\u0432 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u044b\u0435 \u0432 \u043f\u043b\u0430\u043d\u0435 \u043e\u0446\u0435\u043d\u043a\u0438 \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u044b\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b. \u0422\u0430\u043a\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043b\u0435\u0433\u043a\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0438 \u043e\u0446\u0435\u043d\u0438\u0432\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0437\u043d\u0430\u043a\u043e\u043c \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043e\u0439.\u00a0<\/p>\n<p>\u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043d\u0435\u0442. \u0423 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u0432 \u0435\u0441\u0442\u044c \u0438 \u043f\u043b\u044e\u0441\u044b, \u0438 \u043c\u0438\u043d\u0443\u0441\u044b. \u041d\u043e \u0443 \u0432\u0441\u0435\u0445 \u043d\u0438\u0445 \u0435\u0441\u0442\u044c \u043a\u043e\u0435-\u0447\u0442\u043e \u043e\u0431\u0449\u0435\u0435. \u0412 \u043e\u0441\u043d\u043e\u0432\u0435 \u043b\u044e\u0431\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043b\u0435\u0436\u0430\u0442 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u041e\u041e\u041f (\u043e\u0431\u044a\u0435\u043a\u0442\u043d\u043e \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f). \u041a\u0430\u043a\u0438\u0435-\u0442\u043e \u0438\u0437 \u043d\u0438\u0445 \u0441\u043e\u0431\u043b\u044e\u0434\u0430\u044e\u0442\u0441\u044f \u0432 \u043c\u0435\u043d\u044c\u0448\u0435\u0439 \u0441\u0442\u0435\u043f\u0435\u043d\u0438, \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u2014 \u0432 \u0431\u043e\u043b\u044c\u0448\u0435\u0439. \u041d\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u0440\u043e\u0436\u0434\u0430\u044e\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0435, \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043d\u043e \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b. \u0422\u0430 \u0441\u0430\u043c\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430.\u00a0<\/p>\n<p>\u041d\u0430\u0441 \u0436\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0442 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439:<\/p>\n<ul>\n<li>\n<p>MVC \u2014 Model View Controller, \u043e\u043d \u0436\u0435 Massive ViewController;<\/p>\n<\/li>\n<li>\n<p>MVP \u2014 Model View Presenter;<\/p>\n<\/li>\n<li>\n<p>MVVM \u2014 Model View ViewModel;<\/p>\n<\/li>\n<li>\n<p>VIPER \u2014 View Interactor Presenter Entity Router.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d7b\/25a\/505\/d7b25a505e2795ede4e217402f58905c.png\" width=\"2522\" height=\"1438\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d7b\/25a\/505\/d7b25a505e2795ede4e217402f58905c.png\"\/><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/055\/5c3\/54a\/0555c354a03fe1171f700e5900542360.png\" width=\"2522\" height=\"1438\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/055\/5c3\/54a\/0555c354a03fe1171f700e5900542360.png\"\/><\/figure>\n<h4>\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0438 \u0432\u044b\u0431\u043e\u0440<\/h4>\n<p>\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0440\u0430\u0437\u043d\u044b\u0445 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u0441\u0432\u044f\u0437\u044f\u043c\u0438 \u0434\u0440\u0443\u0433 \u0441 \u0434\u0440\u0443\u0433\u043e\u043c. \u041c\u043e\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u2014 \u044d\u0442\u043e \u0434\u0435\u0442\u0430\u043b\u044c \u043f\u0430\u0437\u043b\u0430. \u0410 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440 \u0441\u0432\u044f\u0437\u0435\u0439 \u0441 \u0441\u043e\u0441\u0435\u0434\u043d\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0432\u044b\u043f\u0443\u043a\u043b\u043e\u0441\u0442\u0438 \u0438 <s>\u0432\u043f\u0443\u043a\u043b\u043e\u0441\u0442\u0438<\/s> \u0432\u044b\u0435\u043c\u043a\u0438 \u044d\u0442\u043e\u0439 \u0434\u0435\u0442\u0430\u043b\u0438.\u00a0<\/p>\n<p>\u0412\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u0432\u044b\u0448\u0435 \u043e\u0431\u043b\u0430\u0434\u0430\u044e\u0442 \u043e\u0431\u0449\u0438\u043c\u0438 \u0442\u0438\u043f\u0430\u043c\u0438 \u0434\u0435\u0442\u0430\u043b\u0435\u0439: View \u0438 Model. \u0422\u0430\u043a, View \u2014 \u044d\u0442\u043e UI, \u0430 Model \u2014 \u044d\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u043b\u0438 \u00ab\u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430\u00bb.<\/p>\n<p>\u041f\u0440\u0438 \u0432\u044b\u0431\u043e\u0440\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u043e\u0440\u044b \u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u043e\u043f\u0438\u0440\u0430\u044e\u0442\u0441\u044f \u043d\u0430 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0444\u0430\u043a\u0442\u043e\u0440\u043e\u0432: \u043e\u0442 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 (\u043f\u0435\u0442-\u043f\u0440\u043e\u0435\u043a\u0442 \u2014 \u043d\u0435\u0442 \u0441\u043c\u044b\u0441\u043b\u0430 \u0437\u0430\u043b\u0435\u0437\u0430\u0442\u044c \u0432 VIPER, \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u2014 \u043e\u0433\u0440\u0435\u0431\u0451\u043c \u0441 MVC) \u0434\u043e \u043b\u0438\u0447\u043d\u043e\u0433\u043e \u043e\u043f\u044b\u0442\u0430.\u00a0<\/p>\n<p>\u0415\u0441\u043b\u0438 \u043e\u0431\u043e\u0431\u0449\u0438\u0442\u044c, \u0442\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u0432\u044b\u0431\u0438\u0440\u0430\u044e\u0442 \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0441\u0442\u0435\u043a\u0430 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, MVVM \u0447\u0430\u0441\u0442\u043e \u0438\u0434\u0435\u0442 \u0432 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u0435 \u0441 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u0449\u0438\u043d\u043e\u0439, \u0431\u0443\u0434\u044c \u0442\u043e Combine \u0438\u043b\u0438 RxSwift. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c VIPER \u043d\u0435 \u0441\u043e\u0447\u0435\u0442\u0430\u0435\u0442\u0441\u044f \u0441 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u043c\u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u043c\u0438.\u00a0<\/p>\n<p>\u0414\u043e \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u044f SwiftUI \u0432\u044b\u0431\u043e\u0440 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043e\u0441\u043d\u043e\u0432\u044b\u0432\u0430\u043b\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u043d\u0430 \u0441\u0442\u0435\u043a\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f \u0438\u043b\u0438 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438. \u0412\u0451\u0440\u0441\u0442\u043a\u0430 \u043d\u0430 UIKit \u043d\u0435 \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 \u043f\u0440\u0438 \u0432\u044b\u0431\u043e\u0440\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b. View \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f UI \u0438 \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u0438.<\/p>\n<h4>\u0427\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c \u0441 \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u0435\u043c SwiftUI\u00a0<\/h4>\n<p>Conditional Views \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0432\u0435\u0440\u0441\u0442\u0430\u0442\u044c \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e UI, \u0430 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u043d\u044b\u0439 UI \u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u043d\u0435\u044f\u0432\u043d\u044b\u043c\u0438 State.\u00a0<\/p>\n<p>Published-\u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c State \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e, \u0438 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e, \u043d\u043e \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 Combine \u0434\u0435\u043b\u0430\u0442\u044c \u0443\u0434\u043e\u0431\u043d\u0435\u0435.\u00a0<\/p>\n<p>\u0412\u0441\u0451 \u044d\u0442\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u044e\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e View \u0438, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u0437\u0430\u0442\u0440\u0443\u0434\u043d\u044f\u044e\u0442 \u0432\u044b\u0431\u043e\u0440 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b.<\/p>\n<p>SwiftUI-View \u2014 \u044d\u0442\u043e \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e UI, \u044d\u0442\u043e View-\u0448\u0432\u0435\u0439\u0446\u0430\u0440\u0441\u043a\u0438\u0439 \u043d\u043e\u0436. \u0418 \u043c\u043d\u043e\u0433\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0437\u0434\u0435\u0441\u044c \u043d\u0430\u0440\u0443\u0448\u0430\u0435\u0442 \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u0435\u0434\u0438\u043d\u043e\u0439 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043b\u0435\u0436\u0438\u0442 \u0432 \u043e\u0441\u043d\u043e\u0432\u0435 \u043c\u043d\u043e\u0433\u0438\u0445 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440. \u042d\u0442\u0430 \u043d\u043e\u0432\u0430\u044f \u0434\u0435\u0442\u0430\u043b\u044c \u0438\u043c\u0435\u0435\u0442 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0432\u044b\u0435\u043c\u043e\u043a \u0438 \u0432\u044b\u0441\u0442\u0443\u043f\u043e\u0432. \u041f\u0430\u0437\u043b \u043d\u0435 \u0441\u0445\u043e\u0434\u0438\u0442\u0441\u044f.<\/p>\n<p>\u0418 \u0432\u043e\u0442 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0438\u0448\u043b\u0430 \u043f\u043e\u0440\u0430 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438 \u0441 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430\u043c\u0438.<\/p>\n<h4>MVC<\/h4>\n<p>\u0412 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435 MVC \u0432\u0441\u0435\u0433\u043e \u0434\u0432\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430: View \u0438 Model. \u041c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0434\u043b\u044f \u043c\u043d\u043e\u0433\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 SwiftUI View \u044d\u0442\u0430 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0431\u043e\u043b\u044c\u0448\u0435. \u041d\u043e \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c, \u0447\u0442\u043e \u0432 \u043a\u0440\u0443\u043f\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0442\u0430\u043a\u0443\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u0441\u043b\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c. \u0427\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u043b\u043e\u0433\u0438\u043a\u0438 \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f \u0432\u043e View, \u0442\u0435\u043c \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0435\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c. \u0418 \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0442\u044c \u0443 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u043d\u0430\u0434 \u043e\u0434\u043d\u0438\u043c \u044d\u043a\u0440\u0430\u043d\u043e\u043c. \u0410 \u0432 \u043e\u0441\u043e\u0431\u043e \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u043c\u043e\u0436\u043d\u043e \u0434\u0430\u0436\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/978\/be3\/8d7\/978be38d7016cd79a663d0e7070a5516.png\" alt=\"\u041a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0443 \u0441\u0442\u0430\u043b\u043e \u0441\u043b\u043e\u0436\u043d\u043e\" title=\"\u041a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0443 \u0441\u0442\u0430\u043b\u043e \u0441\u043b\u043e\u0436\u043d\u043e\" width=\"1302\" height=\"80\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/978\/be3\/8d7\/978be38d7016cd79a663d0e7070a5516.png\"\/><\/p>\n<div><figcaption>\u041a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0443 \u0441\u0442\u0430\u043b\u043e \u0441\u043b\u043e\u0436\u043d\u043e<\/figcaption><\/div>\n<\/figure>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c MVC \u0432\u043c\u0435\u0441\u0442\u0435 \u0441\u043e SwiftUI \u043c\u043e\u0436\u043d\u043e \u0434\u043b\u044f \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u043b\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0432 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u0435. \u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 conditional views \u0443\u043b\u0443\u0447\u0448\u0438\u0442 \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c \u0432\u0435\u0442\u0432\u043b\u0435\u043d\u0438\u0439, \u0435\u0441\u043b\u0438 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0442\u044c \u0441 \u0442\u043e\u0439 \u0436\u0435 \u043b\u043e\u0433\u0438\u043a\u043e\u0439 \u0432 UIKit-\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0435.<\/p>\n<p>\u0421\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0430\u0437\u043b \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c MVC \u0438 SwiftUI \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0414\u0435\u0442\u0430\u043b\u0438 \u0442\u0430\u043a\u0438\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u0435, \u0447\u0442\u043e \u044d\u0442\u043e \u043f\u0430\u0437\u043b \u0434\u043b\u044f \u043c\u043b\u0430\u0434\u0435\u043d\u0446\u0435\u0432. \u0421\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u043c \u0440\u0435\u0431\u044f\u0442\u0430\u043c \u0447\u0430\u0441\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0441\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u0437\u0430 \u043e\u0434\u043d\u0443 \u0434\u0435\u0442\u0430\u043b\u044c.<\/p>\n<h4>MVP<\/h4>\n<p>\u041e\u0442\u043b\u0438\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c MVP \u2014 passive view. Presenter \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f. \u041e\u043d \u043e\u0442\u0434\u0430\u0435\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432 View \u0447\u0435\u0440\u0435\u0437 \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c View \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e Presenter. <\/p>\n<p>\u0412 SwiftUI \u0441\u043b\u043e\u0436\u043d\u043e \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432\u0441\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 View \u2014 \u044d\u0442\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b. \u0418\u0437-\u0437\u0430 \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 View \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c\u0438 \u0438\u0437 presenter-\u0430.\u00a0<\/p>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 State \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0438\u043b\u0438 StateObject. \u0415\u0433\u043e \u043a\u0430\u043a \u0440\u0430\u0437 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u043e\u043c \u0438 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e presenter. \u041d\u043e \u0442\u043e\u0433\u0434\u0430 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c Model View State Presenter \u2014 4 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0432\u043c\u0435\u0441\u0442\u043e 3, \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0435.\u00a0<\/p>\n<p>\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u0441\u0432\u044f\u0437\u044c \u043c\u0435\u0436\u0434\u0443 \u0441\u0442\u0430\u0440\u044b\u043c\u0438 \u0438 \u043d\u043e\u0432\u044b\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u043c \u2014 \u0438\u043d\u0430\u0447\u0435 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435 \u0435\u0434\u0438\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u043e.\u00a0<\/p>\n<pre><code class=\"swift\">public struct MVPViewGroup: View {     private static let model: Model = .init()      @StateObject private var catalogPresenter: CatalogPresenter      \/\/ MARK: - State     @State private var isCartShown = false     @State private var detailSelected: Item?      \/\/ MARK: - Init      public init() {     self._catalogPresenter = .init(wrappedValue: .init(model: MVPViewGroup.model))     }      \/\/ MARK: - View      public var body: some View {     NavigationStack {         CatalogView(items: $catalogPresenter.allItems,                     cart: $catalogPresenter.cartItems,                     output: catalogPresenter)         .navigationTitle(\"Items\")     }       .onAppear {         catalogPresenter.onShowDetail = { item in             self.detailSelected = item         }         catalogPresenter.onCartShow = {             self.isCartShown = true         }       } }<\/code><\/pre>\n<p>\u0421\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0430\u0437\u043b \u043f\u043e \u043a\u0430\u043d\u043e\u043d\u0438\u0447\u043d\u043e\u043c\u0443 MVP \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c SwiftUI \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f. \u0414\u0435\u0442\u0430\u043b\u0438 \u043d\u0435 \u0441\u043a\u043b\u0435\u044f\u0442\u0441\u044f.<\/p>\n<h4>MVVM<\/h4>\n<p>\u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 MVP \u0432 MVVM \u0438\u043c\u0435\u043d\u043d\u043e View \u0434\u0435\u0440\u0436\u0438\u0442 ViewModel. \u041e\u043d \u0436\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043e\u0442 \u043d\u0435\u0451 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043e\u0431\u0440\u0430\u0442\u043d\u043e.\u00a0<\/p>\n<p>\u041d\u043e \u043a\u0430\u043a \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0432 View \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e ViewModel? \u041f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f ViewModel \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440 ViewB \u0438\u0437 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 ViewA, \u043c\u044b \u043d\u0430\u0442\u043a\u043d\u0451\u043c\u0441\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0440\u0438\u0441\u043e\u0432\u043a\u0435 ViewA \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 ViewModel. \u0410 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u0435\u0442\u0441\u044f.\u00a0<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u0438\u0442\u044c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 ViewModel \u043a \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u043e\u043c\u0443 \u0446\u0438\u043a\u043b\u0443 View \u0438 \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u0446\u0438\u043a\u043b\u043e\u0432 \u043f\u0435\u0440\u0435\u0440\u0438\u0441\u043e\u0432\u043a\u0438 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 View, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c ViewModel \u0447\u0435\u0440\u0435\u0437 EnvironmentObject. <\/p>\n<pre><code class=\"swift\">public struct MVVMViewGroup: View {     \/\/ MARK: - Properties     private static let model: Model = .init()      private var catalogVM: CatalogViewModel      \/\/ MARK: - State      @State private var isCartShown = false     @State private var detailSelected: Item?      \/\/ MARK: - Init      public init() {     self.catalogVM = .init(model: MVVMViewGroup.model)     }      \/\/ MARK: - View      public var body: some View {     NavigationStack {         CatalogView()             .environmentObject(catalogVM)             .navigationTitle(\"Items\")     }     .onAppear {         catalogVM.onShowDetail = { item in             self.detailSelected = item         }         catalogVM.onCartShow = {             self.isCartShown = true         }     }     }  }<\/code><\/pre>\n<p>\u041d\u043e \u0438 \u0443 \u044d\u0442\u043e\u0433\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0435\u0441\u0442\u044c \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u0438:<\/p>\n<ul>\n<li>\n<p>\u0441\u0432\u044f\u0437\u044c \u043c\u0435\u0436\u0434\u0443 ViewModel \u0438 View \u043d\u0435\u044f\u0432\u043d\u0430\u044f;<\/p>\n<\/li>\n<li>\n<p>\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 ViewModel \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439, \u0438\u043d\u0430\u0447\u0435 \u2014 \u043a\u0440\u0430\u0448;<\/p>\n<\/li>\n<li>\n<p>\u0437\u0430\u043a\u0440\u044b\u0442\u044c\u00a0 ViewModel \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u043c \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f. \u0410 \u044d\u0442\u043e \u0441\u043d\u0438\u0436\u0430\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u044d\u043a\u0440\u0430\u043d\u0430.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u044b\u0445\u043e\u0434\u0438\u0442, \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0430\u0437\u043b \u0438\u0437 MVVM \u0438 SwiftUI \u043c\u043e\u0436\u043d\u043e \u2014 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u043d\u0438\u043a\u043e\u0432.<\/p>\n<h4>VIPER<\/h4>\n<p>VIPER \u2014 \u044d\u0442\u043e \u044d\u0432\u043e\u043b\u044e\u0446\u0438\u044f MVP. \u041d\u0435 \u0441\u0430\u043c\u0430\u044f \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430. \u041e\u0431\u0438\u043b\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043e\u0431\u043c\u0430\u043d\u0447\u0438\u0432\u043e\u0435 \u043e\u0449\u0443\u0449\u0435\u043d\u0438\u0435 \u0435\u0451 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438.\u00a0<\/p>\n<p>\u0421 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b, \u043e\u0431\u0438\u043b\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0431\u043e\u043b\u0435\u0435 \u0436\u0451\u0441\u0442\u043a\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0437\u043e\u043d\u044b \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0438 \u0438\u0445 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435. \u0410 \u044d\u0442\u043e \u043f\u0440\u0438\u0431\u043b\u0438\u0436\u0430\u0435\u0442 \u043d\u0430\u0441 \u043a clean code \u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0441\u043e\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u043e\u0432 \u041e\u041e\u041f.<\/p>\n<p>\u041d\u043e \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0430\u0437\u043b \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u043f\u043e \u0442\u043e\u0439 \u0436\u0435 \u043f\u0440\u0438\u0447\u0438\u043d\u0435, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0435 \u0432\u044b\u0448\u043b\u043e \u0441 MVP. \u0412\u0434\u043e\u0431\u0430\u0432\u043e\u043a, \u043c\u044b \u0442\u0443\u0442 \u0436\u0435 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u043c\u0441\u044f \u0441 \u0442\u0440\u0443\u0434\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u0441 \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0438\u0435\u043c \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u0432 Router. \u0412\u0441\u0451 \u0438\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0432 SwiftUI \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0435\u0439 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u043e \u043a View.<\/p>\n<pre><code class=\"swift\">public struct VIPERViewGroup: View {      \/\/ MARK: - Properties      private static let model: Model = .init()      \/\/ MARK: - Init      public init() {}      \/\/ MARK: - View      public var body: some View {     NavigationStack {         CatalogView(presenter:                 .init(interactor: .init(model: VIPERViewGroup.model),                       router: .init()                 )         )         .navigationTitle(\"Items\")     }     }  }<\/code><\/pre>\n<h4>\u0420\u0435\u0446\u0435\u043f\u0442 \u0443\u0441\u043f\u0435\u0445\u0430<\/h4>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0430\u0437\u043b \u0438\u0437 SwiftUI \u0438 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u043d\u0443\u0436\u043d\u043e \u0443\u0447\u0435\u0441\u0442\u044c \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043d\u043e\u0432\u043e\u0439 \u043c\u043d\u043e\u0433\u043e\u043f\u0440\u043e\u0444\u0438\u043b\u044c\u043d\u043e\u0439 \u0434\u0435\u0442\u0430\u043b\u044c\u043a\u0438 SwiftUI View:<\/p>\n<ul>\n<li>\n<p>View \u2014 \u044d\u0442\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b;<\/p>\n<\/li>\n<li>\n<p>View \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 State-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435;<\/p>\n<\/li>\n<li>\n<p>View \u043c\u043e\u0433\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 State;<\/p>\n<\/li>\n<li>\n<p>View \u0438\u043c\u0435\u044e\u0442 DI \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 Environment.<\/p>\n<\/li>\n<\/ul>\n<p>\u042d\u0442\u0438 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u043d\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0438 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438. \u0412 \u043e\u0431\u0449\u0435\u043c, \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u043e\u0432\u0443\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443, \u0431\u0435\u0437 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u043e\u0432 \u0430\u0434\u0430\u043f\u0442\u0430\u0446\u0438\u0439.\u00a0<\/p>\n<h3>VSURF<\/h3>\n<p>\u0414\u0430, \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\u0430 \u043d\u0430\u0448\u0435\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043d\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438. \u041d\u043e \u043d\u0435 \u0441\u043f\u0435\u0448\u0438\u0442\u0435 \u0433\u043d\u0435\u0432\u0430\u0442\u044c\u0441\u044f, \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0430 \u043d\u0435 \u043b\u0438\u0448\u0435\u043d\u0430 \u0441\u043c\u044b\u0441\u043b\u0430:<\/p>\n<ul>\n<li>\n<p><strong>V<\/strong>iew<\/p>\n<\/li>\n<li>\n<p>view <strong>S<\/strong>tate<\/p>\n<\/li>\n<li>\n<p>business <strong>U<\/strong>nit<\/p>\n<\/li>\n<li>\n<p>navigation <strong>R<\/strong>outing<\/p>\n<\/li>\n<li>\n<p>singleton services <strong>F<\/strong>actory<\/p>\n<\/li>\n<\/ul>\n<p><strong>View<\/strong> \u2014 \u043f\u0435\u0440\u0432\u0430\u044f \u0431\u0443\u043a\u0432\u0430 \u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u0438\u0437\u0430\u0439\u043d-\u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0438 <a href=\"https:\/\/github.com\/surfstudio\/SurfPlaybook\"><u>Playbook<\/u><\/a>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0447\u0430\u0441\u0442\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u0441 UI.<\/p>\n<p><strong>ViewState<\/strong> \u2014 \u043c\u044b \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f SwiftUI View \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 State \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445. \u0421\u0442\u0440\u043e\u0438\u043c binding \u0441 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u043e\u0439 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0445 Published \u0441\u0432\u043e\u0439\u0441\u0442\u0432.<\/p>\n<p><strong>Business Unit<\/strong> \u2014 \u043e\u0442\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043b\u043e\u0433\u0438\u043a\u0438 \u043e\u0442 View. \u0412 \u044d\u0442\u043e\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432: \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u043d\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0438 \u0434\u0440\u0443\u0433\u0438\u0445. \u0422\u043e \u0435\u0441\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430. \u0418\u043d\u044b\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438, \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u043d\u044b\u0439 flow.<\/p>\n<p><strong>Navigation Routing<\/strong> \u2014 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u043c\u0435\u0436\u0434\u0443 \u044d\u043a\u0440\u0430\u043d\u0430\u043c\u0438, \u0432\u0435\u0434\u044c \u0432 SwiftUI \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f \u0437\u0430\u0432\u044f\u0437\u0430\u043d\u0430 \u043d\u0430 Binding. \u041f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u043c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u043e\u0440\u043e\u043c \u0442\u0443\u0442 \u043d\u0435 \u043e\u0431\u043e\u0439\u0442\u0438\u0441\u044c.<\/p>\n<p><strong>SIngleton Services Factory<\/strong> \u2014 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432. Network-\u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0441 \u0411\u0414 \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u2014 \u044d\u0442\u043e \u0441\u0438\u043d\u0433\u043b\u0442\u043e\u043d\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0440\u043e\u0436\u0434\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0444\u0430\u0431\u0440\u0438\u043a\u0438.\u00a0<\/p>\n<p>\u041f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0435 \u0430\u0441\u043f\u0435\u043a\u0442\u044b \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0437\u0432\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c\u0438 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430\u043c\u0438 \u043d\u0430\u0448\u0435\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b. \u0415\u0441\u043b\u0438 \u043f\u043e\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0433\u043b\u0443\u0431\u0436\u0435, \u0431\u0443\u0434\u0435\u043c \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u043e \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u0443\u0436\u0435 \u043f\u043e\u0442\u043e\u043c \u2014 \u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u0445.<\/p>\n<h4>\u041c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0441\u0442\u044c<\/h4>\n<p>\u041d\u0438 \u043e\u0434\u043d\u0430 \u0438\u0437 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440 \u0438\u0437 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0430 \u043d\u0435 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0438 \u0441\u0432\u044f\u0437\u0438 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438. \u0425\u043e\u0442\u044f \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044f \u043c\u043d\u043e\u0433\u043e\u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0443\u0436\u0435 \u0434\u0430\u0432\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043d\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445.<\/p>\n<p>\u0417\u0430 \u0441\u0447\u0451\u0442 \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u0442\u043a\u043e\u0439 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043e\u0434\u0430 \u043c\u043d\u043e\u0433\u043e\u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>\u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430;<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u0432\u044b\u0441\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442\u0430;<\/p>\n<\/li>\n<li>\n<p>\u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u044b\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>\u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447.<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0443 <strong>VSURF<\/strong> \u0435\u0441\u0442\u044c \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438 \u0438 \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438. \u041a\u0430\u0436\u0434\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0441\u0432\u043e\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0439 \u0437\u043e\u043d\u0435 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438. \u041c\u043e\u0434\u0443\u043b\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u0434\u0440\u0443\u0433 \u043e\u0442 \u0434\u0440\u0443\u0433\u0430.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a51\/b72\/aa4\/a51b72aa45f835a89f5dc410005510d8.png\" alt=\"\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438\" title=\"\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438\" width=\"2522\" height=\"2522\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a51\/b72\/aa4\/a51b72aa45f835a89f5dc410005510d8.png\"\/><\/p>\n<div><figcaption>\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/09a\/d36\/6e0\/09ad366e01e2b6d4c86c52617714cc97.png\" alt=\"\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438\" title=\"\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438\" width=\"2522\" height=\"1438\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/09a\/d36\/6e0\/09ad366e01e2b6d4c86c52617714cc97.png\"\/><\/p>\n<div><figcaption>\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438<\/figcaption><\/div>\n<\/figure>\n<p>\u0420\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>\u0443\u043f\u0440\u043e\u0449\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0434\u0443\u043b\u0435\u0439;<\/p>\n<\/li>\n<li>\n<p>\u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439.<\/p>\n<\/li>\n<\/ul>\n<p>\u041c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435, \u043d\u043e \u043e\u0442\u043c\u0435\u0442\u0438\u043c, \u0447\u0442\u043e \u0443 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043a:<\/p>\n<ol>\n<li>\n<p>\u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043d\u043d\u044b\u043c \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c:<\/p>\n<ol>\n<li>\n<p>\u043d\u0438\u043a\u0430\u043a\u0438\u0435;<\/p>\n<\/li>\n<li>\n<p>\u0442\u043e\u043b\u044c\u043a\u043e \u0443\u0442\u0438\u043b\u0438\u0442\u0430\u0440\u043d\u044b\u0435 (\u043d\u0435 SDK);<\/p>\n<\/li>\n<li>\n<p>\u041b\u044e\u0431\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<\/li>\n<\/ol>\n<\/li>\n<li>\n<p>\u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u043c \u0442\u0438\u043f\u0430\u043c \u0442\u0435\u0441\u0442\u043e\u0432:<\/p>\n<ol>\n<li>\n<p>Unit;<\/p>\n<\/li>\n<li>\n<p>UI;<\/p>\n<\/li>\n<li>\n<p>Snapshot.<\/p>\n<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p>\u0422\u0430\u043a\u043e\u0435 \u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u043c\u043e\u0434\u0443\u043b\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043b\u0430\u0434\u0438\u0442\u044c \u0444\u0430\u0431\u0440\u0438\u043a\u0443 \u043f\u043e \u0441\u0431\u043e\u0440\u043a\u0435 \u0434\u0435\u0442\u0430\u043b\u0435\u0439 \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043f\u0430\u0437\u043b\u0430. \u0410 \u043e\u043d\u0438, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0441\u043e\u0441\u0442\u043e\u044f\u0442 \u0438\u0437 \u0431\u043e\u043b\u0435\u0435 \u043c\u0435\u043b\u043a\u0438\u0445 \u0434\u0435\u0442\u0430\u043b\u0435\u0439 \u2014 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u043f\u043e \u00ab\u043b\u0438\u043d\u0438\u044f\u043c\u00bb \u0441\u0431\u043e\u0440\u043a\u0438.\u00a0<\/p>\n<h4>\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b<\/h4>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0449\u0435 \u0431\u044b\u043b\u043e \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u044c \u043d\u0430\u0448\u0443 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u0441 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430\u043c\u0438, \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440 \u0441\u0432\u044f\u0437\u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u043e\u0439.\u00a0<\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0437\u0430 \u044d\u0442\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0442 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b View, ViewStateHolder \u0438 Unit.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/251\/be1\/9dd\/251be19dd5199cce48a23577fa770e3f.png\" width=\"2522\" height=\"1438\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/251\/be1\/9dd\/251be19dd5199cce48a23577fa770e3f.png\"\/><\/figure>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 ViewStateHolder \u2014 \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0438\u0441 \u0438 \u0441\u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u0438\u0437\u043d\u0435\u0441-\u043c\u043e\u0434\u0435\u043b\u044c \u0432 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f View. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, ViewStateHolder \u043f\u0440\u043e\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043e\u0442 View \u0432 Unit, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b.\u00a0 <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/582\/de0\/d87\/582de0d874b32fba22d95520c0cb7e43.png\" width=\"2522\" height=\"1438\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/582\/de0\/d87\/582de0d874b32fba22d95520c0cb7e43.png\"\/><\/figure>\n<p>\u0422\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438 Unit \u2014 \u044d\u0442\u043e \u0441\u0435\u0440\u0432\u0438\u0441 \u0441:<\/p>\n<ul>\n<li>\n<p>Input-\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u043c \u0434\u043b\u044f \u043f\u0440\u0438\u0451\u043c\u0430 \u043a\u043e\u043c\u0430\u043d\u0434 \u043e\u0442 ViewStateHolder;<\/p>\n<\/li>\n<li>\n<p>Output-\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u043c, \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u044f\u044e\u0449\u0438\u043c \u043f\u043e\u0442\u043e\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 AnyPublisher.\u00a0<\/p>\n<\/li>\n<\/ul>\n<p>ViewStateHolder \u2014 \u044d\u0442\u043e ObservableObject, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0443\u044e View \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 StateObject:\u00a0<\/p>\n<ul>\n<li>\n<p>Input-\u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0434\u043b\u044f \u043f\u0440\u0438\u0451\u043c\u0430 \u043a\u043e\u043c\u0430\u043d\u0434 \u043e\u0442 View;<\/p>\n<\/li>\n<li>\n<p>Output \u0432 \u0432\u0438\u0434\u0435 @Published \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 ViewState.<\/p>\n<\/li>\n<\/ul>\n<p>View \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043b\u043e\u0433\u0438\u043a\u0443 \u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 State \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f child View:<\/p>\n<ul>\n<li>\n<p>\u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0441 Binding&lt;ViewState&gt;;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 Weak \u0440\u0435\u0444\u0435\u0440\u0435\u043d\u0441 \u043d\u0430 Input StateHolder, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0439 \u0447\u0435\u0440\u0435\u0437 Environment.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043e\u0431\u0449\u0438\u0435 \u0447\u0435\u0440\u0442\u044b \u0438 \u0441 MVP, \u0438 \u0441 MVVM.<\/p>\n<p>\u0418\u0434\u0451\u043c \u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435.<\/p>\n<h4>\u0417\u0430\u0434\u0430\u0447\u0430<\/h4>\n<p>\u0421\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b\u0438 5 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0443\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u043e\u0433\u043e \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430 \u0441 \u043a\u043e\u0440\u0437\u0438\u043d\u043e\u0439. \u041a\u0430\u0436\u0434\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e SwiftUI \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e Combine \u2014 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438.\u00a0<\/p>\n<p><strong>\u0414\u0430\u043d\u043e<\/strong><\/p>\n<ul>\n<li>\n<p>\u00a0\u0441\u0442\u0430\u0442\u0438\u0447\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430.<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0421\u0434\u0435\u043b\u0430\u0442\u044c<\/strong><\/p>\n<ul>\n<li>\n<p>\u043f\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0438 \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0438\u0437 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u0435\u0437\u0435\u043d\u0442\u0430\u0446\u0438\u044e;<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0438 \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0441 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0447\u0435\u0440\u0435\u0437 navigationStack;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u2014 \u043f\u043e\u043a\u0443\u043f\u043a\u0430.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0441\u0447\u0451\u0442\u0435 \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u0442\u0430\u0431\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b. \u0412\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e \u0432\u0441\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b, \u043d\u043e \u043f\u043e \u043a\u043e\u0434\u0443 \u0432 \u043d\u0438\u0445 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043e\u0442\u043b\u0438\u0447\u0438\u0439.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/064\/874\/25d\/06487425d648f77c8c4e21df11e06714.gif\" width=\"295\" height=\"640\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/064\/874\/25d\/06487425d648f77c8c4e21df11e06714.gif\"\/><\/figure>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043b\u0435\u0436\u0430\u0442 <a href=\"https:\/\/github.com\/surfstudio\/vsurf-demo\"><u>\u0442\u0443\u0442<\/u><\/a>. \u0415\u0441\u043b\u0438 \u0432\u044b \u044d\u043a\u0441\u043f\u0435\u0440\u0442 \u0432 \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440 \u0438 \u0432\u0438\u0434\u0438\u0442\u0435 \u043d\u0435\u0434\u043e\u0447\u0451\u0442\u044b \u0432 \u043d\u0430\u0448\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0439\u0442\u0435 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 pr \u0438\u043b\u0438 issue.<\/p>\n<p>\u041c\u044b \u0436\u0435 \u0443\u0434\u0435\u043b\u0438\u043c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043d\u0430\u0448\u0435\u0439 \u043d\u043e\u0432\u043e\u0438\u0441\u043f\u0435\u0447\u0451\u043d\u043d\u043e\u0439 VSURF.<\/p>\n<h4>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/h4>\n<p>\u0421\u043e\u0433\u043b\u0430\u0441\u043d\u043e VSURF, \u043b\u044e\u0431\u043e\u0439 \u043c\u043e\u0434\u0443\u043b\u044c Flow \u0443\u0440\u043e\u0432\u043d\u044f \u0438\u043c\u0435\u0435\u0442 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u0443\u044e View. \u042d\u0442\u0430 View \u0441\u0442\u0430\u043d\u0435\u0442 \u0442\u043e\u0447\u043a\u043e\u0439 \u0432\u0445\u043e\u0434\u0430 \u0432\u043e Flow, \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0432\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u043d\u0443\u0436\u043d\u043e\u0435 \u043c\u0435\u0441\u0442\u043e.\u00a0<\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 ViewGroup \u0432\u0441\u0435\u0445 \u043f\u044f\u0442\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432 TabView. \u041d\u043e SwiftUI \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u0435\u043d \u0442\u0435\u043c, \u0447\u0442\u043e View \u2014 \u0432\u0441\u0451 \u0435\u0449\u0451 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043b\u0435\u0433\u043a\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u044e View.<\/p>\n<pre><code class=\"swift\">TabView(selection: $selectedTab) {     MVCViewGroup()         .tag(AppTab.mvc)     MVPViewGroup()         .tag(AppTab.mvp)     MVVMViewGroup()         .tag(AppTab.mvvm)     VIPERViewGroup()         .tag(AppTab.viper)     VSURFViewGroup()         .tag(AppTab.vsurf) }<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u0447\u0435\u0440\u043a\u043d\u0443\u0442\u044c \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u044c SwiftUI, \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 VSURF \u2014 <strong>\u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439 init<\/strong> ViewGroup <strong>\u0431\u0435\u0437 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432<\/strong>.\u00a0<\/p>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432, \u0432\u0451\u0440\u0441\u0442\u043a\u0430 \u0438 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f \u0432\u043d\u0443\u0442\u0440\u0438 \u043c\u043e\u0434\u0443\u043b\u044f \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 ViewGroup. \u0418\u0437\u043e\u043b\u044f\u0446\u0438\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u044d\u043a\u0441\u043f\u043b\u0443\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c SwiftUI \u0438 \u043f\u0435\u0440\u0435\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u0440\u0438\u0441\u0443\u043d\u043e\u043a \u043f\u0430\u0437\u043b\u0430, \u043d\u0435 \u043c\u0435\u043d\u044f\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440 \u0434\u0435\u0442\u0430\u043b\u0435\u043a (flow).<\/p>\n<pre><code class=\"swift\">public struct VSURFViewGroup: View {      \/\/ MARK: - State      \/\/...      @StateObject private var catalogStateHolder: CatalogViewStateHolder     @StateObject private var cartStateHolder: CartViewStateHolder      \/\/ MARK: - Private Properties      private let catalogUnit: CatalogUnitOutput     private let cartUnit: CartUnitInput &amp; CartUnitOutput      \/\/ MARK: - Init      public init() {         let catalogUnit = VSURFStateFacade.Units.catalog()       let cartUnit = VSURFStateFacade.Units.cart()       self.catalogUnit = catalogUnit       self.cartUnit = cartUnit       self._catalogStateHolder = .init(wrappedValue: .init(catalogUnit: catalogUnit, cartUnit: cartUnit))       self._cartStateHolder = .init(wrappedValue: .init(cartUnit: cartUnit))     }     \/\/... }<\/code><\/pre>\n<p>\u041f\u0440\u0430\u0432\u0438\u043b\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0431\u0435\u0437 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 ViewGroup, \u043d\u043e \u0438 \u043d\u0430 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e business unit. \u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u044d\u0442\u043e \u2014 catalogUnit \u0438 cartUnit, \u0442\u0430\u043a\u0438\u0435 \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u044b \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438. \u041e\u043d\u0438 \u043f\u043e\u0445\u043e\u0436\u0438 \u043d\u0430 interactor \u0438\u0437 VIPER.<\/p>\n<pre><code class=\"swift\">final class CatalogUnit {      \/\/ MARK: - Private Properties      private let localItems: [Item]      \/\/ MARK: - Init      init(items: [Item]) {         self.localItems = items     } }  \/\/ MARK: - CatalogUnitOutput  extension CatalogUnit: CatalogUnitOutput {      var items: AnyPublisher&lt;[Item], Never&gt; {         Just(localItems).eraseToAnyPublisher()     }  }<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0438\u0439 unit \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442, \u043e\u0442\u043a\u0443\u0434\u0430 \u0432\u0437\u044f\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435. \u0414\u043b\u044f \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u0438\u044f \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0432 \u044d\u0442\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430. \u041d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u0436\u0435 \u0432\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a \u0441\u0435\u0442\u0435\u0432\u043e\u043c\u0443 \u0441\u043b\u043e\u044e, \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435.<\/p>\n<pre><code class=\"swift\">final class CartUnit {       \/\/ MARK: - Private Properties      private let model: Model      \/\/ MARK: - Init      init(model: Model) {         self.model = model     }  }  \/\/ MARK: - CartUnitInput  extension CartUnit: CartUnitInput {      func addItem(_ item: Item) {         model.addItem(item)     }      func removeItem(_ item: Item) {         model.removeItem(item)     }      func removeAll() {         model.removeAll()     } }  \/\/ MARK: - CartUnitOutput  extension CartUnit: CartUnitOutput {      var items: AnyPublisher&lt;[Item], Never&gt; {         model.cart     }  }<\/code><\/pre>\n<p>\u0412 \u0431\u043e\u043b\u0435\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0443 unit \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f input-\u043c\u0435\u0442\u043e\u0434\u044b, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u044b \u043c\u0435\u043d\u044f\u0435\u043c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0441 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u043e\u0439.\u00a0<\/p>\n<p>\u0412\u043d\u0443\u0442\u0440\u0438 flow \u043c\u043e\u0434\u0443\u043b\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430 \u0438 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0433\u043e unit. Unit \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u044d\u043a\u0440\u0430\u043d\u0430\u043c\u0438. <\/p>\n<pre><code class=\"swift\">final class CartViewStateHolder: ObservableObject {      \/\/ MARK: - Private Properties      private var cancellables: Set&lt;AnyCancellable&gt; = []     private var cartUnit: CartUnitInput &amp; CartUnitOutput      \/\/ MARK: - Published      @Published var state: CartView.ViewState = .init(items: [])      \/\/ MARK: - Init      init(cartUnit: CartUnitInput &amp; CartUnitOutput) {         self.cartUnit = cartUnit       subscribe()     }  }  \/\/ MARK: - ViewOutput  extension CartViewStateHolder: CartViewOutput {      func buy() {         cartUnit.removeAll()     }  }<\/code><\/pre>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0431\u0438\u0437\u043d\u0435\u0441-\u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0432 \u043c\u043e\u0434\u0435\u043b\u044c View \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f StateHolder. \u042d\u0442\u043e \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c, \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u043d\u0430\u044f \u043a View. \u0411\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0439 \u0430\u043d\u0430\u043b\u043e\u0433 \u2014 Presenter \u0438\u043b\u0438 ViewModel.\u00a0<\/p>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, StateHolder \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432 unit \u0447\u0435\u0440\u0435\u0437 \u0435\u0433\u043e input-\u043c\u0435\u0442\u043e\u0434\u044b.<\/p>\n<pre><code class=\"swift\">\/\/ MARK: - Private Methods  private extension CartViewStateHolder {      func subscribe() {          cartUnit.items             .map { cartItems -&gt; [(String, Int)] in                 cartItems.reduce(into: [Item: Int]()) { result, item in                     result.updateValue((result[item] ?? 0) + 1, forKey: item)                 }                 .sorted(by: { $0.key.title &gt; $1.key.title })                 .compactMap { ($0.key.title, $0.value) }             }         .receive(on: DispatchQueue.main)         .map { items -&gt; CartView.ViewState in             CartView.ViewState(items: items.map { (item, count) -&gt; CartView.CartItem in                     .init(title: item, count: count)             })         }         .assign(to: \\.state, on: self)         .store(in: &amp;cancellables)  }  }<\/code><\/pre>\n<p>\u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0430\u0433\u0438\u0438 Combine \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c ViewState \u0438 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0435\u0433\u043e \u0432 Published \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e. \u041e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a View \u0447\u0435\u0440\u0435\u0437 Binding.<\/p>\n<pre><code class=\"swift\">public struct CatalogView: View {      \/\/ MARK: - Nested Types      struct CatalogItem {         let title: String       let canRemoveFromCart: Bool     }      struct CartSnapshot {       let count: Int        var isEmpty: Bool {         count == 0       }     }      struct ViewState {       let items: [CatalogItem]       let cart: CartSnapshot     }      \/\/ MARK: - States      @Binding private var state: ViewState     @Binding private var navigationState: VSURFNavigationState     @Binding private var detailSelected: String?      \/\/ MARK: - Weak Reference      @WeakReference private var output: CatalogViewOutput?      \/\/ MARK: - Init      init(state: Binding&lt;ViewState&gt;,          navigationState: Binding&lt;VSURFNavigationState&gt;,         detailSelected: Binding&lt;String?&gt;) {         _state = state       _navigationState = navigationState       _detailSelected = detailSelected     }     \/\/... }<\/code><\/pre>\n<p>\u041a\u0440\u043e\u043c\u0435 Binding&lt;ViewState&gt; \u0432 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0435 View \u0435\u0441\u0442\u044c \u0435\u0449\u0451 \u0434\u0432\u0430 Binding. \u041e\u0434\u0438\u043d \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044e \u0432 NavigationStack, \u0434\u0440\u0443\u0433\u043e\u0439 \u2014 \u0437\u0430 \u043f\u0440\u0435\u0437\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430.<\/p>\n<pre><code class=\"swift\">public var body: some View {     List {         ForEach(state.items, id: \\.title) { item in             Button(action: {                 detailSelected = item.title             }, label: {                 HStack {                     Text(item.title)                     Spacer()                     Button(action: {                         output?.removeItem(item.title)                     }, label: {                         Image(systemName: \"minus\")                     })                     .disabled(!item.canRemoveFromCart)                     Button(action: {                         output?.addItem(item.title)                     }, label: {                         Image(systemName: \"plus\")                     })                 }             })         }     }.toolbar {         Button(action: {             navigationState.push(destination: .cart)         }, label: {             Image(systemName: \"cart\")             Text(\"\\(state.cart.count)\")         })         .disabled(state.cart.isEmpty)     } }<\/code><\/pre>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0434\u043b\u044f \u044d\u043a\u0440\u0430\u043d\u0430 ViewState. \u041e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0432\u0435\u0440\u0441\u0442\u0430\u0442\u044c \u0435\u0433\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e SwiftUI.\u00a0<\/p>\n<p>\u041a\u0441\u0442\u0430\u0442\u0438, binding \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0434\u0432\u0443\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u044e\u044e \u0441\u0432\u044f\u0437\u044c \u2014 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0447\u0435\u0441\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u043d\u043e \u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0438\u0445.\u00a0<\/p>\n<p>\u0421 ViewState \u044d\u0442\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u0430, \u043d\u043e \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0435\u0439 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f. <\/p>\n<pre><code class=\"swift\">\/\/ MARK: - Preview  struct CatalogView_Previews: PreviewProvider {      enum Preset: String, CaseIterable {       case cartIsEmpty       case cartIsNotEmpty     }      static var previews: some View {         snapshots.previews     }      static var snapshots: PreviewSnapshots&lt;Preset&gt; {       return PreviewSnapshots(states: Preset.allCases,                               name: \\.rawValue,                               configure: { preset in         switch preset {         case .cartIsEmpty:             CatalogView(state: .constant(.init(items: [                     .init(title: \"Item 1\", canRemoveFromCart: false),                     .init(title: \"Item 2\", canRemoveFromCart: false),                     .init(title: \"Item 3\", canRemoveFromCart: false)             ], cart: .init(count: 0))),                         navigationState: .constant(.initial),                         detailSelected: .constant(nil))         case .cartIsNotEmpty:             CatalogView(state: .constant(.init(items: [                     .init(title: \"Item 1\", canRemoveFromCart: true),                     .init(title: \"Item 2\", canRemoveFromCart: true),                     .init(title: \"Item 3\", canRemoveFromCart: true)             ], cart: .init(count: 3))),                         navigationState: .constant(.initial),                         detailSelected: .constant(nil))         }     })     } }<\/code><\/pre>\n<p>Unit \u043d\u0430 binding \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043b\u0435\u0433\u043a\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c View \u0434\u043b\u044f preview. \u0410 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 <a href=\"https:\/\/github.com\/doordash-oss\/swiftui-preview-snapshots\"><u>PreviewSnapshots<\/u><\/a> \u0434\u043b\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/github.com\/pointfreeco\/swift-snapshot-testing?tab=readme-ov-file\"><u>SnapshotTesting<\/u><\/a> \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0438 preview \u0432 snapshot-\u0442\u0435\u0441\u0442\u0430\u0445.<\/p>\n<pre><code class=\"swift\">public var body: some View {      NavigationStack(path: $navigationState.navigationPath) {         CatalogView(state: $catalogStateHolder.state,                     navigationState: $navigationState,                     detailSelected: $detailSelected)         .navigationTitle(\"Items\")         .navigationDestination(for: VSURFNavigationState.Destination.self) { destination in             switch destination {             case .cart:                     CartView(state: $cartStateHolder.state,                           navigationState: $navigationState)                     .navigationTitle(\"Cart\")             }         }     }     .sheet(item: $detailSelected) { item in         DetailViewGroup(item: item, cartUnit: cartUnit)     }     .weakReference(cartStateHolder, as: CartViewOutput.self)     .weakReference(catalogStateHolder, as: CatalogViewOutput.self)  }<\/code><\/pre>\n<p>\u0412\u0435\u0440\u043d\u0451\u043c\u0441\u044f \u0432 ViewGroup \u0438 \u043f\u043e\u043a\u0430\u0436\u0435\u043c, \u043a\u0430\u043a \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f.\u00a0<\/p>\n<p>\u041f\u0440\u0435\u0437\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0447\u0435\u0440\u0435\u0437 sheet \u0441\u043c\u0435\u043d\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u0430 \u043b\u0435\u0433\u043a\u043e \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0430 popover, fullscreen \u0438\u043b\u0438 \u043a\u0430\u0441\u0442\u043e\u043c. NavigationStack \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u043f\u0443\u0448\u0438\u0442\u044c \u044d\u043a\u0440\u0430\u043d\u044b \u0441 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u043c NavigationBar.<\/p>\n<pre><code class=\"swift\">@NavigationState struct VSURFNavigationState {      enum Destination: Hashable, CaseIterable {       case cart     }  }<\/code><\/pre>\n<p>NavigationPath \u0434\u043b\u044f NavigationStack \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 NavigationState, \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043c\u0430\u0433\u0438\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0441\u043a\u0440\u044b\u0442\u0430 \u043f\u043e\u0434 \u043c\u0430\u043a\u0440\u043e\u0441\u043e\u043c.\u00a0<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c, \u043f\u0440\u0438\u0432\u044b\u043a\u0448\u0438\u043c \u043a UIKit-\u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438, \u0431\u044b\u043b\u043e \u043f\u0440\u043e\u0449\u0435 \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0434 SwiftUI, \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0435 \u043f\u043e UINavigationController \u043c\u0435\u0442\u043e\u0434\u044b: push, pop, popToRoot.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/64c\/169\/eba\/64c169eba76994a355fb67d6bbe4dac6.png\" alt=\"\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043c\u0430\u043a\u0440\u043e\u0441\u0430 NavigationState\" title=\"\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043c\u0430\u043a\u0440\u043e\u0441\u0430 NavigationState\" width=\"688\" height=\"589\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/64c\/169\/eba\/64c169eba76994a355fb67d6bbe4dac6.png\"\/><\/p>\n<div><figcaption>\u0420\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043c\u0430\u043a\u0440\u043e\u0441\u0430 NavigationState<\/figcaption><\/div>\n<\/figure>\n<p>\u0423 \u0432\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0447\u0438\u0442\u0430\u0442\u0435\u043b\u044f \u0435\u0449\u0451 \u043e\u0441\u0442\u0430\u043d\u0443\u0442\u0441\u044f \u0432\u043e\u043f\u0440\u043e\u0441\u044b \u043f\u0440\u043e weakReference \u0438\u043b\u0438 \u043f\u0440\u043e facade. \u041d\u043e \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u0440\u043e \u0432\u0441\u0435 \u0430\u0441\u043f\u0435\u043a\u0442\u044b \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u0432 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 <a href=\"https:\/\/github.com\/surfstudio\/vsurf-demo\"><u>\u0434\u0435\u043c\u043e-\u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c<\/u><\/a> \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c DocC <a href=\"https:\/\/surfstudio.github.io\/vsurf-demo\/tutorials\/demo\/\"><u>\u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u044b<\/u><\/a> \u0438 <a href=\"https:\/\/surfstudio.github.io\/vsurf-demo\/documentation\/demo\/overview\"><u>\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e<\/u><\/a>.\u00a0<\/p>\n<p>\u0421\u043a\u0430\u0436\u0435\u043c \u0447\u0435\u0441\u0442\u043d\u043e, \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 \u0441\u0442\u0430\u0434\u0438\u0438 \u043e\u0431\u043a\u0430\u0442\u043a\u0438 \u0438 \u043f\u043e\u043a\u0430 \u043d\u0435 \u043d\u0430\u0448\u0451\u043b \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043d\u0438 \u043d\u0430 \u043e\u0434\u043d\u043e\u043c \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d-\u043f\u0440\u043e\u0435\u043a\u0442\u0435. \u041d\u043e \u043c\u044b \u0432\u043e\u0437\u043b\u0430\u0433\u0430\u0435\u043c \u043d\u0430 \u043d\u0435\u0433\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u043d\u0430\u0434\u0435\u0436\u0434\u044b.<\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u041a\u043e\u043c\u0443-\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043c\u044b \u0438\u0437\u043e\u0431\u0440\u0435\u0442\u0430\u0435\u043c \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434. \u041a\u0442\u043e-\u0442\u043e \u0443\u043f\u0440\u0435\u043a\u043d\u0435\u0442 \u043d\u0430\u0441 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043c\u044b \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043d\u0430\u043a\u043e\u0441\u044f\u0447\u0438\u043b\u0438 \u0441 \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440, \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0435\u0451 \u0432 \u043f\u043b\u043e\u0445\u043e\u043c \u0441\u0432\u0435\u0442\u0435. \u0414\u0440\u0443\u0433\u043e\u0439 \u0441\u043a\u0430\u0436\u0435\u0442, \u0447\u0442\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u043d\u0443\u0436\u043d\u0430.<\/p>\n<p>\u041d\u043e \u043c\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b \u0432 \u0441\u0432\u043e\u0438\u0445 \u0441\u0438\u043b\u0430\u0445. \u0412\u0435\u0434\u044c \u0443 \u043d\u0430\u0441 \u0443\u0436\u0435 \u0431\u044b\u043b\u0430 <a href=\"https:\/\/surfstudio.github.io\/Surf-iOS-Developers\/ru\/Docs\/tech-stack\/architectures\/Surf_MVP.html\"><u>SurfMVP<\/u><\/a>, \u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0438\u0448\u0435\u043b \u0447\u0430\u0441 VSURF. \u0422\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 \u043d\u0435 \u0441\u0442\u043e\u044f\u0442 \u043d\u0430 \u043c\u0435\u0441\u0442\u0435, \u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u043f\u0440\u0435\u0436\u043d\u0438\u043c\u0438. \u0417\u0430\u043a\u0430\u0437\u0447\u0438\u043a\u0443 \u043d\u0443\u0436\u043d\u0430 \u00ab\u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u00bb. \u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442 \u00ab\u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443\u00bb \u043a\u0430\u043a \u043f\u0430\u0437\u043b. \u0410 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u043e\u0440 \u043f\u0440\u043e\u0434\u0443\u043c\u044b\u0432\u0430\u0435\u0442 \u0434\u0435\u0442\u0430\u043b\u0438 \u044d\u0442\u043e\u0433\u043e \u043f\u0430\u0437\u043b\u0430.<\/p>\n<blockquote>\n<p>\u0411\u043e\u043b\u044c\u0448\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0433\u043e \u043f\u0440\u043e \u043d\u0430\u0442\u0438\u0432\u043d\u0443\u044e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u2014 \u0432 Telegram-\u043a\u0430\u043d\u0430\u043b\u0435 <strong>Surf Tech Team<\/strong>.\u00a0<\/p>\n<p>\u041a\u0435\u0439\u0441\u044b, \u043b\u0443\u0447\u0448\u0438\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0438, \u043d\u043e\u0432\u043e\u0441\u0442\u0438 \u0438 \u0432\u0430\u043a\u0430\u043d\u0441\u0438\u0438 \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 Android Surf \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435.\u00a0<a href=\"https:\/\/t.me\/+7PPioeLRsg84Mjky\"><strong>\u041f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u044f\u0439\u0442\u0435\u0441\u044c<\/strong><\/a>!<\/p>\n<\/blockquote>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/874958\/\"> https:\/\/habr.com\/ru\/articles\/874958\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><strong>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0441 \u0432\u0430\u043c\u0438 \u041d\u0438\u043a\u0438\u0442\u0430, iOS Team Lead \u0432 Surf. \u041d\u0438\u043a\u0438\u0442\u0430 \u043e\u0431\u044a\u044f\u0441\u043d\u0438\u0442, \u043f\u043e\u0447\u0435\u043c\u0443 \u043c\u044b \u0432 Surf \u0440\u0435\u0448\u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0430 SwiftUI.\u00a0<\/strong><\/p>\n<p>SwiftUI \u0444\u0443\u043d\u0434\u0430\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 UIKit. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u043d \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0441\u0432\u043e\u0435\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u043a \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435. \u0412\u0441\u0435\u043c \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0435 MVP, MVVM, \u043d\u0430\u0448\u0430 <a href=\"https:\/\/surfstudio.github.io\/Surf-iOS-Developers\/ru\/Docs\/tech-stack\/architectures\/Surf_MVP.html\"><u>SurfMVP<\/u><\/a> \u0438 \u043f\u0440\u043e\u0447\u0438\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u044b \u0432 \u0447\u0438\u0441\u0442\u043e\u043c \u0432\u0438\u0434\u0435 \u043d\u0435 \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u043f\u043e\u0434 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 SwiftUI.\u00a0<\/p>\n<h3>\u0417\u0430\u0447\u0435\u043c \u043d\u0443\u0436\u043d\u0430 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430<\/h3>\n<h4>\u0427\u0442\u043e \u044d\u0442\u043e \u0442\u0430\u043a\u043e\u0435<\/h4>\n<p>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u2014 \u044d\u0442\u043e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438. \u0421\u043e\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u043e\u0432 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u044b\u0435 \u0432 \u043f\u043b\u0430\u043d\u0435 \u043e\u0446\u0435\u043d\u043a\u0438 \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u044b\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b. \u0422\u0430\u043a\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043b\u0435\u0433\u043a\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0438 \u043e\u0446\u0435\u043d\u0438\u0432\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0437\u043d\u0430\u043a\u043e\u043c \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043e\u0439.\u00a0<\/p>\n<p>\u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043d\u0435\u0442. \u0423 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u0432 \u0435\u0441\u0442\u044c \u0438 \u043f\u043b\u044e\u0441\u044b, \u0438 \u043c\u0438\u043d\u0443\u0441\u044b. \u041d\u043e \u0443 \u0432\u0441\u0435\u0445 \u043d\u0438\u0445 \u0435\u0441\u0442\u044c \u043a\u043e\u0435-\u0447\u0442\u043e \u043e\u0431\u0449\u0435\u0435. \u0412 \u043e\u0441\u043d\u043e\u0432\u0435 \u043b\u044e\u0431\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043b\u0435\u0436\u0430\u0442 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u041e\u041e\u041f (\u043e\u0431\u044a\u0435\u043a\u0442\u043d\u043e \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f). \u041a\u0430\u043a\u0438\u0435-\u0442\u043e \u0438\u0437 \u043d\u0438\u0445 \u0441\u043e\u0431\u043b\u044e\u0434\u0430\u044e\u0442\u0441\u044f \u0432 \u043c\u0435\u043d\u044c\u0448\u0435\u0439 \u0441\u0442\u0435\u043f\u0435\u043d\u0438, \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u2014 \u0432 \u0431\u043e\u043b\u044c\u0448\u0435\u0439. \u041d\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u0440\u043e\u0436\u0434\u0430\u044e\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0435, \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043d\u043e \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b. \u0422\u0430 \u0441\u0430\u043c\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430.\u00a0<\/p>\n<p>\u041d\u0430\u0441 \u0436\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0442 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439:<\/p>\n<ul>\n<li>\n<p>MVC \u2014 Model View Controller, \u043e\u043d \u0436\u0435 Massive ViewController;<\/p>\n<\/li>\n<li>\n<p>MVP \u2014 Model View Presenter;<\/p>\n<\/li>\n<li>\n<p>MVVM \u2014 Model View ViewModel;<\/p>\n<\/li>\n<li>\n<p>VIPER \u2014 View Interactor Presenter Entity Router.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><\/figure>\n<figure class=\"full-width\"><\/figure>\n<h4>\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0438 \u0432\u044b\u0431\u043e\u0440<\/h4>\n<p>\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0440\u0430\u0437\u043d\u044b\u0445 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u0441\u0432\u044f\u0437\u044f\u043c\u0438 \u0434\u0440\u0443\u0433 \u0441 \u0434\u0440\u0443\u0433\u043e\u043c. \u041c\u043e\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u2014 \u044d\u0442\u043e \u0434\u0435\u0442\u0430\u043b\u044c \u043f\u0430\u0437\u043b\u0430. \u0410 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440 \u0441\u0432\u044f\u0437\u0435\u0439 \u0441 \u0441\u043e\u0441\u0435\u0434\u043d\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0432\u044b\u043f\u0443\u043a\u043b\u043e\u0441\u0442\u0438 \u0438 <s>\u0432\u043f\u0443\u043a\u043b\u043e\u0441\u0442\u0438<\/s> \u0432\u044b\u0435\u043c\u043a\u0438 \u044d\u0442\u043e\u0439 \u0434\u0435\u0442\u0430\u043b\u0438.\u00a0<\/p>\n<p>\u0412\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u0432\u044b\u0448\u0435 \u043e\u0431\u043b\u0430\u0434\u0430\u044e\u0442 \u043e\u0431\u0449\u0438\u043c\u0438 \u0442\u0438\u043f\u0430\u043c\u0438 \u0434\u0435\u0442\u0430\u043b\u0435\u0439: View \u0438 Model. \u0422\u0430\u043a, View \u2014 \u044d\u0442\u043e UI, \u0430 Model \u2014 \u044d\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u043b\u0438 \u00ab\u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430\u00bb.<\/p>\n<p>\u041f\u0440\u0438 \u0432\u044b\u0431\u043e\u0440\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u043e\u0440\u044b \u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u043e\u043f\u0438\u0440\u0430\u044e\u0442\u0441\u044f \u043d\u0430 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0444\u0430\u043a\u0442\u043e\u0440\u043e\u0432: \u043e\u0442 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 (\u043f\u0435\u0442-\u043f\u0440\u043e\u0435\u043a\u0442 \u2014 \u043d\u0435\u0442 \u0441\u043c\u044b\u0441\u043b\u0430 \u0437\u0430\u043b\u0435\u0437\u0430\u0442\u044c \u0432 VIPER, \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u2014 \u043e\u0433\u0440\u0435\u0431\u0451\u043c \u0441 MVC) \u0434\u043e \u043b\u0438\u0447\u043d\u043e\u0433\u043e \u043e\u043f\u044b\u0442\u0430.\u00a0<\/p>\n<p>\u0415\u0441\u043b\u0438 \u043e\u0431\u043e\u0431\u0449\u0438\u0442\u044c, \u0442\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u0432\u044b\u0431\u0438\u0440\u0430\u044e\u0442 \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0441\u0442\u0435\u043a\u0430 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, MVVM \u0447\u0430\u0441\u0442\u043e \u0438\u0434\u0435\u0442 \u0432 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u0435 \u0441 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u0449\u0438\u043d\u043e\u0439, \u0431\u0443\u0434\u044c \u0442\u043e Combine \u0438\u043b\u0438 RxSwift. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c VIPER \u043d\u0435 \u0441\u043e\u0447\u0435\u0442\u0430\u0435\u0442\u0441\u044f \u0441 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u043c\u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u043c\u0438.\u00a0<\/p>\n<p>\u0414\u043e \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u044f SwiftUI \u0432\u044b\u0431\u043e\u0440 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043e\u0441\u043d\u043e\u0432\u044b\u0432\u0430\u043b\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u043d\u0430 \u0441\u0442\u0435\u043a\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f \u0438\u043b\u0438 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438. \u0412\u0451\u0440\u0441\u0442\u043a\u0430 \u043d\u0430 UIKit \u043d\u0435 \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 \u043f\u0440\u0438 \u0432\u044b\u0431\u043e\u0440\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b. View \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f UI \u0438 \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u0438.<\/p>\n<h4>\u0427\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c \u0441 \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u0435\u043c SwiftUI\u00a0<\/h4>\n<p>Conditional Views \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0432\u0435\u0440\u0441\u0442\u0430\u0442\u044c \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e UI, \u0430 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u043d\u044b\u0439 UI \u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u043d\u0435\u044f\u0432\u043d\u044b\u043c\u0438 State.\u00a0<\/p>\n<p>Published-\u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c State \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e, \u0438 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e, \u043d\u043e \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 Combine \u0434\u0435\u043b\u0430\u0442\u044c \u0443\u0434\u043e\u0431\u043d\u0435\u0435.\u00a0<\/p>\n<p>\u0412\u0441\u0451 \u044d\u0442\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u044e\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e View \u0438, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u0437\u0430\u0442\u0440\u0443\u0434\u043d\u044f\u044e\u0442 \u0432\u044b\u0431\u043e\u0440 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b.<\/p>\n<p>SwiftUI-View \u2014 \u044d\u0442\u043e \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e UI, \u044d\u0442\u043e View-\u0448\u0432\u0435\u0439\u0446\u0430\u0440\u0441\u043a\u0438\u0439 \u043d\u043e\u0436. \u0418 \u043c\u043d\u043e\u0433\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0437\u0434\u0435\u0441\u044c \u043d\u0430\u0440\u0443\u0448\u0430\u0435\u0442 \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u0435\u0434\u0438\u043d\u043e\u0439 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043b\u0435\u0436\u0438\u0442 \u0432 \u043e\u0441\u043d\u043e\u0432\u0435 \u043c\u043d\u043e\u0433\u0438\u0445 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440. \u042d\u0442\u0430 \u043d\u043e\u0432\u0430\u044f \u0434\u0435\u0442\u0430\u043b\u044c \u0438\u043c\u0435\u0435\u0442 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0432\u044b\u0435\u043c\u043e\u043a \u0438 \u0432\u044b\u0441\u0442\u0443\u043f\u043e\u0432. \u041f\u0430\u0437\u043b \u043d\u0435 \u0441\u0445\u043e\u0434\u0438\u0442\u0441\u044f.<\/p>\n<p>\u0418 \u0432\u043e\u0442 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0438\u0448\u043b\u0430 \u043f\u043e\u0440\u0430 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438 \u0441 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430\u043c\u0438.<\/p>\n<h4>MVC<\/h4>\n<p>\u0412 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435 MVC \u0432\u0441\u0435\u0433\u043e \u0434\u0432\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430: View \u0438 Model. \u041c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0434\u043b\u044f \u043c\u043d\u043e\u0433\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 SwiftUI View \u044d\u0442\u0430 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0431\u043e\u043b\u044c\u0448\u0435. \u041d\u043e \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c, \u0447\u0442\u043e \u0432 \u043a\u0440\u0443\u043f\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0442\u0430\u043a\u0443\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u0441\u043b\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c. \u0427\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u043b\u043e\u0433\u0438\u043a\u0438 \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f \u0432\u043e View, \u0442\u0435\u043c \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0435\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c. \u0418 \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0442\u044c \u0443 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u043d\u0430\u0434 \u043e\u0434\u043d\u0438\u043c \u044d\u043a\u0440\u0430\u043d\u043e\u043c. \u0410 \u0432 \u043e\u0441\u043e\u0431\u043e \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u043c\u043e\u0436\u043d\u043e \u0434\u0430\u0436\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443:<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u041a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0443 \u0441\u0442\u0430\u043b\u043e \u0441\u043b\u043e\u0436\u043d\u043e<\/figcaption><\/div>\n<\/figure>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c MVC \u0432\u043c\u0435\u0441\u0442\u0435 \u0441\u043e SwiftUI \u043c\u043e\u0436\u043d\u043e \u0434\u043b\u044f \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u043b\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0432 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u0435. \u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 conditional views \u0443\u043b\u0443\u0447\u0448\u0438\u0442 \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c \u0432\u0435\u0442\u0432\u043b\u0435\u043d\u0438\u0439, \u0435\u0441\u043b\u0438 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0442\u044c \u0441 \u0442\u043e\u0439 \u0436\u0435 \u043b\u043e\u0433\u0438\u043a\u043e\u0439 \u0432 UIKit-\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0435.<\/p>\n<p>\u0421\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0430\u0437\u043b \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c MVC \u0438 SwiftUI \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u0414\u0435\u0442\u0430\u043b\u0438 \u0442\u0430\u043a\u0438\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u0435, \u0447\u0442\u043e \u044d\u0442\u043e \u043f\u0430\u0437\u043b \u0434\u043b\u044f \u043c\u043b\u0430\u0434\u0435\u043d\u0446\u0435\u0432. \u0421\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u043c \u0440\u0435\u0431\u044f\u0442\u0430\u043c \u0447\u0430\u0441\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0441\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u0437\u0430 \u043e\u0434\u043d\u0443 \u0434\u0435\u0442\u0430\u043b\u044c.<\/p>\n<h4>MVP<\/h4>\n<p>\u041e\u0442\u043b\u0438\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c MVP \u2014 passive view. Presenter \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f. \u041e\u043d \u043e\u0442\u0434\u0430\u0435\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432 View \u0447\u0435\u0440\u0435\u0437 \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c View \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e Presenter. <\/p>\n<p>\u0412 SwiftUI \u0441\u043b\u043e\u0436\u043d\u043e \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432\u0441\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 View \u2014 \u044d\u0442\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b. \u0418\u0437-\u0437\u0430 \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 View \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c\u0438 \u0438\u0437 presenter-\u0430.\u00a0<\/p>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 State \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0438\u043b\u0438 StateObject. \u0415\u0433\u043e \u043a\u0430\u043a \u0440\u0430\u0437 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u043e\u043c \u0438 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e presenter. \u041d\u043e \u0442\u043e\u0433\u0434\u0430 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c Model View State Presenter \u2014 4 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0432\u043c\u0435\u0441\u0442\u043e 3, \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0435.\u00a0<\/p>\n<p>\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u0441\u0432\u044f\u0437\u044c \u043c\u0435\u0436\u0434\u0443 \u0441\u0442\u0430\u0440\u044b\u043c\u0438 \u0438 \u043d\u043e\u0432\u044b\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u043c \u2014 \u0438\u043d\u0430\u0447\u0435 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435 \u0435\u0434\u0438\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u043e.\u00a0<\/p>\n<pre><code class=\"swift\">public struct MVPViewGroup: View {     private static let model: Model = .init()      @StateObject private var catalogPresenter: CatalogPresenter      \/\/ MARK: - State     @State private var isCartShown = false     @State private var detailSelected: Item?      \/\/ MARK: - Init      public init() {     self._catalogPresenter = .init(wrappedValue: .init(model: MVPViewGroup.model))     }      \/\/ MARK: - View      public var body: some View {     NavigationStack {         CatalogView(items: $catalogPresenter.allItems,                     cart: $catalogPresenter.cartItems,                     output: catalogPresenter)         .navigationTitle(\"Items\")     }       .onAppear {         catalogPresenter.onShowDetail = { item in             self.detailSelected = item         }         catalogPresenter.onCartShow = {             self.isCartShown = true         }       } }<\/code><\/pre>\n<p>\u0421\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0430\u0437\u043b \u043f\u043e \u043a\u0430\u043d\u043e\u043d\u0438\u0447\u043d\u043e\u043c\u0443 MVP \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c SwiftUI \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f. \u0414\u0435\u0442\u0430\u043b\u0438 \u043d\u0435 \u0441\u043a\u043b\u0435\u044f\u0442\u0441\u044f.<\/p>\n<h4>MVVM<\/h4>\n<p>\u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 MVP \u0432 MVVM \u0438\u043c\u0435\u043d\u043d\u043e View \u0434\u0435\u0440\u0436\u0438\u0442 ViewModel. \u041e\u043d \u0436\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043e\u0442 \u043d\u0435\u0451 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043e\u0431\u0440\u0430\u0442\u043d\u043e.\u00a0<\/p>\n<p>\u041d\u043e \u043a\u0430\u043a \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0432 View \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e ViewModel? \u041f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f ViewModel \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440 ViewB \u0438\u0437 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 ViewA, \u043c\u044b \u043d\u0430\u0442\u043a\u043d\u0451\u043c\u0441\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u0440\u0438\u0441\u043e\u0432\u043a\u0435 ViewA \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 ViewModel. \u0410 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u0435\u0442\u0441\u044f.\u00a0<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u0438\u0442\u044c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 ViewModel \u043a \u0436\u0438\u0437\u043d\u0435\u043d\u043d\u043e\u043c\u0443 \u0446\u0438\u043a\u043b\u0443 View \u0438 \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u043e\u0442 \u0446\u0438\u043a\u043b\u043e\u0432 \u043f\u0435\u0440\u0435\u0440\u0438\u0441\u043e\u0432\u043a\u0438 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 View, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c ViewModel \u0447\u0435\u0440\u0435\u0437 EnvironmentObject. <\/p>\n<pre><code class=\"swift\">public struct MVVMViewGroup: View {     \/\/ MARK: - Properties     private static let model: Model = .init()      private var catalogVM: CatalogViewModel      \/\/ MARK: - State      @State private var isCartShown = false     @State private var detailSelected: Item?      \/\/ MARK: - Init      public init() {     self.catalogVM = .init(model: MVVMViewGroup.model)     }      \/\/ MARK: - View      public var body: some View {     NavigationStack {         CatalogView()             .environmentObject(catalogVM)             .navigationTitle(\"Items\")     }     .onAppear {         catalogVM.onShowDetail = { item in             self.detailSelected = item         }         catalogVM.onCartShow = {             self.isCartShown = true         }     }     }  }<\/code><\/pre>\n<p>\u041d\u043e \u0438 \u0443 \u044d\u0442\u043e\u0433\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0435\u0441\u0442\u044c \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u0438:<\/p>\n<ul>\n<li>\n<p>\u0441\u0432\u044f\u0437\u044c \u043c\u0435\u0436\u0434\u0443 ViewModel \u0438 View \u043d\u0435\u044f\u0432\u043d\u0430\u044f;<\/p>\n<\/li>\n<li>\n<p>\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 ViewModel \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439, \u0438\u043d\u0430\u0447\u0435 \u2014 \u043a\u0440\u0430\u0448;<\/p>\n<\/li>\n<li>\n<p>\u0437\u0430\u043a\u0440\u044b\u0442\u044c\u00a0 ViewModel \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u043c \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f. \u0410 \u044d\u0442\u043e \u0441\u043d\u0438\u0436\u0430\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u044d\u043a\u0440\u0430\u043d\u0430.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u044b\u0445\u043e\u0434\u0438\u0442, \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0430\u0437\u043b \u0438\u0437 MVVM \u0438 SwiftUI \u043c\u043e\u0436\u043d\u043e \u2014 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u043d\u0438\u043a\u043e\u0432.<\/p>\n<h4>VIPER<\/h4>\n<p>VIPER \u2014 \u044d\u0442\u043e \u044d\u0432\u043e\u043b\u044e\u0446\u0438\u044f MVP. \u041d\u0435 \u0441\u0430\u043c\u0430\u044f \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430. \u041e\u0431\u0438\u043b\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043e\u0431\u043c\u0430\u043d\u0447\u0438\u0432\u043e\u0435 \u043e\u0449\u0443\u0449\u0435\u043d\u0438\u0435 \u0435\u0451 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438.\u00a0<\/p>\n<p>\u0421 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b, \u043e\u0431\u0438\u043b\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0431\u043e\u043b\u0435\u0435 \u0436\u0451\u0441\u0442\u043a\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0437\u043e\u043d\u044b \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0438 \u0438\u0445 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435. \u0410 \u044d\u0442\u043e \u043f\u0440\u0438\u0431\u043b\u0438\u0436\u0430\u0435\u0442 \u043d\u0430\u0441 \u043a clean code \u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0441\u043e\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044e \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u043e\u0432 \u041e\u041e\u041f.<\/p>\n<p>\u041d\u043e \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0430\u0437\u043b \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u043f\u043e \u0442\u043e\u0439 \u0436\u0435 \u043f\u0440\u0438\u0447\u0438\u043d\u0435, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0435 \u0432\u044b\u0448\u043b\u043e \u0441 MVP. \u0412\u0434\u043e\u0431\u0430\u0432\u043e\u043a, \u043c\u044b \u0442\u0443\u0442 \u0436\u0435 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u043c\u0441\u044f \u0441 \u0442\u0440\u0443\u0434\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u0441 \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0438\u0435\u043c \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u0432 Router. \u0412\u0441\u0451 \u0438\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0432 SwiftUI \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0435\u0439 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u043e \u043a View.<\/p>\n<pre><code class=\"swift\">public struct VIPERViewGroup: View {      \/\/ MARK: - Properties      private static let model: Model = .init()      \/\/ MARK: - Init      public init() {}      \/\/ MARK: - View      public var body: some View {     NavigationStack {         CatalogView(presenter:                 .init(interactor: .init(model: VIPERViewGroup.model),                       router: .init()                 )         )         .navigationTitle(\"Items\")     }     }  }<\/code><\/pre>\n<h4>\u0420\u0435\u0446\u0435\u043f\u0442 \u0443\u0441\u043f\u0435\u0445\u0430<\/h4>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u0430\u0437\u043b \u0438\u0437 SwiftUI \u0438 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u043d\u0443\u0436\u043d\u043e \u0443\u0447\u0435\u0441\u0442\u044c \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043d\u043e\u0432\u043e\u0439 \u043c\u043d\u043e\u0433\u043e\u043f\u0440\u043e\u0444\u0438\u043b\u044c\u043d\u043e\u0439 \u0434\u0435\u0442\u0430\u043b\u044c\u043a\u0438 SwiftUI View:<\/p>\n<ul>\n<li>\n<p>View \u2014 \u044d\u0442\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b;<\/p>\n<\/li>\n<li>\n<p>View \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 State-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435;<\/p>\n<\/li>\n<li>\n<p>View \u043c\u043e\u0433\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 State;<\/p>\n<\/li>\n<li>\n<p>View \u0438\u043c\u0435\u044e\u0442 DI \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 Environment.<\/p>\n<\/li>\n<\/ul>\n<p>\u042d\u0442\u0438 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u043d\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0438 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438. \u0412 \u043e\u0431\u0449\u0435\u043c, \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u043e\u0432\u0443\u044e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443, \u0431\u0435\u0437 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u043e\u0432 \u0430\u0434\u0430\u043f\u0442\u0430\u0446\u0438\u0439.\u00a0<\/p>\n<h3>VSURF<\/h3>\n<p>\u0414\u0430, \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\u0430 \u043d\u0430\u0448\u0435\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043d\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438. \u041d\u043e \u043d\u0435 \u0441\u043f\u0435\u0448\u0438\u0442\u0435 \u0433\u043d\u0435\u0432\u0430\u0442\u044c\u0441\u044f, \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0430 \u043d\u0435 \u043b\u0438\u0448\u0435\u043d\u0430 \u0441\u043c\u044b\u0441\u043b\u0430:<\/p>\n<ul>\n<li>\n<p><strong>V<\/strong>iew<\/p>\n<\/li>\n<li>\n<p>view <strong>S<\/strong>tate<\/p>\n<\/li>\n<li>\n<p>business <strong>U<\/strong>nit<\/p>\n<\/li>\n<li>\n<p>navigation <strong>R<\/strong>outing<\/p>\n<\/li>\n<li>\n<p>singleton services <strong>F<\/strong>actory<\/p>\n<\/li>\n<\/ul>\n<p><strong>View<\/strong> \u2014 \u043f\u0435\u0440\u0432\u0430\u044f \u0431\u0443\u043a\u0432\u0430 \u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u0438\u0437\u0430\u0439\u043d-\u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0438 <a href=\"https:\/\/github.com\/surfstudio\/SurfPlaybook\"><u>Playbook<\/u><\/a>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0447\u0430\u0441\u0442\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u0441 UI.<\/p>\n<p><strong>ViewState<\/strong> \u2014 \u043c\u044b \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f SwiftUI View \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 State \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445. \u0421\u0442\u0440\u043e\u0438\u043c binding \u0441 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u043e\u0439 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0445 Published \u0441\u0432\u043e\u0439\u0441\u0442\u0432.<\/p>\n<p><strong>Business Unit<\/strong> \u2014 \u043e\u0442\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043b\u043e\u0433\u0438\u043a\u0438 \u043e\u0442 View. \u0412 \u044d\u0442\u043e\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432: \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u043d\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u0440\u0437\u0438\u043d\u044b \u0438 \u0434\u0440\u0443\u0433\u0438\u0445. \u0422\u043e \u0435\u0441\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430. \u0418\u043d\u044b\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438, \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u043d\u044b\u0439 flow.<\/p>\n<p><strong>Navigation Routing<\/strong> \u2014 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u043c\u0435\u0436\u0434\u0443 \u044d\u043a\u0440\u0430\u043d\u0430\u043c\u0438, \u0432\u0435\u0434\u044c \u0432 SwiftUI \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f \u0437\u0430\u0432\u044f\u0437\u0430\u043d\u0430 \u043d\u0430 Binding. \u041f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u043c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u043e\u0440\u043e\u043c \u0442\u0443\u0442 \u043d\u0435 \u043e\u0431\u043e\u0439\u0442\u0438\u0441\u044c.<\/p>\n<p><strong>SIngleton Services Factory<\/strong> \u2014 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432. Network-\u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0441 \u0411\u0414 \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u2014 \u044d\u0442\u043e \u0441\u0438\u043d\u0433\u043b\u0442\u043e\u043d\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0440\u043e\u0436\u0434\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0444\u0430\u0431\u0440\u0438\u043a\u0438.\u00a0<\/p>\n<p>\u041f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0435 \u0430\u0441\u043f\u0435\u043a\u0442\u044b \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0437\u0432\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c\u0438 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430\u043c\u0438 \u043d\u0430\u0448\u0435\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b. \u0415\u0441\u043b\u0438 \u043f\u043e\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0433\u043b\u0443\u0431\u0436\u0435, \u0431\u0443\u0434\u0435\u043c \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u043e \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u0443\u0436\u0435 \u043f\u043e\u0442\u043e\u043c \u2014 \u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u0445.<\/p>\n<h4>\u041c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0441\u0442\u044c<\/h4>\n<p>\u041d\u0438 \u043e\u0434\u043d\u0430 \u0438\u0437 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440 \u0438\u0437 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0430 \u043d\u0435 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0438 \u0441\u0432\u044f\u0437\u0438 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438. \u0425\u043e\u0442\u044f \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044f \u043c\u043d\u043e\u0433\u043e\u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0443\u0436\u0435 \u0434\u0430\u0432\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043d\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445.<\/p>\n<p>\u0417\u0430 \u0441\u0447\u0451\u0442 \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u0442\u043a\u043e\u0439 <\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-445600","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/445600","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=445600"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/445600\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=445600"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=445600"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=445600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}