{"id":394579,"date":"2024-06-29T11:42:56","date_gmt":"2024-06-29T11:42:56","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=394579"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=394579","title":{"rendered":"<span>Quick, Nimble. \u041d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u0441\u0442\u043e\u0432 \u0431\u043e\u043b\u044c\u0448\u0435<\/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<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/eb0\/25a\/dde\/eb025adde2114a09df6a1371ea6e0a70.jpg\" width=\"1280\" height=\"720\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/eb0\/25a\/dde\/eb025adde2114a09df6a1371ea6e0a70.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<h3>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0438 \u0437\u0430\u0447\u0435\u043c \u0438\u0445 \u043f\u0438\u0441\u0430\u0442\u044c?<\/h3>\n<p>\u041f\u0435\u0440\u0435\u0434 \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u0435\u043c \u044f \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 <a href=\"https:\/\/habr.com\/ru\/company\/cian\/blog\/567358\/\"><u>\u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435\u0439 \u0441\u0435\u0440\u0438\u0438<\/u><\/a>. \u041e\u043d\u0430 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u0447\u0435\u043c \u0445\u043e\u0440\u043e\u0448\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u043e\u0442 \u043f\u043b\u043e\u0445\u0438\u0445, \u043a\u0430\u043a\u0438\u0435 \u0432\u0438\u0434\u044b \u0442\u0435\u0441\u0442\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442 \u0438 \u0432 \u043a\u0430\u043a\u0438\u0445 \u043f\u0440\u043e\u043f\u043e\u0440\u0446\u0438\u044f\u0445 \u043e\u043d\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f. \u042d\u0442\u043e\u0442 \u0436\u0435 \u043f\u043e\u0441\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0441\u0444\u043e\u043a\u0443\u0441\u0438\u0440\u043e\u0432\u0430\u043d \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Quick \u0438 Nimble, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0442\u0435\u043e\u0440\u0438\u044f \u0438\u0437 \u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u0430\u043c \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0432 \u0441\u0435\u0440\u0438\u0438 \u043c\u044b <a href=\"https:\/\/habr.com\/ru\/company\/cian\/blog\/570988\/\"><u>\u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e UI-\u0442\u0435\u0441\u0442\u044b<\/u><\/a>.<\/p>\n<p>\u0414\u043e \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u043d\u0430\u0447\u0430\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b, \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f, \u0447\u0435\u043c \u043e\u043d\u0438 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u043e\u0442 Unit-\u0442\u0435\u0441\u0442\u043e\u0432. \u042d\u0442\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0434\u0438\u0441\u043a\u0443\u0441\u0441\u0438\u043e\u043d\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441, \u0438 \u043e\u043d \u0443\u0436\u0435 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u043b\u0441\u044f \u0432 <a href=\"https:\/\/habr.com\/ru\/company\/cian\/blog\/567358\/\"><u>\u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/u><\/a>, \u043d\u043e \u044f \u0434\u0443\u043c\u0430\u044e, \u0442\u0443\u0442 \u0441\u0442\u043e\u0438\u0442 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c\u0441\u044f. Unit-\u0442\u0435\u0441\u0442\u043e\u043c \u043c\u044b \u0441\u0447\u0438\u0442\u0430\u0435\u043c \u0442\u0435\u0441\u0442 \u043d\u0430 \u043e\u0434\u0438\u043d \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441, \u0433\u0434\u0435 \u0432\u0441\u0435 \u0435\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0437\u0430\u043a\u0440\u044b\u0442\u044b \u043c\u043e\u043a\u0430\u043c\u0438. \u0410 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u043c \u043c\u044b \u0441\u0447\u0438\u0442\u0430\u0435\u043c \u0442\u0435\u0441\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442 \u0441\u0432\u044f\u0437\u043a\u0443 \u043a\u043b\u0430\u0441\u0441\u043e\u0432 (\u043c\u043e\u0434\u0443\u043b\u044c) \u0441 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u043c\u043e\u043a\u043e\u0432. \u0418\u043d\u043e\u0433\u0434\u0430 \u0442\u0430\u043a\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u0435\u0449\u0435 \u043c\u043e\u0433\u0443\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u043c\u0438 \u0438\u043b\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u043c\u0438.<\/p>\n<p>\u041c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u043c\u043e\u0434\u0443\u043b\u044f (\u0447\u0438\u0442\u0430\u0439: \u044d\u043a\u0440\u0430\u043d\u0430) \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043c\u043e\u043a\u043e\u0432 \u0438 \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043a. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u043b\u0438\u0436\u0435 \u043a \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u043c\u043e\u0434\u0443\u043b\u044e \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435, \u0447\u0435\u043c \u0435\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u0432\u0441\u0435 \u0447\u0430\u0441\u0442\u0438 \u043c\u043e\u0434\u0443\u043b\u044f \u043f\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438. \u0412\u0435\u0434\u044c \u0435\u0441\u043b\u0438 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043a\u043b\u0430\u0441\u0441\u0430 \u043d\u0430 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u0438 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0442\u043e \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043b\u0438\u0448\u044c \u0442\u043e, \u0447\u0442\u043e \u043a\u043b\u0430\u0441\u0441 \u0445\u043e\u0440\u043e\u0448\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430\u043c\u0438, \u043d\u043e \u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043a\u043b\u0430\u0441\u0441\u0430\u043c\u0438 \u043c\u044b \u0443\u0437\u043d\u0430\u0435\u043c \u043c\u0430\u043b\u043e.<\/p>\n<p>\u0412 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u0430\u0445 \u043d\u0430\u0441 \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0438\u0441\u0442\u043e\u0440\u0438\u0439, \u0430 \u043d\u0435 \u0434\u0435\u0442\u0430\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0435 \u0447\u0430\u0441\u0442\u0438 \u043c\u043e\u0434\u0443\u043b\u044f. \u0422\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 \u0441\u0438\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u043c\u043e\u0434\u0443\u043b\u044f \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0441\u0430\u043c\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c. \u0414\u0430\u0436\u0435 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0437\u0430\u043c\u0435\u043d\u044b \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043c\u043e\u0434\u0443\u043b\u044f \u043d\u0435 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u044e\u0449\u0438\u0435 \u0435\u0433\u043e \u0442\u0435\u0441\u0442\u044b \u0441 \u043d\u0443\u043b\u044f.<\/p>\n<p>\u041f\u0440\u0438 \u0432\u044b\u0431\u043e\u0440\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438\u0441\u044c \u043d\u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u0445 Quick \u0438 Nimble, \u0438 \u043e\u043d\u0438 \u043d\u0435\u043f\u043b\u043e\u0445\u043e \u0441\u0435\u0431\u044f \u0437\u0430\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u043e\u0432\u0430\u043b\u0438. \u0421 \u0438\u0445 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0434\u043e\u0431\u043d\u043e \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u043e\u043d\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0438\u0437\u0431\u0435\u0433\u0430\u0442\u044c \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u044b\u043c\u0438 \u0438 \u0432\u044b\u0440\u0430\u0437\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438.<\/p>\n<h4>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Quick, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Nimble?<\/h4>\n<p>\u0415\u0441\u043b\u0438 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u0432\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u043e, \u0442\u043e Quick \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a, \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0449\u0438\u0439 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432, \u0430 Nimble \u2014 \u044d\u0442\u043e \u043c\u0435\u0442\u0447\u0435\u0440. \u041f\u0440\u043e\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435, \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0449\u0435\u043c \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u044d\u0442\u0438\u0445 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432. \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441 \u043a\u0430\u043b\u044c\u043a\u0443\u043b\u044f\u0442\u043e\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043c\u0435\u0435\u0442 \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0442\u044c \u0447\u0438\u0441\u043b\u0430.\u00a0<\/p>\n<pre><code class=\"swift\">override func spec() {     describe(\"\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c SumCalculator\") { \/\/ 1         var calculator: SumCalculator!         beforeEach { \/\/ 2             calculator = SumCalculator()         }         context(\"\u0415\u0441\u043b\u0438 \u0441\u043b\u043e\u0436\u0438\u0442\u044c 1 \u0438 2\") { \/\/ 3             beforeEach { \/\/ 4                 calculator.sum(1, 2)             }             it(\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0432\u0435\u043d 3\") { \/\/ 5                 expect(calculator.result).to(equal(3)) \/\/ 6             }         }         context(\"\u0415\u0441\u043b\u0438 \u0441\u043b\u043e\u0436\u0438\u0442\u044c 1 \u0438 -1\") { \/\/ 7             beforeEach { \/\/ 8                 calculator.sum(1, -1) \/\/ 9             }             it(\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0432\u0435\u043d 0\") { \/\/ 10                 expect(calculator.result) == 0 \/\/ 11             }         }     } }<\/code><\/pre>\n<p>\u0412\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f <code>expect(calculator.result).to(equal(3))<\/code> (3) \u0438 <code>expect(calculator.result) == 0 (11)<\/code> \u043e\u0442\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u043a \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0443 Nimble, \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e \u043d\u0438\u0445 \u0447\u0443\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<p>\u0410 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>describe()<\/code> (1), <code>beforeEach()<\/code> (2, 4, 8), <code>context()<\/code> (3, 7) \u0438 <code>it()<\/code> (5, 10) \u043e\u0442\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u043a \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0443 Quick. \u041e\u043d\u0438 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0431\u0430\u0437\u043e\u0439 \u044d\u0442\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430. \u0414\u0430\u043b\u0435\u0435 \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0431\u043b\u043e\u043a\u0430\u043c\u0438.<\/p>\n<p>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e \u0431\u043b\u043e\u043a\u0430\u0445<\/p>\n<ol>\n<li>\n<p><code>describe()<\/code> \u2014 \u0431\u043b\u043e\u043a, \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u0432 \u043c\u0435\u0442\u043e\u0434 <code>spec()<\/code> \u0438 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442-\u043a\u0435\u0439\u0441\u043e\u0432. \u0411\u043b\u043e\u043a\u043e\u0432 <code>describe()<\/code> \u0432\u043d\u0443\u0442\u0440\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>spec()<\/code> \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0431\u0438\u0442\u044c \u043e\u0434\u0438\u043d \u0431\u043e\u043b\u044c\u0448\u043e\u0439 <code>describe()<\/code> \u0434\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u0438 \u043a\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><code>context()<\/code> \u2014\u00a0 \u0442\u0430\u043a\u043e\u0439 \u0431\u043b\u043e\u043a, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043a\u0430\u043a\u043e\u0435-\u043b\u0438\u0431\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0438\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0438. \u0411\u043b\u043e\u043a\u0438 <code>context()<\/code> \u0432\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u0431\u043b\u043e\u043a\u0438 <code>describe()<\/code> \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0431\u043b\u043e\u043a\u0438 <code>context()<\/code>. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u044d\u0442\u0438\u0445 \u0431\u043b\u043e\u043a\u043e\u0432 \u0443\u0434\u043e\u0431\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0432 \u0432\u0438\u0434\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \u201c\u0415\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043b\u0438\u0441\u044c \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u201d \u0438\u043b\u0438 \u201c\u0415\u0441\u043b\u0438 \u0432\u0432\u0435\u043b\u0438 \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435\u201d.<\/p>\n<\/li>\n<li>\n<p><code>beforeEach()<\/code> \u2014 \u0442\u0430\u043a\u043e\u0439 \u0431\u043b\u043e\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432\u043d\u0443\u0442\u0440\u0438 <code>describe()<\/code> \u0438\u043b\u0438 <code>context()<\/code>. \u041a\u043e\u0434 \u0432\u043d\u0443\u0442\u0440\u0438 <code>beforeEach()<\/code> \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0432 \u043d\u043e\u0432\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435. \u0416\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043d\u0430\u0434 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043b\u0438\u0441\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u044d\u0442\u0438\u0445 \u0431\u043b\u043e\u043a\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><code>it()<\/code> \u2014 \u0432 \u044d\u0442\u0438\u0445 \u0431\u043b\u043e\u043a\u0430\u0445 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0432 <code>beforeEach()<\/code> \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u0440\u0438\u0432\u0435\u043b\u0438 \u043a \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c.\u00a0 \u0412 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0431\u043b\u043e\u043a\u0430 \u043e\u0431\u044b\u0447\u043d\u043e \u0432\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \u201cView \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c State\u201d \u0438\u043b\u0438 \u201c\u0412\u043e View \u0431\u044b\u043b\u0430 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0430\u201d.<\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u043e\u0440\u044f\u0434\u043e\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0431\u043b\u043e\u043a\u043e\u0432 \u043a\u043e\u0434\u0430:<\/p>\n<p>\u041f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043f\u043e\u0440\u044f\u0434\u043a\u0430 \u0432\u044b\u0437\u043e\u0432\u0430 \u0431\u043b\u043e\u043a\u043e\u0432 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043b\u044e\u0447\u043e\u043c \u043a \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044e \u0440\u0430\u0431\u043e\u0442\u044b Quick. \u0412\u0430\u0436\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c, \u0447\u0442\u043e \u0431\u043b\u043e\u043a\u0438 <code>it()<\/code> \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u0440\u0443\u0433 \u0437\u0430 \u0434\u0440\u0443\u0433\u043e\u043c. \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0431\u043b\u043e\u043a\u0430 <code>it()<\/code> \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u0437\u043e\u0432\u043e\u0432, \u0441\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0431\u043b\u043e\u043a\u043e\u0432 <code>describe()<\/code>, <code>context()<\/code> \u0438 <code>it()<\/code>. \u0412 \u044d\u0442\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u0437\u043e\u0432\u0430 \u0431\u043b\u043e\u043a\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0442\u0430\u043a\u0430\u044f: [2, 4, 5] \u0438 [2, 8, 10], \u0442.\u0435. \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e <code>it()<\/code> (5, 10). \u041d\u043e \u043d\u0438\u043a\u0430\u043a \u043d\u0435 [2, 4, 5, 8, 10].<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u0437\u0430\u0445\u043e\u0442\u0435\u043b\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043d\u0430\u0431\u043e\u0440 \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430 Quick \u0432 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0435 XCTest, \u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b\u0438 \u0431\u044b \u043e\u043d\u0438 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0441 XCTest<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">func testSum1And2() {     \/\/ Arrange     let calculator = SumCalculator()     \/\/ Act     calculator.sum(1, 2)     \/\/ Assert     XCTAssertEqual(calculator.result, 3) }      func testSum1AndMinus1() {     \/\/ Arrange     let calculator = SumCalculator()     \/\/ Act     calculator.sum(1, -1)     \/\/ Assert     XCTAssertEqual(calculator.result, 0) }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0412\u0430\u0436\u043d\u044b\u0435 \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u044f:<\/p>\n<ol>\n<li>\n<p>\u0424\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0431\u043b\u043e\u043a <code>it()<\/code> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u0430\u0436\u043d\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043a \u043c\u043e\u043c\u0435\u043d\u0442\u0443 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0431\u043b\u043e\u043a\u0430 <code>it()<\/code> \u0432\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0431\u044b\u043b\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438. <\/p>\n<\/li>\n<li>\n<p>\u0412\u0430\u0436\u043d\u043e \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u0437\u0430 \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c\u044e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0439 \u0431\u043b\u043e\u043a\u043e\u0432 <code>describe()<\/code>, <code>context()<\/code> \u0438 <code>it()<\/code>. \u0427\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c \u2014 \u043e\u0434\u043d\u0430 \u0438\u0437 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a \u0442\u0435\u0441\u0442\u0430. \u0410 \u0435\u0449\u0435 \u044d\u0442\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043f\u0440\u043e\u0449\u0435 \u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0443\u043f\u0430\u0432\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u043a\u043e\u0434 \u0432\u043d\u0435 \u0431\u043b\u043e\u043a\u043e\u0432 <code>beforeEach()<\/code> \u0438\u043b\u0438 <code>it()<\/code>. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044e \u0442\u0435\u0441\u0442\u0430. \u041d\u043e \u0432\u043d\u0435 \u044d\u0442\u0438\u0445 \u0431\u043b\u043e\u043a\u043e\u0432 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044f \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435, \u043a\u0430\u043a, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>SumCalculator<\/code> \u0438\u0437 \u0442\u0435\u0441\u0442\u0430, \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u0448\u0435. \u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u2014 \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0442\u044c \u0435\u043c\u0443 \u043d\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0431\u043b\u043e\u043a\u0430\u0445 <code>beforeEach()<\/code>. <\/p>\n<\/li>\n<li>\n<p>\u0411\u043b\u043e\u043a\u0438 \u043d\u0430 \u043e\u0434\u043d\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043d\u0435 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u0432 \u043a\u043e\u0434\u0435, \u0430 \u0432 \u0430\u043b\u0444\u0430\u0432\u0438\u0442\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u0438\u0445 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0439. \u041d\u043e \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u043d\u0430 \u044d\u0442\u043e \u0437\u0430\u0432\u044f\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f. \u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u043b\u0443\u0447\u0448\u0435 \u0441\u0447\u0438\u0442\u0430\u0442\u044c, \u0447\u0442\u043e \u043e\u043d\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435.<\/p>\n<\/li>\n<\/ol>\n<h2>\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0432 \u043f\u043e \u043f\u043e\u0432\u043e\u0434\u0443 Nimble<\/h2>\n<p>Nimble \u2014 \u044d\u0442\u043e \u043c\u0435\u0442\u0447\u0435\u0440. \u041c\u0435\u0442\u0447\u0435\u0440 \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0438 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432. \u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 Nimble \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 <code>expect(calculator.result).to(equal(3))<\/code> (6) \u0438 <code>expect(calculator.result) == 0<\/code> (11). \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 <code>==<\/code> \u0438 <code>.to(equal())<\/code> \u044d\u043a\u0432\u0438\u0432\u0430\u043b\u0435\u043d\u0442\u044b. \u041d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, <code>==<\/code> \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u0447\u0443\u0442\u044c \u043b\u0443\u0447\u0448\u0435, \u043d\u043e \u0442\u0443\u0442 \u0443\u0436\u0435 \u0434\u0435\u043b\u043e \u0432\u043a\u0443\u0441\u0430, \u043a\u0430\u0436\u0434\u044b\u0439 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442, \u0447\u0442\u043e \u0435\u043c\u0443 \u0443\u0434\u043e\u0431\u043d\u0435\u0435.<\/p>\n<p>\u0415\u0449\u0435 \u0438\u0437 \u0432\u0430\u0436\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 Nimble \u0441\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 <code>toEventually()<\/code>. \u041e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438 \u0432\u044b\u0437\u043e\u0432\u0430\u043c\u0438 \u0438 \u0436\u0434\u0430\u0442\u044c \u043c\u043e\u043c\u0435\u043d\u0442\u0430, \u043a\u043e\u0433\u0434\u0430 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u0441\u0442\u0430\u043d\u0435\u0442 \u0432\u0435\u0440\u043d\u044b\u043c. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0435\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p>\u0410 \u0435\u0449\u0435 \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c <a href=\"https:\/\/github.com\/Quick\/Nimble\"><u>\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e<\/u><\/a>, \u0442\u0430\u043c \u043c\u043d\u043e\u0433\u043e \u0432\u0441\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 Nimble \u0435\u0441\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439, \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043d\u0435\u0442\u043e\u0447\u043d\u044b\u0435 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0447\u0438\u0441\u0435\u043b \u0441 \u043f\u043b\u0430\u0432\u0430\u044e\u0449\u0435\u0439 \u0442\u043e\u0447\u043a\u043e\u0439), \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \u0438 \u043c\u043d\u043e\u0433\u043e \u0434\u0440\u0443\u0433\u043e\u0435.<\/p>\n<h2>\u0427\u0442\u043e \u0431\u0443\u0434\u0435\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c?<\/h2>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u0443\u0434\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044c, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u0439 \u0437\u0430 \u044d\u043a\u0440\u0430\u043d \u0432\u043e\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u0444\u0430\u043a\u0442\u043e\u0432. \u0412 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043c\u043e\u0434\u0443\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0441\u043b\u043e\u0438\u0441\u0442\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0441 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435\u043c View, Presenter \u0438 Service. \u0422\u0443\u0442 \u043c\u043e\u0433\u043b\u0430 \u0431\u044b \u0431\u044b\u0442\u044c \u043b\u044e\u0431\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 View \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u0434\u0435\u043b\u0438\u0442\u044c \u043e\u0442 \u043b\u043e\u0433\u0438\u043a\u0438. \u042d\u0442\u043e \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0432\u0435\u0449\u0438, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441 UIKit, \u043e\u0447\u0435\u043d\u044c \u043d\u0435\u0443\u0434\u043e\u0431\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c.\u00a0<\/p>\n<p>\u041d\u0430\u043c \u0432\u0430\u0436\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0438\u0437 View-\u0441\u043b\u043e\u044f, \u0447\u0442\u043e\u0431\u044b \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0448\u0438\u0431\u043e\u043a \u0432 \u043d\u0435\u043c \u0431\u044b\u043b\u0430 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430. \u041a\u0440\u043e\u043c\u0435 \u044d\u0442\u043e\u0433\u043e, \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 <code>Date()<\/code> \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0430 \u0432 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 <code>DateProvider<\/code>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 <code>Date()<\/code> \u00a0\u2014 \u044d\u0442\u043e \u043d\u0435\u044f\u0432\u043d\u0430\u044f \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c\u0430\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0432\u043b\u0438\u044f\u0442\u044c \u043d\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.\u00a0<\/p>\n<p>\u0420\u0430\u0431\u043e\u0442\u0443 \u0441 \u043b\u043e\u043a\u0430\u0446\u0438\u0435\u0439 (\u0442\u0443\u0442 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f) \u0442\u043e\u0436\u0435 \u043b\u0443\u0447\u0448\u0435 \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u043c \u0438 \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0438\u0437 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u043a\u043e\u0434\u0430. \u0410 \u0432\u043e\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0431\u0430\u0437\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 (\u0442\u0443\u0442 \u0442\u043e\u0436\u0435 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f), <code>UserDefault<\/code> \u0438 \u0434\u0440\u0443\u0433\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432 \u043a\u043e\u0434\u0435, \u0433\u043b\u0430\u0432\u043d\u043e\u0435 \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0442\u044c \u043e\u0447\u0438\u0449\u0430\u0442\u044c \u044d\u0442\u0438 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0435\u0440\u0435\u0434 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u0442\u0435\u0441\u0442\u0430. <\/p>\n<h2>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043c\u043e\u0434\u0443\u043b\u044f<\/h2>\n<p>\u041c\u043e\u0434\u0443\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442. \u041f\u0440\u0438 \u0437\u0430\u0445\u043e\u0434\u0435 \u043d\u0430 \u044d\u043a\u0440\u0430\u043d \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u043d\u043e\u043f\u043a\u0430 \u201c\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u201d.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9e2\/c0f\/7b4\/9e2c0f7b4f9bfd1ea8deab1f5c2aa7ff.png\" alt=\"\" title=\"\" width=\"640\" height=\"1136\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9e2\/c0f\/7b4\/9e2c0f7b4f9bfd1ea8deab1f5c2aa7ff.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043d\u0435\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043b\u043e\u0430\u0434\u0435\u0440 \u0438 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u043e\u043f\u044b\u0442\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0441\u0435\u0442\u0438.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ba0\/f25\/1c5\/ba0f251c50266a1f23951c2f67542a4e.png\" width=\"640\" height=\"1136\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ba0\/f25\/1c5\/ba0f251c50266a1f23951c2f67542a4e.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442 \u0438 \u0435\u0433\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438. \u041f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u201c\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435\u201d \u043e\u0442\u043a\u0440\u043e\u0435\u0442\u0441\u044f \u044d\u043a\u0440\u0430\u043d \u0441 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439. \u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0442\u0430\u0442\u044c\u0438 \u044d\u043a\u0440\u0430\u043d \u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043d\u0430\u0441 \u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442, \u0441\u0444\u043e\u043a\u0443\u0441\u0438\u0440\u0443\u0435\u043c\u0441\u044f \u043d\u0430 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/086\/bd2\/867\/086bd2867ca23d0ee54f8b6cbcf1c687.png\" width=\"640\" height=\"1136\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/086\/bd2\/867\/086bd2867ca23d0ee54f8b6cbcf1c687.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0440\u0438 \u043d\u0435\u0443\u0434\u0430\u0447\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0430\u043b\u0435\u0440\u0442 \u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435. \u041f\u0440\u0438 \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u0438 \u0430\u043b\u0435\u0440\u0442\u0430 \u0441\u043d\u043e\u0432\u0430 \u0432\u0438\u0434\u043d\u043e \u043a\u043d\u043e\u043f\u043a\u0443 \u201c\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u201d.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ca1\/41f\/55f\/ca141f55ff281668f140278627b86077.png\" width=\"640\" height=\"1136\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ca1\/41f\/55f\/ca141f55ff281668f140278627b86077.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u043a\u043e\u0434\u0435. \u041d\u0430\u0448 \u043c\u043e\u0434\u0443\u043b\u044c \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0447\u0430\u0441\u0442\u0435\u0439: View, Presenter, Service, Router \u0438 NetworkTransport. \u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043a\u0430\u0436\u0434\u0443\u044e \u0438\u0437 \u043d\u0438\u0445 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e. \u041f\u0440\u0438\u043d\u0435\u0441\u0435\u043c \u0432 \u0436\u0435\u0440\u0442\u0432\u0443 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0440\u043e\u0448\u0438\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0440\u0430\u0434\u0438 \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e\u0441\u0442\u0438 \u0441\u0442\u0430\u0442\u044c\u0438, \u0442\u0430\u043a \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u043a\u0430\u0436\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043a\u043e\u0434 \u043c\u043e\u0433 \u0431\u044b \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u0435\u0435, \u0442\u043e, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u043e\u043d\u043e \u0442\u0430\u043a \u0438 \u0435\u0441\u0442\u044c. <\/p>\n<h2>Network<\/h2>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 NetworkTransport \u0438 Service. \u0415\u0441\u0442\u044c \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b NetworkTransport, \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u044e\u0449\u0438\u0439 NetworkTransportImpl (\u0441\u0430\u043c \u043a\u043e\u0434 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u043d\u0435 \u0431\u0443\u0434\u0443, \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u043e\u043d \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 URLSession), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043c\u0435\u0435\u0442 \u0445\u043e\u0434\u0438\u0442\u044c \u0432 \u0441\u0435\u0442\u044c \u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u043c\u0438. <\/p>\n<pre><code class=\"swift\">struct Request {     enum Method {         case get         case post     }     let method: Method     let url: String     let params: [String: String] }   protocol NetworkTransport {     func load&lt;T: Decodable>(         request: Request,         onResult: @escaping ((Result&lt;T, Error>) -> Void)     ) }<\/code><\/pre>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0432\u043d\u0443\u0442\u0440\u0438 InfoService. \u042d\u0442\u043e \u0441\u0435\u0440\u0432\u0438\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u043d\u0430\u0435\u0442, \u043f\u043e \u043a\u0430\u043a\u043e\u043c\u0443 URL \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u0438 \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441.<\/p>\n<pre><code class=\"swift\">final class InfoService {     private let networkTransport: NetworkTransport       init(networkTransport: NetworkTransport) {         self.networkTransport = networkTransport     }       func loadInfo(onResult: @escaping ((Result&lt;InfoModel, Error>) -> Void)) {         let request = Request(method: .get, url: \"https:\/\/info-example.com\/get_info\", params: [:])         networkTransport.load(request: request, onResult: onResult)     } }<\/code><\/pre>\n<p>\u041e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043d\u0430\u043c \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0438\u0439\u0442\u0438 \u0442\u0430\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435:<\/p>\n<pre><code class=\"json\">{     \"infoText\": \"Quick \u0438 Nimble \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 iOS \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435. Quick \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0430 Nimble \u2014 \u044d\u0442\u043e \u043c\u0435\u0442\u0447\u0435\u0440.\",     \"identifier\": 123,     \"creationDate\": \"2021-09-22T10:32:36.917\" }<\/code><\/pre>\n<p>\u0418 \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043c\u0430\u043f\u043f\u0438\u0442\u044c \u0438\u0445 \u0432 \u044d\u0442\u0443 \u043c\u043e\u0434\u0435\u043b\u044c:<\/p>\n<pre><code class=\"swift\">struct InfoModel: Decodable {     let infoText: String     let identifier: Int     let creationDate: Date \/\/ \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 Date \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c init(from decoder: Decoder) } <\/code><\/pre>\n<h2>Presenter<\/h2>\n<p>\u0414\u0430\u043b\u044c\u0448\u0435 \u0438\u0434\u0435\u0442 Presentation-\u0441\u043b\u043e\u0439. \u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0432 \u043d\u0435\u043c \u0438\u0434\u0435\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430.\u00a0<\/p>\n<p>\u0421\u0430\u043c Presenter \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0449\u0430\u0442\u044c\u0441\u044f \u0441 View \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b <code>InfoViewInput<\/code>. \u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>update()<\/code> \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 state \u044d\u043a\u0440\u0430\u043d\u0430, \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044f \u0435\u0433\u043e \u0432 \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u0430 <code>showError()<\/code> \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0430\u043b\u0435\u0440\u0442 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435. \u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0443 \u044d\u043a\u0440\u0430\u043d\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u0441\u0435\u0433\u043e \u0442\u0440\u0438: \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u0441 \u0441\u0438\u043d\u0435\u0439 \u043a\u043d\u043e\u043f\u043a\u043e\u0439 \u201c\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u201d, \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0441 \u043b\u043e\u0430\u0434\u0435\u0440\u043e\u043c \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443 \u044d\u043a\u0440\u0430\u043d\u0430 \u0438 \u0443\u0436\u0435 \u044d\u043a\u0440\u0430\u043d \u0441 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u0444\u0430\u043a\u0442\u043e\u043c, \u0434\u0430\u0442\u043e\u0439 \u0438 \u043a\u043d\u043e\u043f\u043a\u043e\u0439 \u201c\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435\u201d. \u042d\u0442\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043a\u0430\u043a \u0440\u0430\u0437 \u0438 \u043e\u0442\u0440\u0430\u0436\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 <code>InfoViewState<\/code>.<\/p>\n<pre><code class=\"swift\">enum InfoViewState: Equatable {     case initial     case loading     case info(infoText: String, timeAgoText: String) }   protocol InfoViewInput: AnyObject {     func update(withViewState viewState: InfoViewState)     func showError() }<\/code><\/pre>\n<p>Presenter \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u043c, \u0447\u0442\u043e \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 View \u0438 Service. View \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442 Presenter \u043e \u0441\u0432\u043e\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445 (\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0438\u043b\u0438 \u043d\u0430\u0436\u0430\u0442\u0430 \u043a\u043d\u043e\u043f\u043a\u0430), \u0430 Presenter \u0440\u0435\u0448\u0430\u0435\u0442, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0441 \u044d\u0442\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 (\u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c state, \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0441\u0435\u0442\u0438 \u0438\u043b\u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c).<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 Presenter<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">final class InfoPresenter {     weak var view: InfoViewInput? \/\/ weak \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 view       private let service: InfoService      private let router: InfoRouter      private let dateProvider: DateProvider        private var lastLoadedInfoModel: InfoModel?       init(service: InfoService,          router: InfoRouter,          dateProvider: DateProvider) {         self.service = service         self.router = router         self.dateProvider = dateProvider     }       func start() { \/\/ \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u043d\u0443\u0442\u0440\u0438 UIViewController.viewDidLoad()         view?.update(withViewState: .initial)     }       func didTapLoadInfoButton() { \/\/ \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u201c\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u201d         view?.update(withViewState: .loading)         service.loadInfo { [weak self] result in             guard let self = self else { return }             let viewState: InfoViewState             switch result {             case let .success(infoModel):                 self.lastLoadedInfoModel = infoModel                 viewState = self.makeSuccessInfoState(from: infoModel)             case .failure:                 viewState = .initial                 self.view?.showError()             }             self.view?.update(withViewState: viewState)         }     }       func didTapShowDetailsButton() { \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u201c\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435\u201d         guard let identifier = lastLoadedInfoModel?.identifier else { return }         router.openDetails(withIdentifier: identifier)     }       private func makeSuccessInfoState(from infoModel: InfoModel) -> InfoViewState {         let formatter = RelativeDateTimeFormatter()         formatter.unitsStyle = .full         let currentDate = dateProvider.getCurrentDate()         let timeAgoText = formatter.localizedString(for: infoModel.creationDate, relativeTo: currentDate)         return .info(infoText: infoModel.infoText, timeAgoText: timeAgoText)     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u044d\u0442\u043e\u0442 \u043a\u043b\u0430\u0441\u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435.\u041c\u0435\u0442\u043e\u0434 \u043f\u0440\u0435\u0437\u0435\u043d\u0442\u0435\u0440\u0430 <code>start()<\/code> \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 <code>viewDidLoad()<\/code> \u0443 UIViewController\u2019\u0430. \u041c\u0435\u0442\u043e\u0434 <code>start()<\/code> \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0441\u0442\u0435\u0439\u0442 \u0443 \u044d\u0442\u043e\u0433\u043e UIViewController\u2019\u0430 \u0438 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u0435\u0433\u043e \u0432 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441 \u043a\u043d\u043e\u043f\u043a\u043e\u0439 \u201c\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u201d \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443. <\/p>\n<p>\u041c\u0435\u0442\u043e\u0434 <code>didTapLoadInfoButton()<\/code> \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043a\u043d\u043e\u043f\u043a\u0443 \u201c\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u201d \u0438 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442 View \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043b\u043e\u0430\u0434\u0435\u0440\u0430. \u041f\u0440\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f, \u0438 Presenter \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0441 \u043d\u0438\u043c\u0438 \u0441\u0442\u0435\u0439\u0442 \u0443 View. \u0415\u0441\u043b\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0435\u0443\u0434\u0430\u0447\u043d\u0430\u044f, \u0442\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u0438 View \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0442\u0435\u0439\u0442.<\/p>\n<p>\u0410 \u043c\u0435\u0442\u043e\u0434 <code>didTapShowDetailsButton()<\/code> \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u201c\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435\u201d, \u0447\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0432\u044b\u0437\u043e\u0432\u0443 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 Router\u2019\u0430. \u0421\u0430\u043c Router \u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b, \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u0435\u0433\u043e, \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0442\u0430\u043a:<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b InfoRouter \u0438 \u0435\u0433\u043e \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">protocol InfoRouter {     func openDetails(withIdentifier identifier: Int) }   final class InfoRouterImpl: InfoRouter {     private weak var viewController: UIViewController?     init(viewController: UIViewController) {         self.viewController = viewController     }       func openDetails(withIdentifier identifier: Int) {         let factory = DetailInfoFactoryImpl()         let detailViewController = factory.makeViewController(withIdentifier: identifier)         viewController?.present(detailViewController, animated: true)     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0421\u0442\u043e\u0438\u0442 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 <code>DateProvider<\/code> \u0432\u043d\u0443\u0442\u0440\u0438 Presenter \u0438 \u0441\u0430\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u0442\u044b.<\/p>\n<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 <code>Date()<\/code> \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u0430\u0442\u044b, \u0442\u043e \u0441 \u0442\u0435\u0447\u0435\u043d\u0438\u0435\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u0440\u0430\u0437\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0430 \u044d\u0442\u043e, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u043e\u0439 \u0441 \u0434\u0430\u0442\u0430\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u043c. \u041d\u043e \u0443 \u044d\u0442\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0435\u0441\u0442\u044c \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435: \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 <code>DateProvider<\/code>. <code>DateProvider<\/code> \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u043a\u043b\u0430\u0441\u0441, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439 \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u0434\u0430\u0442\u0443. \u0414\u043b\u044f \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e <code>Date()<\/code>, \u0430 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u2014 \u043f\u0440\u0435\u0434\u0437\u0430\u0434\u0430\u043d\u043d\u0443\u044e \u0434\u0430\u0442\u0443 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432. \u0412\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u044d\u0442\u043e \u0442\u0430\u043a:<\/p>\n<pre><code class=\"swift\">protocol DateProvider {     func getCurrentDate() -> Date }   \/\/ \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 final class RealDateProvider: DateProvider {     func getCurrentDate() -> Date {         return Date()     } }   \/\/ \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0442\u0435\u0441\u0442\u0430\u0445 final class FakeDateProvider: DateProvider {     func getCurrentDate() -> Date {         Date(timeIntervalSince1970: 1632744444)     } }<\/code><\/pre>\n<h2>View<\/h2>\n<p>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u043a\u043e\u0434 UIViewController\u2019\u0430, \u0432 \u043d\u0435\u043c \u0432\u0441\u0435 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e. \u041e\u0441\u0442\u0430\u0432\u0438\u043c \u0437\u0430 \u0441\u043a\u043e\u0431\u043a\u0430\u043c\u0438 \u0441\u0442\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 layout\u2019\u0430::<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 InfoViewController<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">final class InfoViewController: UIViewController {       var presenter: InfoPresenter!       private let loadInfoButton = UIButton()     private let activityIndicator = UIActivityIndicatorView(style: .medium)     private let infoContainer = UIView() \/\/ \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0434\u043b\u044f showDetailsButton, infoLabel \u0438 timeAgoLabel     private let showDetailsButton = UIButton()     private let infoLabel = UILabel()     private let timeAgoLabel = UILabel()       override func viewDidLoad() {         super.viewDidLoad()         setupViews()         presenter.start()     }       private func setupViews() {         loadInfoButton.addTarget(self, action: #selector(didTapLoadInfoButton), for: .touchUpInside)         showDetailsButton.addTarget(self, action: #selector(didTapShowDetailsButton), for: .touchUpInside)         \/\/ \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u0441\u0435\u0445 view         \/\/ ...     }       @objc     private func didTapLoadInfoButton() {         presenter.didTapLoadInfoButton()     }       @objc     private func didTapShowDetailsButton() {         presenter.didTapShowDetailsButton()     } }   extension InfoViewController: InfoViewInput {     func update(withViewState viewState: InfoViewState) {         switch viewState {         case .initial:             loadInfoButton.isHidden = false             activityIndicator.isHidden = true             infoContainer.isHidden = true         case .loading:             loadInfoButton.isHidden = true             activityIndicator.isHidden = false             infoContainer.isHidden = true         case let .info(infoText: infoText, timeAgoText: timeAgoText):             loadInfoButton.isHidden = true             activityIndicator.isHidden = true             infoContainer.isHidden = false               infoLabel.text = infoText             timeAgoLabel.text = timeAgoText         }     }       func showError() {         let alertController = makeErrorAlert()         present(alertController, animated: true)     } }<\/code><\/pre>\n<p>\u0422\u0443\u0442 \u0441\u0442\u043e\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 update(), \u0432 \u043d\u0435\u0439 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 state \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f, \u0441\u043a\u0440\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b.<\/p>\n<\/div>\n<\/details>\n<h2>Factory<\/h2>\n<p>\u0412\u0441\u0435 \u044d\u0442\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043c\u043e\u0434\u0443\u043b\u044f \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0444\u0430\u0431\u0440\u0438\u043a\u0443:<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 \u0444\u0430\u0431\u0440\u0438\u043a\u0438<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">final class InfoModuleFactory {     func makeViewController() -> UIViewController {         let viewController = InfoViewController()         let networkTransport = NetworkTransportImpl()         let service = InfoService(networkTransport: networkTransport)         let router = InfoRouterImpl(viewController: viewController)         let dateProvider = RealDateProvider()         let presenter = InfoPresenter(service: service, router: router,                                       dateProvider: dateProvider)         viewController.presenter = presenter         presenter.view = viewController           return viewController     } } <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0441\u0451 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043c\u043e\u0434\u0443\u043b\u044f. \u0421 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b, \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043e\u0431\u044a\u0435\u043c\u043d\u043e \u0434\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430, \u0441 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b, \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u0435\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u044c \u0438 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u043e \u0441\u043b\u043e\u044f\u043c. \u0414\u0430\u043b\u044c\u0448\u0435 \u043d\u0430\u0441 \u0436\u0434\u0435\u0442 \u044d\u0442\u0430\u043f \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u043c\u043e\u0434\u0443\u043b\u044f \u043a \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e.<\/p>\n<h4>\u0427\u0442\u043e \u0438 \u043a\u0430\u043a \u0431\u0443\u0434\u0435\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c?<\/h4>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f \u043b\u0435\u0436\u0438\u0442 \u0432 Presenter \u0438 \u0432 Service, \u044d\u0442\u0443 \u0441\u0432\u044f\u0437\u043a\u0443 \u043c\u044b \u0438 \u0431\u0443\u0434\u0435\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u0415\u0441\u043b\u0438 \u0431\u044b \u0432 \u043d\u0430\u0448\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u0435 \u0431\u044b\u043b \u0435\u0449\u0435 \u0438 Interactor, \u0442\u043e \u043e\u043d \u0442\u043e\u0436\u0435 \u043f\u043e\u043f\u0430\u043b \u0431\u044b \u0432 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u0443\u044e \u0441\u0432\u044f\u0437\u043a\u0443. \u0410 \u0447\u0442\u043e\u0431\u044b \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u043e\u0438, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0435 \u043b\u043e\u0433\u0438\u043a\u0443, \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u043c\u043e\u043a\u0430\u043c\u0438 \u0442\u0435 \u0447\u0430\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0438\u043b\u0438 \u043d\u0435\u043e\u043f\u0440\u0430\u0432\u0434\u0430\u043d\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c.<\/p>\n<p>\u041a\u0430\u043a\u0438\u0435 \u0447\u0430\u0441\u0442\u0438 \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u043c\u043e\u043a\u0430\u043c\u0438? \u041c\u043e\u043a\u0430\u043c\u0438 \u043c\u044b \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c View, Router, DateProvider \u0438 NetworkTransport. View \u0438 Router \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0432 \u043d\u0438\u0445 \u0435\u0441\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 UIKit. \u041f\u0440\u043e <code>DateProvider<\/code> \u0443\u0436\u0435 \u043f\u043e\u044f\u0441\u043d\u0438\u043b\u0438 \u0447\u0443\u0442\u044c \u0432\u044b\u0448\u0435, \u0442\u0430\u043a \u043c\u044b \u0438\u0437\u0431\u0430\u0432\u043b\u044f\u0435\u043c\u0441\u044f \u043e\u0442 \u043d\u0435\u044f\u0432\u043d\u043e\u0439 \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c\u043e\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 <code>Date()<\/code>. \u0410 NetworkTransport \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u043c\u043e\u043a\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0445\u043e\u0434\u0438\u0442\u044c \u0437\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0432 \u0441\u0435\u0442\u044c \u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u0431\u044b\u0441\u0442\u0440\u044b\u043c\u0438 \u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u043c\u044b\u043c\u0438.<\/p>\n<p>\u0421\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0434\u043b\u044f \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u0438\u044f \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u043e\u043a\u0430\u043c\u0438 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043b\u044e\u0431\u0430\u044f \u0447\u0430\u0441\u0442\u044c, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0434\u043c\u0435\u043d\u044f\u0435\u0442 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434. \u0425\u043e\u0442\u044f \u043f\u043e \u0444\u0430\u043a\u0442\u0443 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437 \u043d\u0438\u0445 \u0431\u044b\u043b\u043e \u0431\u044b \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0435\u0435 \u043d\u0430\u0437\u0432\u0430\u0442\u044c Spy \u0438\u043b\u0438 Fake. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e \u043c\u043e\u043a\u0430\u0445, \u0441\u0442\u0430\u0431\u0430\u0445 \u0438 \u0448\u043f\u0438\u043e\u043d\u0430\u0445 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 <a href=\"https:\/\/martinfowler.com\/articles\/mocksArentStubs.html\"><u>Mocks aren\u2019t Stubs<\/u><\/a>.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u043e\u043a\u0438:<\/p>\n<p>\u0412 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0448\u0438\u0442\u044c, \u043a\u0430\u043a \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u043e\u043a\u0438. \u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0440\u0443\u043a\u0430\u043c\u0438. \u041d\u043e \u044d\u0442\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0443\u0442\u043e\u043c\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0438\u0441\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0431\u043e\u0439\u043b\u0435\u0440\u043f\u043b\u0435\u0439\u0442\u0430 \u0438 \u0448\u0430\u043d\u0441 \u043e\u0448\u0438\u0431\u0438\u0442\u044c\u0441\u044f \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0432\u044b\u0441\u043e\u043a.\u00a0<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0431\u043e\u0439\u043b\u0435\u0440\u043f\u043b\u0435\u0439\u0442\u0430, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c SwiftMockGeneratorForXcode. \u042d\u0442\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f Xcode, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043c\u043e\u043a\u0438 \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u0438\u043a\u043e\u0432 \u043c\u044b\u0448\u043a\u043e\u0439. \u041d\u0435 \u0431\u0443\u0434\u0443 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044e \u0442\u0443\u0442, \u0435\u0435 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043d\u0430 <a href=\"https:\/\/github.com\/seanhenry\/SwiftMockGeneratorForXcode\"><u>\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043a\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/u><\/a>, \u0442\u0430\u043c \u0432\u0441\u0451 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e \u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043c\u0438\u043d\u0443\u0442 5 \u043d\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u044f \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u0443 \u0443\u0442\u0438\u043b\u0438\u0442\u0443.<\/p>\n<p>\u0410 \u0435\u0449\u0435 \u043c\u043e\u043a\u0438 \u043c\u043e\u0436\u043d\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Sourcery, \u0432 <a href=\"https:\/\/github.com\/krzysztofzablocki\/Sourcery\"><u>\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438<\/u><\/a> \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0435\u0441\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0443\u043d\u043a\u0442 \u043f\u0440\u043e \u043a\u043e\u0434\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043c\u043e\u043a\u043e\u0432. \u0412 \u0426\u0438\u0430\u043d \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0438\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u043e\u0442 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 <code>InfoViewInput<\/code> \u0438 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043c\u043e\u043a \u0447\u0435\u0440\u0435\u0437 SwiftMockGeneratorForXcode.<\/p>\n<details class=\"spoiler\">\n<summary>\u041c\u043e\u043a InfoViewInput<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">final class InfoViewInputSpy: InfoViewInput {       var invokedUpdate = false     var invokedUpdateCount = 0     var invokedUpdateParameters: (viewState: InfoViewState, Void)?     var invokedUpdateParametersList = [(viewState: InfoViewState, Void)]()       func update(withViewState viewState: InfoViewState) {         invokedUpdate = true         invokedUpdateCount += 1         invokedUpdateParameters = (viewState, ())         invokedUpdateParametersList.append((viewState, ()))     }       var invokedShowError = false     var invokedShowErrorCount = 0       func showError() {         invokedShowError = true         invokedShowErrorCount += 1     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0443 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 <code>InfoViewInput<\/code> \u043d\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u0442\u043e \u043d\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u044d\u0442\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043c\u043e\u043a\u043e\u0432. \u0422\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0441\u0438\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0442\u0435\u0441\u0442\u0430\u043c\u0438.<\/p>\n<p>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u043a \u0434\u043b\u044f <code>InfoRouter<\/code>:<\/p>\n<details class=\"spoiler\">\n<summary>\u041c\u043e\u043a InfoRouter<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">final class InfoRouterSpy: InfoRouter {       var invokedOpenDetails = false     var invokedOpenDetailsCount = 0     var invokedOpenDetailsParameters: (identifier: Int, Void)?     var invokedOpenDetailsParametersList = [(identifier: Int, Void)]()       func openDetails(withIdentifier identifier: Int) {         invokedOpenDetails = true         invokedOpenDetailsCount += 1         invokedOpenDetailsParameters = (identifier, ())         invokedOpenDetailsParametersList.append((identifier, ()))     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0414\u0430\u043b\u044c\u0448\u0435 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0441\u0430\u043c\u043e\u043c\u0443 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u043c\u0443 \u043c\u043e\u043a\u0443: <code>NetworkTransport<\/code>. \u0412 \u043f\u0440\u043e\u0441\u0442\u043e\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 <code>NetworkTransport<\/code> \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043d\u0430\u0431\u043e\u0440\u0430 \u0442\u0435\u0441\u0442\u043e\u0432, \u043d\u043e \u044d\u0442\u043e \u0432 \u043f\u0435\u0440\u0441\u043f\u0435\u043a\u0442\u0438\u0432\u0435 \u043e\u0442\u043d\u0438\u043c\u0430\u0435\u0442 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u0435\u043c\u0441\u044f \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435. <\/p>\n<p>\u0421\u043c\u044b\u0441\u043b \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043a\u043b\u0430\u0441\u0442\u044c json-\u0444\u0430\u0439\u043b \u0441 \u043e\u0442\u0432\u0435\u0442\u043e\u043c \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0432 bundle \u0441 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0443\u0436\u0435 \u043e\u0442\u0442\u0443\u0434\u0430, \u0430 \u043d\u0435 \u0438\u0437 \u0441\u0435\u0442\u0438. \u0410 \u0441\u0430\u043c NetworkTransportMock \u0443\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u0448\u0430\u0442\u044c, \u0434\u043b\u044f \u043a\u0430\u043a\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a\u0430\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c, \u0438 \u043c\u0430\u043f\u043f\u0438\u0442\u044c \u0438\u0445 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u0438.<\/p>\n<p>\u041a\u043e\u0434 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u0438\u0441\u0442\u0438\u0447\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 FakeNetworkTransport \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 NetworkTransportMock<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">public struct FakeResponse: Hashable {     enum ResponseType {         case success(responseFileName: String)         case failure(error: Swift.Error)     }       let url: String     let method: Request.Method     let response: ResponseType       public static func == (lhs: FakeResponse, rhs: FakeResponse) -> Bool {         lhs.url == rhs.url &amp;&amp;         lhs.method == rhs.method     }       public func hash(into hasher: inout Hasher) {         hasher.combine(url)         hasher.combine(method)     } }   public final class NetworkTransportMock {     public var delay: TimeInterval = 0     private let bundle: Bundle     private var fakeResponses = Set&lt;FakeResponse>()     private(set) var invokedRequests = [Request]()       public init(bundle: Bundle) {         self.bundle = bundle     }       func mockSuccess(url: String, method: Request.Method, responseFile: String) {         let responseType: FakeResponse.ResponseType = .success(responseFileName: responseFile)         let fakeResponse = FakeResponse(url: url, method: method, response: responseType)         insertFakeResponse(fakeResponse)     }       func mockFailure(url: String, method: Request.Method, error: Swift.Error) {         let responseType: FakeResponse.ResponseType = .failure(error: error)         let fakeResponse = FakeResponse(url: url, method: method, response: responseType)         insertFakeResponse(fakeResponse)     }       func removeFakeResponse(withURL url: String, method: Request.Method) {         guard let fakeResponse = findFakeResponse(forURL: url, method: method) else { return }         fakeResponses.remove(fakeResponse)     }          private func insertFakeResponse(_ fakeResponse: FakeResponse) {         removeFakeResponse(withURL: fakeResponse.url, method: fakeResponse.method)         fakeResponses.insert(fakeResponse)     }       private func findFakeResponse(forURL url: String, method: Request.Method) -> FakeResponse? {         return fakeResponses.first(where: { $0.url == url &amp;&amp; $0.method == method })     }       private func getObject&lt;T: Decodable>(with jsonName: String) -> T {         guard             let url = bundle.url(forResource: jsonName, withExtension: \"json\"),             let jsonData = try? Data(contentsOf: url),             let object = try? JSONDecoder().decode(T.self, from: jsonData)         else {             fatalError()         }         return object     } }   extension NetworkTransportMock: NetworkTransport {       func load&lt;T>(request: Request, onResult: @escaping ((Result&lt;T, Error>) -> Void)) where T : Decodable {         invokedRequests.append(request)         guard let fakeResponse = findFakeResponse(forURL: request.url, method: request.method) else {             fatalError()         }         let result: Result&lt;T, Error>         switch fakeResponse.response {         case let .success(responseFileName: responseFileName):             let responseObject: T = getObject(with: responseFileName)             result = .success(responseObject)         case let .failure(error: error):             result = .failure(error)         }         if delay == 0 {             DispatchQueue.main.async {                 onResult(result)             }         } else {             DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(Int(1000 * delay))) {                 onResult(result)             }         }     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0422\u0443\u0442 \u0441\u0442\u043e\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>mockSuccess()<\/code> \u0438 <code>mockFailure()<\/code>. \u0427\u0435\u0440\u0435\u0437 \u043d\u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u043e\u0431\u0449\u0438\u0442\u044c <code>NetworkTransportMock<\/code> \u0434\u043b\u044f \u043a\u0430\u043a\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a\u0430\u043a\u0438\u0435 json-\u0444\u0430\u0439\u043b\u044b \u0441 \u043e\u0442\u0432\u0435\u0442\u0430\u043c\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c. \u0410 \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 <code>load()<\/code> \u0443\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u043f\u043e\u0438\u0441\u043a \u043d\u0443\u0436\u043d\u044b\u0445 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u0432 bundle \u0438 \u0438\u0445 \u0434\u0435\u043a\u043e\u0434\u0438\u043d\u0433.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0441 \u043c\u043e\u043a\u0430\u043c\u0438 \u0432\u0441\u0451. \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0432\u0441\u0451 \u044d\u0442\u043e \u043a\u0430\u043a-\u0442\u043e \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u0432\u043e\u0435\u0434\u0438\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041e\u0431\u044a\u0435\u043a\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u043d\u0430\u043c \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 <code>TestInfoModule<\/code>. <\/p>\n<pre><code class=\"swift\">struct TestInfoModule {     let presenter: InfoPresenter       let viewInputSpy: InfoViewInputSpy     let routerSpy: InfoRouterSpy     let transportMock: NetworkTransportMock }<\/code><\/pre>\n<p>\u0410 \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0444\u0430\u0431\u0440\u0438\u043a\u0438.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 \u0444\u0430\u0431\u0440\u0438\u043a\u0438 \u0434\u043b\u044f TestInfoModule<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">final class TestInfoFactory {     func makeModule() -> TestInfoModule {                  let viewInputSpy = InfoViewInputSpy()         let bundle = Bundle(for: TestInfoFactory.self)         let fakeNetworkTransport = NetworkTransportMock(bundle: bundle)         let service = InfoService(networkTransport: fakeNetworkTransport)         let routerSpy = InfoRouterSpy()         let dateProvider = FakeDateProvider()         let presenter = InfoPresenter(service: service, router: routerSpy, dateProvider: dateProvider)                  presenter.view = viewInputSpy           return TestInfoModule(             presenter: presenter,             viewInputSpy: viewInputSpy,             routerSpy: routerSpy,             transportMock: fakeNetworkTransport         )     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u0430. \u041c\u044b \u0437\u0430\u043c\u0435\u043d\u0438\u043b\u0438 \u043d\u0430 \u043c\u043e\u043a\u0438 \u0432\u0441\u0451, \u0447\u0442\u043e \u043d\u0430\u043c \u0431\u044b\u043b\u043e \u043d\u0443\u0436\u043d\u043e, \u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e!<\/p>\n<h2>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c!<\/h2>\n<p>\u0412\u043e\u0442 \u043c\u044b \u0438 \u043f\u043e\u0447\u0442\u0438 \u0433\u043e\u0442\u043e\u0432\u044b \u043a \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u043d\u0430\u0448\u0435\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f. \u0427\u0442\u043e\u0431\u044b \u043d\u0430\u0447\u0430\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b, \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c Quick \u0432 \u043f\u0440\u043e\u0435\u043a\u0442. \u041a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u043b\u0443\u0447\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u043e \u0432 <a href=\"https:\/\/github.com\/Quick\/Quick\/blob\/main\/Documentation\/en-us\/InstallingQuick.md\"><u>\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438<\/u><\/a>, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0430\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0432\u0430\u043c \u0441\u043f\u043e\u0441\u043e\u0431.\u00a0<\/p>\n<p>\u0412 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u0430\u0445 \u043e\u0447\u0435\u043d\u044c \u0432\u0430\u0436\u043d\u043e \u0441\u043e\u0431\u043b\u044e\u0434\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u0437\u043e\u0432\u043e\u0432, \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0431\u043b\u0438\u0437\u043a\u0443\u044e \u043a \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439. \u0423 \u043d\u0430\u0441 \u0432 Presenter \u0435\u0441\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>start()<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0438\u0437 <code>UIViewController.viewDidLoad()<\/code>, \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>didTapLoadInfoButton()<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445. \u041d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435 \u043d\u0430\u0436\u0430\u0442\u044c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0432\u044b\u0437\u043e\u0432\u0435\u0442\u0441\u044f <code>viewDidLoad()<\/code>. \u0412\u043e\u0442 \u0438 \u0432 \u0442\u0435\u0441\u0442\u0430\u0445, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>didTapLoadInfoButton()<\/code>, \u043c\u044b \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>start()<\/code>. \u0411\u043b\u0430\u0433\u043e \u0432 Quick \u044d\u0442\u043e \u0432\u0441\u0451 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043b\u0435\u0433\u043a\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c.<\/p>\n<h2>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043c\u043e\u0434\u0443\u043b\u044f<\/h2>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043c\u043e\u0434\u0443\u043b\u044f:<\/p>\n<pre><code class=\"swift\">class QuickDemoTests: QuickSpec { \/\/ 1     override func spec() { \/\/ 2         describe(\"\u042d\u043a\u0440\u0430\u043d \u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u043c\u0438 \u0444\u0430\u043a\u0442\u0430\u043c\u0438\") { \/\/ 3               var module: TestInfoModule! \/\/ 4             var presenter: InfoPresenter { module.presenter } \/\/ 4.1             var lastViewState: InfoViewState { module.viewInputSpy.invokedUpdateParameters!.viewState } \/\/ 4.2             var networkTransport: NetworkTransportMock { module.transportMock } \/\/ 4.3               beforeEach {                 module = TestInfoFactory().makeModule() \/\/ 5             }             \/\/ 6         }     } }<\/code><\/pre>\n<p>\u041f\u043e\u0439\u0434\u0435\u043c \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443. \u0412 (1) \u043c\u044b \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0435\u043c \u043d\u0430\u0448 \u043a\u043b\u0430\u0441\u0441 \u0441 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0438 \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u043c \u0435\u0433\u043e \u043e\u0442 QuickSpec, \u0432 (2) \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>spec()<\/code>. \u041f\u043e\u0442\u043e\u043c \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0435\u043c \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u0431\u043b\u043e\u043a <code>describe()<\/code> (3) \u0438 \u0432 \u0435\u0433\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u043f\u0438\u0448\u0435\u043c, \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0432 \u044d\u0442\u043e\u043c \u0442\u0435\u0441\u0442\u0435. \u041e\u0431\u044b\u0447\u043d\u043e \u044d\u0442\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f.<\/p>\n<p>\u0415\u0434\u0435\u043c \u0434\u0430\u043b\u044c\u0448\u0435: \u0432 (4) \u0438\u0434\u0435\u0442 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043c\u043e\u0434\u0443\u043b\u044f, \u0430 \u0432 (4.1, 4.2, 4.3) \u2014 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u043c\u044b\u0435 \u043f\u0440\u043e\u043f\u0435\u0440\u0442\u0438-\u0445\u0435\u043b\u043f\u0435\u0440\u044b \u0434\u043b\u044f \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u0438\u044f \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a \u0447\u0430\u0441\u0442\u044f\u043c \u043c\u043e\u0434\u0443\u043b\u044f. \u0412\u0430\u0436\u043d\u043e, \u0447\u0442\u043e \u0441\u0430\u043c \u043c\u043e\u0434\u0443\u043b\u044c \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f (5) \u0432 \u0431\u043b\u043e\u043a\u0435 <code>beforeEach()<\/code>, \u0430 \u043d\u0435 \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439. \u042d\u0442\u043e \u043d\u0443\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0447\u0430\u0441\u0442\u0438 \u0442\u0435\u0441\u0442\u0430 \u043d\u0435 \u0432\u043b\u0438\u044f\u043b\u0438 \u0434\u0440\u0443\u0433 \u043d\u0430 \u0434\u0440\u0443\u0433\u0430.\u0410 \u043d\u0430 \u043c\u0435\u0441\u0442\u043e (6) \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0447\u0430\u0441\u0442\u0438 \u0442\u0435\u0441\u0442\u0430.<\/p>\n<h2>\u041f\u0435\u0440\u0432\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0442\u0435\u0441\u0442\u0430<\/h2>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f. \u041f\u043e\u0441\u043b\u0435 \u0432\u044b\u0437\u043e\u0432\u0430 <code>viewDidLoad()<\/code> \u0443 UIViewController \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043a\u043d\u043e\u043f\u043a\u0430 \u201c\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u201d, \u0430 \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e Presenter \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u043e\u043e\u0431\u0449\u0438\u0442\u044c View, \u0447\u0442\u043e \u043d\u0430\u0434\u043e \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 <code>initial<\/code>. \u0412 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u0435\u0449\u0435 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440.<\/p>\n<p>\u041a\u043e\u0434 \u0442\u0435\u0441\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a, \u043c\u044b \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u043d\u0430 \u043c\u0435\u0441\u0442\u043e (6):<\/p>\n<pre><code class=\"swift\">context(\"\u041f\u0440\u0438 \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u0438 \u044d\u043a\u0440\u0430\u043d\u0430\") { \/\/ 7     beforeEach {  \/\/ 8         presenter.start()     }     it(\"\u041d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043d\u043e\u043f\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\") { \/\/ 9         expect(module.viewInputSpy.invokedUpdateCount) == 1 \/\/ 10         expect(lastViewState) == .initial \/\/ 11     }     it(\"\u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f\") { \/\/ 12         expect(networkTransport.invokedRequests.isEmpty) == true \/\/ 13     }     \/\/ 14 }<\/code><\/pre>\n<p>\u0418\u0442\u0430\u043a, \u043f\u043e\u0439\u0434\u0435\u043c \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443. \u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0442\u0435\u0441\u0442\u0430 \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>context()<\/code> (7), \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f <code>context()<\/code> \u043c\u044b \u043f\u0438\u0448\u0435\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435, \u043f\u043e\u0441\u043b\u0435\u0434\u0441\u0442\u0432\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0432\u043d\u0443\u0442\u0440\u0438. \u0427\u0438\u0442\u0430\u0442\u044c \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0442\u0430\u043a: \u201c\u0415\u0441\u043b\u0438 View \u043f\u043e\u044f\u0432\u0438\u043b\u043e\u0441\u044c \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435, \u0442\u043e \u2026\u201d.\u00a0 \u0414\u0430\u043b\u0435\u0435 \u0438\u0434\u0435\u0442 \u0431\u043b\u043e\u043a <code>beforeEach()<\/code> (8). \u0412 \u043d\u0435\u043c \u043c\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0443\u0441\u043b\u043e\u0432\u0438\u044e \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 <code>context()<\/code>. \u0412\u0441\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043d\u0430\u0434 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u0432 \u0431\u043b\u043e\u043a\u0430\u0445 <code>beforeEach()<\/code>.\u00a0<\/p>\n<p>\u0427\u0443\u0442\u044c \u043d\u0438\u0436\u0435 \u0438\u0434\u0443\u0442 \u0434\u0432\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 <code>it()<\/code> (9 \u0438 12). \u0412 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a <code>it()<\/code> \u043c\u044b \u043f\u0438\u0448\u0435\u043c, \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c. \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0440\u0430\u0437\u043d\u044b\u0445 \u0430\u0441\u043f\u0435\u043a\u0442\u043e\u0432 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 View \u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0441\u0435\u0442\u044c\u044e) \u043b\u0443\u0447\u0448\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u0442\u044c \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438 <code>it()<\/code>. \u0412 (9) \u043c\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0431\u044b\u043b\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u043e\u0434\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 View (10) \u0438 \u0447\u0442\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441\u0442\u0430\u043b\u043e \u0438\u043c\u0435\u043d\u043d\u043e <code>initial<\/code> (11). \u0410 \u0432 (12) \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0439 \u043a \u0441\u0435\u0442\u0438 \u043d\u0435 \u0431\u044b\u043b\u043e (13).<\/p>\n<p>\u0422\u0443\u0442 \u043e\u0447\u0435\u043d\u044c \u0432\u0430\u0436\u043d\u043e \u0435\u0449\u0435 \u0440\u0430\u0437 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0431\u043b\u043e\u043a\u043e\u0432 \u043a\u043e\u0434\u0430. \u041d\u0443\u0436\u043d\u043e \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 <code>it()<\/code> \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u043c, \u0438 <code>beforeEach()<\/code> \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u0435\u0440\u0435\u0434 \u043a\u0430\u0436\u0434\u044b\u043c \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u043c \u0431\u043b\u043e\u043a\u043e\u043c \u043d\u0430 \u043e\u0434\u043d\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435. \u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u043a\u043e\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0442\u0430\u043a\u043e\u0439: [(8), (9)] \u0438 [(8), (12)], \u043d\u043e \u043d\u0438\u043a\u0430\u043a \u043d\u0435 [(8), (9), (12)].<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448 \u043c\u043e\u0434\u0443\u043b\u044c \u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043d \u0432 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u0438 \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u044b. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0431\u0443\u0434\u0435\u043c \u0432\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u043c\u0435\u0441\u0442\u043e (14).<\/p>\n<h2>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0430\u0436\u0430\u0442\u0438\u0435 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u0443\u044e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043d\u0430 \u0432 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0436\u0430\u0442\u044c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u201c\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u201d, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u044b\u0437\u043e\u0432\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445. \u0421\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u043b\u043e\u0430\u0434\u0435\u0440 (View \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442 \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 <code>loading<\/code>), \u0430 \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 View \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u0442 \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0433\u043e \u0444\u0430\u043a\u0442\u0430 (\u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 <code>info<\/code>).<\/p>\n<pre><code class=\"swift\">context(\"\u041f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u0430\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430\") { \/\/ 15     beforeEach { \/\/ 16         networkTransport.mockSuccessForInfoLoading() \/\/ 17         presenter.didTapLoadInfoButton() \/\/ 18     }     it(\"\u0414\u0430\u043d\u043d\u044b\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0438 \u0432 \u0438\u0442\u043e\u0433\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f\") { \/\/ 19         expect(lastViewState) == .loading \/\/ 20         expect(module.viewInputSpy.invokedUpdateCount) == 2 \/\/ 21           expect(lastViewState).toEventually(equal(self.successState())) \/\/ 22         expect(module.viewInputSpy.invokedUpdateCount) == 3 \/\/ 23     }     \/\/ 24 }   private extension NetworkTransportMock {     static let infoURL = \"https:\/\/info-example.com\/get_info\"     func mockSuccessForInfoLoading() {  \/\/ 17.1 delay = 0.1         mockSuccess(             url: Self.infoURL,             method: .get,             responseFile: \"info-fake-response\"         )     } }   private extension QuickDemoTests {     func successState() -> InfoViewState { \/\/ 22.1         return .info(             infoText: \"Quick \u0438 Nimble \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 iOS \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435. Quick \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0430 Nimble \u2014 \u044d\u0442\u043e \u043c\u0435\u0442\u0447\u0435\u0440.\",             timeAgoText: \"5 \u0434\u043d\u0435\u0439 \u043d\u0430\u0437\u0430\u0434\"         )     } } <\/code><\/pre>\n<details class=\"spoiler\">\n<summary> info-fake-response.json<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"json\">{     \"infoText\": \"Quick \u0438 Nimble \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 iOS \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435. Quick \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0430 Nimble \u2014 \u044d\u0442\u043e \u043c\u0435\u0442\u0447\u0435\u0440.\",     \"identifier\": 123,     \"creationDate\": \"2021-09-22T10:32:36.917\" }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f \u043f\u043e \u043a\u043e\u0434\u0443 \u0442\u0435\u0441\u0442\u0430: <code>context()<\/code> (15) \u0438\u0434\u0435\u0442 \u043d\u0430 \u043c\u0435\u0441\u0442\u0435 (14), \u0442. \u0435. \u043e\u043d \u0432\u043b\u043e\u0436\u0435\u043d \u0432\u043d\u0443\u0442\u0440\u044c \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e <code>context()<\/code> (7). \u042d\u0442\u043e \u043d\u0443\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0437\u043e\u0432\u0430 <code>presenter.start()<\/code> \u0438\u0437 (8), \u0430 \u0435\u0449\u0435 \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 \u0432\u043d\u044f\u0442\u043d\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<p>\u0412 <code>beforeEach()<\/code> (16) \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (17) \u0432 <code>NetworkTransportMock<\/code> \u0447\u0435\u0440\u0435\u0437 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432 \u0435\u0433\u043e extension (17.1). \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u043e \u0432 extension \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0432\u044b\u0441\u0438\u0442\u044c \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c \u043a\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u0430.<\/p>\n<p>\u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0432 <code>NetworkTransportMock<\/code> \u043f\u0440\u0438\u0434\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441 <code>url: https:\/\/info-example.com\/get_info<\/code> \u0438 <code>method: GET<\/code>, \u0442\u043e \u043e\u0442\u0432\u0435\u0442 \u0431\u0443\u0434\u0435\u0442 \u0432\u0437\u044f\u0442 \u0438\u0437 <code>info-fake-response.json<\/code>.\u00a0<\/p>\n<p>\u0410 \u0432 (18) \u043c\u044b \u0443\u0436\u0435 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 \u0441\u0430\u043c\u0443 \u043a\u043d\u043e\u043f\u043a\u0443 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0432 <code>it()<\/code> (19) \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 View \u0431\u044b\u043b\u043e \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0432 \u043e\u0431\u0449\u0435\u0439 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438 2 \u0440\u0430\u0437\u0430 (20) (\u043f\u0435\u0440\u0432\u044b\u0439 \u0440\u0430\u0437 \u0441\u0442\u0435\u0439\u0442 \u0431\u044b\u043b <code>initial<\/code>). \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0441\u0442\u0435\u0439\u0442, \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443, \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f <code>loading<\/code> (21).\u00a0<\/p>\n<p>\u0412 (22) \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0435\u0435. \u0418\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e \u0447\u0442\u043e \u0443 <code>NetworkTransportMock<\/code> \u043c\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0443 \u043e\u0442\u0432\u0435\u0442\u0430 \u0432 0.1 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 (\u0432 \u043c\u0435\u0442\u043e\u0434\u0435 (17.1)), \u0447\u0442\u043e\u0431\u044b \u044d\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443, \u043d\u0430\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u043e\u0436\u0434\u0430\u0442\u044c \u044d\u0442\u0443 0.1 \u0441\u0435\u043a\u0443\u043d\u0434\u044b, \u043f\u043e\u043a\u0430 \u0441\u0442\u0435\u0439\u0442 \u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0434\u043e \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e\u0433\u043e. \u0410 \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u043e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0433\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>toEventually()<\/code>. \u041f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c, \u043a\u0430\u043a \u043e\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043c\u043e\u0436\u043d\u043e <a href=\"https:\/\/github.com\/Quick\/Nimble#asynchronous-expectations\"><u>\u0442\u0443\u0442<\/u><\/a>.\u00a0<\/p>\n<p>\u0412 (22) \u043c\u044b \u043a\u0430\u043a \u0440\u0430\u0437 \u0436\u0434\u0435\u043c, \u043f\u043e\u043a\u0430 \u0441\u0442\u0435\u0439\u0442 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0434\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0433\u043e \u043d\u0430\u043c. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0442\u0435\u0439\u0442\u0430 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u043e \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e (22.1) \u0434\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u0438 \u043a\u043e\u0434\u0430. \u0412 (23) \u043c\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0441\u0442\u0435\u0439\u0442 \u043e\u0431\u043d\u043e\u0432\u0438\u043b\u0441\u044f \u0440\u043e\u0432\u043d\u043e 3 \u0440\u0430\u0437\u0430, \u0438 \u043c\u044b \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u0438\u043b\u0438 \u0435\u0433\u043e \u043b\u0438\u0448\u043d\u0438\u0439 \u0440\u0430\u0437 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445. \u0412 (24) \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d <code>context()<\/code> \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u201c\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435\u201d, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0440\u0430\u043d.<\/p>\n<p>\u0412\u0441\u0451, \u0443\u0441\u043f\u0435\u0448\u043d\u0443\u044e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043b\u0438, \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0435\u0449\u0435 \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u043f\u0430\u0440\u0443 \u0432\u0435\u0449\u0435\u0439. <\/p>\n<h2>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u044d\u043a\u0440\u0430\u043d\u0430 \u0441 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439<\/h2>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0436\u0430\u0442\u044c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443, \u0438 \u043e\u0442\u043a\u0440\u043e\u0435\u0442\u0441\u044f \u044d\u043a\u0440\u0430\u043d \u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439. \u0418\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435 \u043d\u0430\u0436\u0430\u0442\u044c \u043d\u0430 \u044d\u0442\u0443 \u043a\u043d\u043e\u043f\u043a\u0443 \u043c\u043e\u0436\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432 \u0442\u0435\u0441\u0442\u0430\u0445 \u043c\u044b \u0442\u043e\u0436\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u044d\u043a\u0440\u0430\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0442.\u0435. <code>context()<\/code> (25) \u0434\u043e\u043b\u0436\u0435\u043d \u0438\u0434\u0442\u0438 \u043d\u0430 \u043c\u0435\u0441\u0442\u0435 (24).<\/p>\n<pre><code class=\"swift\">context(\"\u041f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438\") { \/\/ 25     beforeEach {         waitUntilEqual(lastViewState, self.successState()) \/\/ 26         presenter.didTapShowDetailsButton() \/\/ 27     }     it(\"\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u044d\u043a\u0440\u0430\u043d \u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u044f\u043c\u0438\") {         expect(module.routerSpy.invokedOpenDetailsCount) == 1 \/\/ 28         expect(module.routerSpy.invokedOpenDetailsParameters!.identifier) == 123 \/\/ 29     } }   \/\/ 30 public func waitUntilEqual&lt;T: Equatable>(file: FileString = #file,                                          line: UInt = #line,                                          _ left: @autoclosure @escaping () throws -> T,                                          _ right: T?,                                          timeout: DispatchTimeInterval = AsyncDefaults.timeout) {     expect(file: file, line: line, left).toEventually(equal(right), timeout: timeout) }<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u044d\u0442\u043e\u0442 \u0442\u0435\u0441\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u043b, \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0442\u0440\u044e\u043a. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 <code>context()<\/code> (25) \u043d\u0430\u0447\u043d\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0432 <code>beforeEach()<\/code> \u043d\u0430 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u0443\u0440\u043e\u0432\u043d\u0435 (16), \u0442\u043e \u0438 \u0432 \u0441\u0442\u0435\u0439\u0442 <code>info<\/code> \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043d\u0435 \u0443\u0441\u043f\u0435\u0435\u0442. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0442\u0443\u0442 \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u043f\u043e\u0434\u043e\u0436\u0434\u0430\u0442\u044c, \u043f\u043e\u043a\u0430 \u044d\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442 \u043f\u0440\u044f\u043c\u043e \u0432 \u044d\u0442\u043e\u043c \u0431\u043b\u043e\u043a\u0435 <code>beforeEach()<\/code> (25). \u041d\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u043d\u0443\u0442\u0440\u0438 <code>beforeEach()<\/code> \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>expect()<\/code>, \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0430 <code>waitUntilTrue()<\/code>, \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f \u043e\u0431\u0435\u0440\u0442\u043a\u0430 \u043d\u0430\u0434 <code>expect().toEventually()<\/code>(30), \u043d\u043e \u043e\u043d\u0430 \u0434\u0435\u043b\u0430\u0435\u0442 \u043a\u043e\u0434 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0447\u0438\u0449\u0435.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u043c\u044b \u0434\u043e\u0436\u0434\u0430\u043b\u0438\u0441\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 <code>info<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>waitUntilTrue()<\/code>, \u043c\u044b \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 (27) \u0438 \u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u043d\u0430\u0436\u0430\u0442\u0438\u0435 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0431\u044b\u043b\u043e \u043e\u0434\u043d\u043e (28), \u0438 \u0432 \u0440\u043e\u0443\u0442\u0435\u0440 \u0431\u044b\u043b \u043f\u0435\u0440\u0435\u0434\u0430\u043d \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0444\u0430\u043a\u0442\u0430 (29).<\/p>\n<p>\u0421 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u044c\u044e \u0432\u0441\u0451, \u043d\u0430\u043c \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0443\u044e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u043e \u0442\u0430\u043c \u0432\u0441\u0451 \u043f\u0440\u0435\u0434\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e.<\/p>\n<h2>\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u0430\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u041f\u0440\u0438 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0430\u043b\u0435\u0440\u0442 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435, \u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 <code>initial<\/code>. <\/p>\n<pre><code class=\"swift\">context(\"\u041f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435\") { \/\/ 31     beforeEach {         networkTransport.mockFailureForInfoLoading() \/\/ 32         presenter.didTapLoadInfoButton() \/\/ 33     }     it(\"\u0412 \u0438\u0442\u043e\u0433\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u0430\") {         expect(lastViewState).toEventually(equal(.initial)) \/\/ 34         expect(module.viewInputSpy.invokedUpdateCount) == 3 \/\/ 35         expect(module.viewInputSpy.invokedShowErrorCount) == 1 \/\/ 36     } }   private extension NetworkTransportMock {     static let infoURL = \"https:\/\/info-example.com\/get_info\"     ...     func mockFailureForInfoLoading() { \/\/ 32.1         mockFailure(             url: Self.infoURL,              method: .get,              error: NSError()         )     } }<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 (31) \u0434\u043e\u043b\u0436\u043d\u0430 \u0438\u0434\u0442\u0438 \u043d\u0430 \u0442\u043e\u043c \u0436\u0435 \u0443\u0440\u043e\u0432\u043d\u0435 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u0447\u0442\u043e \u0438 <code>context()<\/code> \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 (15). \u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0432 <code>NetworkTransportMock<\/code> \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a (32, 32.1), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a \u0441\u0435\u0442\u0438. \u041f\u043e\u0442\u043e\u043c \u043c\u044b \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 (33) \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u0435\u0440\u0435\u0448\u043b\u0430 \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 <code>initial<\/code> (34), \u0432\u0441\u0435\u0433\u043e \u0431\u044b\u043b\u043e 3 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0435\u0439\u0442\u0430 (\u043f\u0435\u0440\u0432\u044b\u0439 \u043d\u0430 <code>initial<\/code>, \u043f\u043e\u0442\u043e\u043c \u043d\u0430 <code>loading<\/code>, \u043f\u043e\u0442\u043e\u043c \u0441\u043d\u043e\u0432\u0430 \u043d\u0430 <code>initial<\/code>) \u0438 \u0431\u044b\u043b \u043f\u043e\u043a\u0430\u0437\u0430\u043d \u0430\u043b\u0435\u0440\u0442 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 (36).<\/p>\n<h2>\u0422\u0435\u0441\u0442\u044b \u0433\u043e\u0442\u043e\u0432\u044b<\/h2>\n<p>\u0413\u043e\u0442\u043e\u0432\u043e, \u043c\u043e\u0434\u0443\u043b\u044c \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d. \u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432 \u043c\u043e\u0434\u0443\u043b\u044f \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u043d\u0438\u0436\u0435, \u043f\u043e \u043d\u0435\u043c\u0443 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043d\u044f\u0442\u043d\u0435\u0435, \u043a\u0430\u043a\u0438\u0435 \u0431\u043b\u043e\u043a\u0438 <code>context()<\/code> \u043a\u0443\u0434\u0430 \u0432\u043b\u043e\u0436\u0435\u043d\u044b. \u0410 \u0435\u0449\u0435 \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043c\u043e\u0434\u0443\u043b\u044f. \u0421 \u0435\u0433\u043e \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u043a\u0430\u043a\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0435\u0441\u0442\u044c \u0443 \u043c\u043e\u0434\u0443\u043b\u044f \u0438 \u0432 \u043a\u0430\u043a\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043e\u043d\u0430 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">import Quick import Nimble @testable import QuickDemo  class QuickDemoTests: QuickSpec { \/\/ 1     override func spec() { \/\/ 2         describe(\"\u042d\u043a\u0440\u0430\u043d \u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u043c\u0438 \u0444\u0430\u043a\u0442\u0430\u043c\u0438\") { \/\/ 3                          var module: TestInfoModule! = TestInfoFactory().makeModule() \/\/ 4             var presenter: InfoPresenter { module.presenter } \/\/ 4.1             var lastViewState: InfoViewState { module.viewInputSpy.invokedUpdateParameters!.viewState } \/\/ 4.2             var networkTransport: NetworkTransportMock { module.transportMock } \/\/ 4.3                          beforeEach {                 module = TestInfoFactory().makeModule() \/\/ 5             }             \/\/ 6             context(\"\u041f\u0440\u0438 \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u0438 \u044d\u043a\u0440\u0430\u043d\u0430\") { \/\/ 7                 beforeEach {  \/\/ 8                     presenter.start()                 }                 it(\"\u041d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043d\u043e\u043f\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\") { \/\/ 9                     expect(lastViewState) == .initial \/\/ 10                     expect(module.viewInputSpy.invokedUpdateCount) == 1 \/\/ 11                 }                 it(\"\u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f\") { \/\/ 12                     expect(networkTransport.invokedRequests.isEmpty) == true \/\/ 13                 }                 \/\/ 14                 context(\"\u041f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u0430\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435\") { \/\/ 15                     beforeEach { \/\/ 16                         networkTransport.mockSuccessForInfoLoading() \/\/ 17                         presenter.didTapLoadInfoButton() \/\/ 18                     }                     it(\"\u0414\u0430\u043d\u043d\u044b\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0438 \u0432 \u0438\u0442\u043e\u0433\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f\") { \/\/ 19                         expect(lastViewState) == .loading \/\/ 20                         expect(module.viewInputSpy.invokedUpdateCount) == 2 \/\/ 21                                                  expect(lastViewState).toEventually(equal(self.successState())) \/\/ 22                                                  expect(lastViewState) == self.successState()                         if case let .info(infoText: infoText, timeAgoText: timeAgoText) = lastViewState {                             expect(infoText) == \"Quick \u0438 Nimble \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 iOS \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435. Quick \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0430 Nimble \u2014 \u044d\u0442\u043e \u043c\u0435\u0442\u0447\u0435\u0440.\"                             expect(timeAgoText) == \"5 \u0434\u043d\u0435\u0439 \u043d\u0430\u0437\u0430\u0434\"                         }                         expect(module.viewInputSpy.invokedUpdateCount) == 3 \/\/ 23                     }                     \/\/ 24                     context(\"\u041f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438\") { \/\/ 25                         beforeEach {                             waitUntilEqual(lastViewState, self.successState()) \/\/ 26                             presenter.didTapShowDetailsButton() \/\/ 27                         }                         it(\"\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u044d\u043a\u0440\u0430\u043d \u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u044f\u043c\u0438\") {                             expect(module.routerSpy.invokedOpenDetailsCount) == 1 \/\/ 28                             expect(module.routerSpy.invokedOpenDetailsParameters!.identifier) == 123 \/\/ 29                         }                     }                 }                 context(\"\u041f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435\") { \/\/ 31                     beforeEach {                         networkTransport.mockFailureForInfoLoading() \/\/ 32                         presenter.didTapLoadInfoButton() \/\/ 33                     }                     it(\"\u0412 \u0438\u0442\u043e\u0433\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u0430\") {                         expect(lastViewState).toEventually(equal(.initial)) \/\/ 34                         expect(module.viewInputSpy.invokedUpdateCount) == 3 \/\/ 35                         expect(module.viewInputSpy.invokedShowErrorCount) == 1 \/\/36                     }                 }             }         }     } }  extension QuickDemoTests {     func successState() -> InfoViewState { \/\/ 22.1         return .info(             infoText: \"Quick \u0438 Nimble \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 iOS \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435. Quick \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0430 Nimble \u2014 \u044d\u0442\u043e \u043c\u0435\u0442\u0447\u0435\u0440.\",             timeAgoText: \"5 \u0434\u043d\u0435\u0439 \u043d\u0430\u0437\u0430\u0434\"         )     } }  private extension NetworkTransportMock {     static let infoURL = \"https:\/\/info-example.com\/get_info\"     func mockSuccessForInfoLoading() { \/\/ 17.1         delay = 0.1         mockSuccess(             url: Self.infoURL,             method: .get,             responseFile: \"info-fake-response\"         )     }          func mockFailureForInfoLoading() { \/\/ 32.1         mockFailure(             url: Self.infoURL,             method: .get,             error: NSError()         )     } }  \/\/ 30 public func waitUntilEqual&lt;T: Equatable>(file: FileString = #file,                                          line: UInt = #line,                                          _ left: @autoclosure @escaping () throws -> T,                                          _ right: T?,                                          timeout: DispatchTimeInterval = AsyncDefaults.timeout) {     expect(file: file, line: line, left).toEventually(equal(right), timeout: timeout) }<\/code><\/pre>\n<\/div>\n<\/details>\n<h4>\u041a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432?<\/h4>\n<p>\u0422\u0443\u0442 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u0438\u0435\u043c\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<h2>\u0412\u044b\u043d\u043e\u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0430\u0431\u0440\u0438\u043a\u0438<\/h2>\n<p>\u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u043c\u043e\u0434\u0443\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0432 \u0441\u0435\u0431\u0435 \u0432\u0441\u0435 \u043a\u043b\u0430\u0441\u0441\u044b, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u042d\u0442\u043e \u0438 \u0441\u0430\u043c\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b, \u0438 \u043c\u043e\u043a\u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438. \u0421\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0442\u0430\u043a\u0438\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 \u0444\u0430\u0431\u0440\u0438\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439. \u041f\u0440\u0438\u043c\u0435\u0440 \u0442\u0430\u043a\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f \u0438 \u0444\u0430\u0431\u0440\u0438\u043a\u0438 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 \u043d\u0430\u0448\u0435\u043c \u0442\u0435\u0441\u0442\u0435 (<code>TestInfoModule<\/code> \u0438 <code>TestInfoFactory<\/code>).<\/p>\n<h2>\u0420\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b<\/h2>\n<p>\u0418\u043d\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0442\u0430\u043a, \u0447\u0442\u043e \u0443 \u043c\u043e\u0434\u0443\u043b\u044f \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f, \u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u0430 \u0441\u0438\u043b\u044c\u043d\u043e \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043e\u0442 \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u043b\u0438 \u0443 \u043c\u043e\u0434\u0443\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439, \u0438 \u0442\u0435\u0441\u0442 \u0441\u0438\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0441\u0442\u0430\u0435\u0442\u0441\u044f.\u00a0<\/p>\n<p>\u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0438\u043c\u0435\u0435\u0442 \u0441\u043c\u044b\u0441\u043b \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u0442\u0435\u0441\u0442 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u043b\u043e\u043a\u043e\u0432 <code>describe()<\/code> \u0438\u043b\u0438 \u0434\u0430\u0436\u0435 \u0440\u0430\u0437\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432.<\/p>\n<p>\u0421\u043b\u0438\u0448\u043a\u043e\u043c \u0434\u043b\u0438\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0438 \u0442\u0435\u0441\u0442\u044b \u0441\u043e \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0433\u043b\u0443\u0431\u043e\u043a\u043e\u0439 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\u044e \u0431\u043b\u043e\u043a\u043e\u0432 <code>describe()<\/code> \u0438 <code>context()<\/code> \u043f\u043b\u043e\u0445\u043e \u0447\u0438\u0442\u0430\u044e\u0442\u0441\u044f, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u043d\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b.<\/p>\n<h2>\u0412\u044b\u043d\u043e\u0441 \u043a\u043e\u0434\u0430 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438<\/h2>\n<p>\u0414\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0439 \u043f\u0443\u043d\u043a\u0442, \u043d\u043e \u0432\u0441\u0435 \u0436\u0435. \u041f\u043e\u0447\u0435\u043c\u0443-\u0442\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0441\u0447\u0438\u0442\u0430\u044e\u0442, \u0447\u0442\u043e \u0432\u044b\u043d\u043e\u0441 \u043a\u043e\u0434\u0430 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432 \u0442\u0435\u0441\u0442\u0430\u0445 \u2014 \u043f\u043b\u043e\u0445\u0430\u044f \u0438\u0434\u0435\u044f, \u043d\u043e \u044d\u0442\u043e \u043d\u0435 \u0442\u0430\u043a. \u0415\u0441\u043b\u0438 \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 \u043a\u043e\u0434 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0438\u043b\u0438 \u0441 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u043c\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u043c\u0438, \u0442\u043e \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u0443\u043b\u0443\u0447\u0448\u0438\u0442 \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c.<\/p>\n<h2>\u041d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 extension \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 <\/h2>\n<p>\u041e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 \u0432\u044b\u043d\u043e\u0441 \u043a\u043e\u0434\u0430 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043d\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0434 \u0434\u0440\u0443\u0433\u0438\u043c \u0443\u0433\u043b\u043e\u043c. \u0412 \u043d\u0430\u0448\u0435\u043c \u0442\u0435\u0441\u0442\u0435 \u0443\u0436\u0435 \u0431\u044b\u043b \u0442\u0430\u043a\u043e\u0439 \u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0432\u044b\u043d\u043e\u0441\u0438\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0432 NetworkTransportMock.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0411\u0435\u0437 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430 extension:<\/p>\n<pre><code class=\"swift\">beforeEach {     networkTransport.mockFailure(         url: \"https:\/\/info-example.com\/get_info\",         method: .get,         error: NSError()     )     presenter.didTapLoadInfoButton()  }<\/code><\/pre>\n<p>\u0421 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u0447\u0435\u0440\u0435\u0437 extension:<\/p>\n<pre><code class=\"swift\">beforeEach {     networkTransport.mockFailureForInfoLoading()     presenter.didTapLoadInfoButton()  } ..... private extension NetworkTransportMock {     static let infoURL = \"https:\/\/info-example.com\/get_info\"     ...     func mockFailureForInfoLoading() { \/\/ 36         mockFailure(             url: Self.infoURL,              method: .get,              error: NSError()         )     } } <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0412\u043e \u0432\u0440\u0435\u043c\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u0430 \u043d\u0430\u0441 \u0432 \u0446\u0435\u043b\u043e\u043c \u043d\u0435 \u0432\u043e\u043b\u043d\u0443\u0435\u0442, \u043a\u0430\u043a \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439, \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0430\u043c \u0444\u0430\u043a\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0432\u043f\u043e\u043b\u043d\u0435 \u043e\u043f\u0440\u0430\u0432\u0434\u0430\u043d \u0438 \u0443\u043b\u0443\u0447\u0448\u0430\u0435\u0442 \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c \u0442\u0435\u0441\u0442\u0430.<\/p>\n<h2>\u0421\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0441 \u043e\u0431\u0440\u0430\u0437\u0446\u043e\u043c<\/h2>\n<p>\u0418\u043d\u043e\u0433\u0434\u0430, \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043a\u0430\u0436\u0434\u043e\u0435 \u043f\u043e\u043b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e, \u043c\u043e\u0436\u043d\u043e \u0441\u0440\u0430\u0437\u0443 \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c. \u042d\u0442\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e, \u0435\u0441\u043b\u0438 \u0442\u0430\u043a\u0438\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044e\u0442\u0441\u044f \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0447\u0430\u0441\u0442\u044f\u0445 \u0442\u0435\u0441\u0442\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440<\/summary>\n<div class=\"spoiler__content\">\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u043f\u0440\u044f\u043c\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438<\/p>\n<pre><code class=\"swift\">it(\"\u0414\u0430\u043d\u043d\u044b\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0438 \u0432 \u0438\u0442\u043e\u0433\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f\") {  if case let .info(infoText: infoText, timeAgoText: timeAgoText) = lastViewState { expect(infoText) == \"Quick \u0438 Nimble \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 iOS \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435. Quick \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0430 Nimble \u2014 \u044d\u0442\u043e \u043c\u0435\u0442\u0447\u0435\u0440.\"         expect(timeAgoText) == \"5 \u0434\u043d\u0435\u0439 \u043d\u0430\u0437\u0430\u0434\" }     expect(module.viewInputSpy.invokedUpdateCount) == 3  }<\/code><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u0441 \u043e\u0431\u0440\u0430\u0437\u0446\u043e\u043c:<\/p>\n<pre><code class=\"swift\">it(\"\u0414\u0430\u043d\u043d\u044b\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0438 \u0432 \u0438\u0442\u043e\u0433\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f\") { expect(lastViewState) == self.successState() expect(module.viewInputSpy.invokedUpdateCount) == 3  }<\/code><\/pre>\n<\/div>\n<\/details>\n<h2>Force unwrap \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0442\u0435\u0441\u0442\u0430\u0445<\/h2>\n<p>\u0412 \u0442\u0435\u0441\u0442\u0430\u0445 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c force unwrap, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439. \u0415\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043d\u0435\u0442, \u0442\u043e \u0442\u0435\u0441\u0442 \u0443\u043f\u0430\u0434\u0435\u0442, \u0430 \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0432 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u043c \u043a\u043e\u0434\u0435 \u043f\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<h2>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u0432 CocoaPods<\/h2>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 CocoaPods \u0434\u043b\u044f \u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 \u043c\u043e\u0434\u0443\u043b\u0438, \u0442\u043e \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c <a href=\"https:\/\/guides.cocoapods.org\/using\/test-specs.html\"><u>\u0432\u043e\u0442 \u0442\u0430\u043a<\/u><\/a>. <\/p>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b \u0437\u043d\u0430\u0435\u0442\u0435 \u043e \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Quick \u0438 Nimble \u0447\u0443\u0442\u043e\u0447\u043a\u0443 \u0431\u043e\u043b\u044c\u0448\u0435. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u043e \u043e\u043d \u0437\u0430\u0442\u0440\u0430\u0433\u0438\u0432\u0430\u0435\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0430\u0441\u043f\u0435\u043a\u0442\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0435\u0441\u0442\u0430\u043c\u0438. \u0422\u0443\u0442 \u0435\u0441\u0442\u044c \u043c\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u043c\u043e\u0434\u0443\u043b\u044f \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u043e\u0432. \u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0435\u0440\u0435\u043b\u043e\u0436\u0438\u0442\u044c \u044d\u0442\u0438 \u0437\u043d\u0430\u043d\u0438\u044f \u043d\u0430 \u043c\u043e\u0434\u0443\u043b\u0438 \u0438\u0437 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0430\u0440\u0443 \u0441\u0432\u043e\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432. \u041b\u0443\u0447\u0448\u0435 \u043d\u0430\u0447\u0430\u0442\u044c \u0441 \u043d\u043e\u0432\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f, \u0442\u0430\u043a \u043a\u0430\u043a \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c \u0441\u0442\u0430\u0440\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u043a \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u043c\u043e\u0436\u0435\u0442 \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043d\u0435\u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0435\u0439, \u0443\u0431\u0438\u0432\u0430\u044e\u0449\u0435\u0439 \u0432\u0441\u0435 \u0436\u0435\u043b\u0430\u043d\u0438\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b. \u0412\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0437\u0430\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0435\u0431\u0435 \u0432\u043e\u043f\u0440\u043e\u0441: \u0443\u0434\u043e\u0431\u043d\u043e \u043b\u0438 \u043c\u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0435\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c? <\/p>\n<p>\u0425\u043e\u0440\u043e\u0448\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u043d\u0443\u0436\u043d\u044b \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0431\u0430\u0433\u043e\u0432, \u043d\u043e \u0438 \u0438\u043c\u0435\u044e\u0442 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0435 \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u043d\u0438 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u044e\u0442 \u0432 \u0440\u043e\u043b\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043a \u043a\u043e\u0434\u0443, \u0441\u043f\u043e\u0441\u043e\u0431\u0441\u0442\u0432\u0443\u044e\u0442 \u043f\u0435\u0440\u0435\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u0432 \u043b\u0443\u0447\u0448\u0443\u044e \u0441\u0442\u043e\u0440\u043e\u043d\u0443 \u0438 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u0438\u0437\u0431\u0435\u0433\u0430\u0442\u044c \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u043b\u0435\u0433\u0430\u0441\u0438. \u0427\u0442\u043e\u0431\u044b \u043d\u0430\u0447\u0430\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b, \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0438\u0442\u044c \u043d\u0435\u043c\u0430\u043b\u043e \u0443\u0441\u0438\u043b\u0438\u0439. \u0421\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u0441 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0440\u0430\u0437\u0430 \u043d\u0435 \u0432\u0441\u0451 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e, \u0438 \u043a\u043e\u0434 \u043c\u043e\u0434\u0443\u043b\u044f \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u043e\u043a \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432. \u0418\u043b\u0438 \u0442\u0435\u0441\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u044b\u043c\u0438 \u0432 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0435, \u0438 \u0438\u0445 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043e\u0442\u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u0442\u044c \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c. \u041f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043c\u043d\u043e\u0433\u043e \u0438\u0441\u043a\u0430\u0442\u044c \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0445 \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u0432 \u0434\u043b\u044f \u043f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0442\u0435\u0441\u0442\u043e\u0432. \u041d\u043e, \u043f\u043e\u0432\u0435\u0440\u044c\u0442\u0435, \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 \u0445\u043e\u0440\u043e\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u043b\u0435\u0433\u043a\u043e \u043e\u043a\u0443\u043f\u0430\u044e\u0442 \u0443\u0441\u0438\u043b\u0438\u044f, \u043f\u043e\u0442\u0440\u0430\u0447\u0435\u043d\u043d\u044b\u0435 \u043d\u0430 \u043d\u0438\u0445!<\/p>\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\/582762\/\"> https:\/\/habr.com\/ru\/articles\/582762\/<\/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<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h3>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0438 \u0437\u0430\u0447\u0435\u043c \u0438\u0445 \u043f\u0438\u0441\u0430\u0442\u044c?<\/h3>\n<p>\u041f\u0435\u0440\u0435\u0434 \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u0435\u043c \u044f \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 <a href=\"https:\/\/habr.com\/ru\/company\/cian\/blog\/567358\/\"><u>\u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435\u0439 \u0441\u0435\u0440\u0438\u0438<\/u><\/a>. \u041e\u043d\u0430 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u0447\u0435\u043c \u0445\u043e\u0440\u043e\u0448\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u043e\u0442 \u043f\u043b\u043e\u0445\u0438\u0445, \u043a\u0430\u043a\u0438\u0435 \u0432\u0438\u0434\u044b \u0442\u0435\u0441\u0442\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442 \u0438 \u0432 \u043a\u0430\u043a\u0438\u0445 \u043f\u0440\u043e\u043f\u043e\u0440\u0446\u0438\u044f\u0445 \u043e\u043d\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f. \u042d\u0442\u043e\u0442 \u0436\u0435 \u043f\u043e\u0441\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0441\u0444\u043e\u043a\u0443\u0441\u0438\u0440\u043e\u0432\u0430\u043d \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Quick \u0438 Nimble, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0442\u0435\u043e\u0440\u0438\u044f \u0438\u0437 \u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u0430\u043c \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0432 \u0441\u0435\u0440\u0438\u0438 \u043c\u044b <a href=\"https:\/\/habr.com\/ru\/company\/cian\/blog\/570988\/\"><u>\u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e UI-\u0442\u0435\u0441\u0442\u044b<\/u><\/a>.<\/p>\n<p>\u0414\u043e \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u043d\u0430\u0447\u0430\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b, \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f, \u0447\u0435\u043c \u043e\u043d\u0438 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u043e\u0442 Unit-\u0442\u0435\u0441\u0442\u043e\u0432. \u042d\u0442\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0434\u0438\u0441\u043a\u0443\u0441\u0441\u0438\u043e\u043d\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441, \u0438 \u043e\u043d \u0443\u0436\u0435 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u043b\u0441\u044f \u0432 <a href=\"https:\/\/habr.com\/ru\/company\/cian\/blog\/567358\/\"><u>\u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/u><\/a>, \u043d\u043e \u044f \u0434\u0443\u043c\u0430\u044e, \u0442\u0443\u0442 \u0441\u0442\u043e\u0438\u0442 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c\u0441\u044f. Unit-\u0442\u0435\u0441\u0442\u043e\u043c \u043c\u044b \u0441\u0447\u0438\u0442\u0430\u0435\u043c \u0442\u0435\u0441\u0442 \u043d\u0430 \u043e\u0434\u0438\u043d \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441, \u0433\u0434\u0435 \u0432\u0441\u0435 \u0435\u0433\u043e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0437\u0430\u043a\u0440\u044b\u0442\u044b \u043c\u043e\u043a\u0430\u043c\u0438. \u0410 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u043c \u043c\u044b \u0441\u0447\u0438\u0442\u0430\u0435\u043c \u0442\u0435\u0441\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442 \u0441\u0432\u044f\u0437\u043a\u0443 \u043a\u043b\u0430\u0441\u0441\u043e\u0432 (\u043c\u043e\u0434\u0443\u043b\u044c) \u0441 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u043c\u043e\u043a\u043e\u0432. \u0418\u043d\u043e\u0433\u0434\u0430 \u0442\u0430\u043a\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u0435\u0449\u0435 \u043c\u043e\u0433\u0443\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u043c\u0438 \u0438\u043b\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u043c\u0438.<\/p>\n<p>\u041c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u043c\u043e\u0434\u0443\u043b\u044f (\u0447\u0438\u0442\u0430\u0439: \u044d\u043a\u0440\u0430\u043d\u0430) \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043c\u043e\u043a\u043e\u0432 \u0438 \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043a. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u043b\u0438\u0436\u0435 \u043a \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u043c\u043e\u0434\u0443\u043b\u044e \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435, \u0447\u0435\u043c \u0435\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u0432\u0441\u0435 \u0447\u0430\u0441\u0442\u0438 \u043c\u043e\u0434\u0443\u043b\u044f \u043f\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438. \u0412\u0435\u0434\u044c \u0435\u0441\u043b\u0438 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043a\u043b\u0430\u0441\u0441\u0430 \u043d\u0430 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u0438 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0442\u043e \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0443\u0437\u043d\u0430\u0442\u044c \u043b\u0438\u0448\u044c \u0442\u043e, \u0447\u0442\u043e \u043a\u043b\u0430\u0441\u0441 \u0445\u043e\u0440\u043e\u0448\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430\u043c\u0438, \u043d\u043e \u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043a\u043b\u0430\u0441\u0441\u0430\u043c\u0438 \u043c\u044b \u0443\u0437\u043d\u0430\u0435\u043c \u043c\u0430\u043b\u043e.<\/p>\n<p>\u0412 \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u0430\u0445 \u043d\u0430\u0441 \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0438\u0441\u0442\u043e\u0440\u0438\u0439, \u0430 \u043d\u0435 \u0434\u0435\u0442\u0430\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0435 \u0447\u0430\u0441\u0442\u0438 \u043c\u043e\u0434\u0443\u043b\u044f. \u0422\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 \u0441\u0438\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u043c\u043e\u0434\u0443\u043b\u044f \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0441\u0430\u043c\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c. \u0414\u0430\u0436\u0435 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0437\u0430\u043c\u0435\u043d\u044b \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043c\u043e\u0434\u0443\u043b\u044f \u043d\u0435 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u044e\u0449\u0438\u0435 \u0435\u0433\u043e \u0442\u0435\u0441\u0442\u044b \u0441 \u043d\u0443\u043b\u044f.<\/p>\n<p>\u041f\u0440\u0438 \u0432\u044b\u0431\u043e\u0440\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438\u0441\u044c \u043d\u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430\u0445 Quick \u0438 Nimble, \u0438 \u043e\u043d\u0438 \u043d\u0435\u043f\u043b\u043e\u0445\u043e \u0441\u0435\u0431\u044f \u0437\u0430\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u043e\u0432\u0430\u043b\u0438. \u0421 \u0438\u0445 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0434\u043e\u0431\u043d\u043e \u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u043e\u043d\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0438\u0437\u0431\u0435\u0433\u0430\u0442\u044c \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u044b\u043c\u0438 \u0438 \u0432\u044b\u0440\u0430\u0437\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438.<\/p>\n<h4>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Quick, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Nimble?<\/h4>\n<p>\u0415\u0441\u043b\u0438 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u0432\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u043e, \u0442\u043e Quick \u2014 \u044d\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a, \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0449\u0438\u0439 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432, \u0430 Nimble \u2014 \u044d\u0442\u043e \u043c\u0435\u0442\u0447\u0435\u0440. \u041f\u0440\u043e\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435, \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0449\u0435\u043c \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u044d\u0442\u0438\u0445 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432. \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441 \u043a\u0430\u043b\u044c\u043a\u0443\u043b\u044f\u0442\u043e\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043c\u0435\u0435\u0442 \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0442\u044c \u0447\u0438\u0441\u043b\u0430.\u00a0<\/p>\n<pre><code class=\"swift\">override func spec() {     describe(\"\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c SumCalculator\") { \/\/ 1         var calculator: SumCalculator!         beforeEach { \/\/ 2             calculator = SumCalculator()         }         context(\"\u0415\u0441\u043b\u0438 \u0441\u043b\u043e\u0436\u0438\u0442\u044c 1 \u0438 2\") { \/\/ 3             beforeEach { \/\/ 4                 calculator.sum(1, 2)             }             it(\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0432\u0435\u043d 3\") { \/\/ 5                 expect(calculator.result).to(equal(3)) \/\/ 6             }         }         context(\"\u0415\u0441\u043b\u0438 \u0441\u043b\u043e\u0436\u0438\u0442\u044c 1 \u0438 -1\") { \/\/ 7             beforeEach { \/\/ 8                 calculator.sum(1, -1) \/\/ 9             }             it(\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0432\u0435\u043d 0\") { \/\/ 10                 expect(calculator.result) == 0 \/\/ 11             }         }     } }<\/code><\/pre>\n<p>\u0412\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f <code>expect(calculator.result).to(equal(3))<\/code> (3) \u0438 <code>expect(calculator.result) == 0 (11)<\/code> \u043e\u0442\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u043a \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0443 Nimble, \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e \u043d\u0438\u0445 \u0447\u0443\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<p>\u0410 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>describe()<\/code> (1), <code>beforeEach()<\/code> (2, 4, 8), <code>context()<\/code> (3, 7) \u0438 <code>it()<\/code> (5, 10) \u043e\u0442\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u043a \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0443 Quick. \u041e\u043d\u0438 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u0442\u0435\u0441\u0442\u043e\u0432 \u0438 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0431\u0430\u0437\u043e\u0439 \u044d\u0442\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430. \u0414\u0430\u043b\u0435\u0435 \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0431\u043b\u043e\u043a\u0430\u043c\u0438.<\/p>\n<p>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e \u0431\u043b\u043e\u043a\u0430\u0445<\/p>\n<ol>\n<li>\n<p><code>describe()<\/code> \u2014 \u0431\u043b\u043e\u043a, \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u0432 \u043c\u0435\u0442\u043e\u0434 <code>spec()<\/code> \u0438 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u0442\u0435\u0441\u0442-\u043a\u0435\u0439\u0441\u043e\u0432. \u0411\u043b\u043e\u043a\u043e\u0432 <code>describe()<\/code> \u0432\u043d\u0443\u0442\u0440\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>spec()<\/code> \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0431\u0438\u0442\u044c \u043e\u0434\u0438\u043d \u0431\u043e\u043b\u044c\u0448\u043e\u0439 <code>describe()<\/code> \u0434\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u0438 \u043a\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><code>context()<\/code> \u2014\u00a0 \u0442\u0430\u043a\u043e\u0439 \u0431\u043b\u043e\u043a, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043a\u0430\u043a\u043e\u0435-\u043b\u0438\u0431\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0438\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0438. \u0411\u043b\u043e\u043a\u0438 <code>context()<\/code> \u0432\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u0431\u043b\u043e\u043a\u0438 <code>describe()<\/code> \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0431\u043b\u043e\u043a\u0438 <code>context()<\/code>. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u044d\u0442\u0438\u0445 \u0431\u043b\u043e\u043a\u043e\u0432 \u0443\u0434\u043e\u0431\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0432 \u0432\u0438\u0434\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \u201c\u0415\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043b\u0438\u0441\u044c \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u201d \u0438\u043b\u0438 \u201c\u0415\u0441\u043b\u0438 \u0432\u0432\u0435\u043b\u0438 \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435\u201d.<\/p>\n<\/li>\n<li>\n<p><code>beforeEach()<\/code> \u2014 \u0442\u0430\u043a\u043e\u0439 \u0431\u043b\u043e\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432\u043d\u0443\u0442\u0440\u0438 <code>describe()<\/code> \u0438\u043b\u0438 <code>context()<\/code>. \u041a\u043e\u0434 \u0432\u043d\u0443\u0442\u0440\u0438 <code>beforeEach()<\/code> \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0432 \u043d\u043e\u0432\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435. \u0416\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043d\u0430\u0434 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043b\u0438\u0441\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u044d\u0442\u0438\u0445 \u0431\u043b\u043e\u043a\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><code>it()<\/code> \u2014 \u0432 \u044d\u0442\u0438\u0445 \u0431\u043b\u043e\u043a\u0430\u0445 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0432 <code>beforeEach()<\/code> \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u0440\u0438\u0432\u0435\u043b\u0438 \u043a \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c.\u00a0 \u0412 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0431\u043b\u043e\u043a\u0430 \u043e\u0431\u044b\u0447\u043d\u043e \u0432\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \u201cView \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c State\u201d \u0438\u043b\u0438 \u201c\u0412\u043e View \u0431\u044b\u043b\u0430 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0430\u201d.<\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u043e\u0440\u044f\u0434\u043e\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0431\u043b\u043e\u043a\u043e\u0432 \u043a\u043e\u0434\u0430:<\/p>\n<p>\u041f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043f\u043e\u0440\u044f\u0434\u043a\u0430 \u0432\u044b\u0437\u043e\u0432\u0430 \u0431\u043b\u043e\u043a\u043e\u0432 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043b\u044e\u0447\u043e\u043c \u043a \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044e \u0440\u0430\u0431\u043e\u0442\u044b Quick. \u0412\u0430\u0436\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c, \u0447\u0442\u043e \u0431\u043b\u043e\u043a\u0438 <code>it()<\/code> \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u0440\u0443\u0433 \u0437\u0430 \u0434\u0440\u0443\u0433\u043e\u043c. \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0431\u043b\u043e\u043a\u0430 <code>it()<\/code> \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u0437\u043e\u0432\u043e\u0432, \u0441\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0431\u043b\u043e\u043a\u043e\u0432 <code>describe()<\/code>, <code>context()<\/code> \u0438 <code>it()<\/code>. \u0412 \u044d\u0442\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u0437\u043e\u0432\u0430 \u0431\u043b\u043e\u043a\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0442\u0430\u043a\u0430\u044f: [2, 4, 5] \u0438 [2, 8, 10], \u0442.\u0435. \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e <code>it()<\/code> (5, 10). \u041d\u043e \u043d\u0438\u043a\u0430\u043a \u043d\u0435 [2, 4, 5, 8, 10].<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u0437\u0430\u0445\u043e\u0442\u0435\u043b\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043d\u0430\u0431\u043e\u0440 \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430 Quick \u0432 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0435 XCTest, \u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b\u0438 \u0431\u044b \u043e\u043d\u0438 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0441 XCTest<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">func testSum1And2() {     \/\/ Arrange     let calculator = SumCalculator()     \/\/ Act     calculator.sum(1, 2)     \/\/ Assert     XCTAssertEqual(calculator.result, 3) }      func testSum1AndMinus1() {     \/\/ Arrange     let calculator = SumCalculator()     \/\/ Act     calculator.sum(1, -1)     \/\/ Assert     XCTAssertEqual(calculator.result, 0) }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0412\u0430\u0436\u043d\u044b\u0435 \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u044f:<\/p>\n<ol>\n<li>\n<p>\u0424\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0431\u043b\u043e\u043a <code>it()<\/code> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u0430\u0436\u043d\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043a \u043c\u043e\u043c\u0435\u043d\u0442\u0443 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0431\u043b\u043e\u043a\u0430 <code>it()<\/code> \u0432\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0431\u044b\u043b\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438. <\/p>\n<\/li>\n<li>\n<p>\u0412\u0430\u0436\u043d\u043e \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u0437\u0430 \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c\u044e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0439 \u0431\u043b\u043e\u043a\u043e\u0432 <code>describe()<\/code>, <code>context()<\/code> \u0438 <code>it()<\/code>. \u0427\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c \u2014 \u043e\u0434\u043d\u0430 \u0438\u0437 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a \u0442\u0435\u0441\u0442\u0430. \u0410 \u0435\u0449\u0435 \u044d\u0442\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043f\u0440\u043e\u0449\u0435 \u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0443\u043f\u0430\u0432\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u043a\u043e\u0434 \u0432\u043d\u0435 \u0431\u043b\u043e\u043a\u043e\u0432 <code>beforeEach()<\/code> \u0438\u043b\u0438 <code>it()<\/code>. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044e \u0442\u0435\u0441\u0442\u0430. \u041d\u043e \u0432\u043d\u0435 \u044d\u0442\u0438\u0445 \u0431\u043b\u043e\u043a\u043e\u0432 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044f \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435, \u043a\u0430\u043a, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>SumCalculator<\/code> \u0438\u0437 \u0442\u0435\u0441\u0442\u0430, \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u0448\u0435. \u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u2014 \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0442\u044c \u0435\u043c\u0443 \u043d\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0431\u043b\u043e\u043a\u0430\u0445 <code>beforeEach()<\/code>. <\/p>\n<\/li>\n<li>\n<p>\u0411\u043b\u043e\u043a\u0438 \u043d\u0430 \u043e\u0434\u043d\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043d\u0435 \u0432 \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u0432 \u043a\u043e\u0434\u0435, \u0430 \u0432 \u0430\u043b\u0444\u0430\u0432\u0438\u0442\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u0438\u0445 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0439. \u041d\u043e \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u043d\u0430 \u044d\u0442\u043e \u0437\u0430\u0432\u044f\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f. \u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u043b\u0443\u0447\u0448\u0435 \u0441\u0447\u0438\u0442\u0430\u0442\u044c, \u0447\u0442\u043e \u043e\u043d\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435.<\/p>\n<\/li>\n<\/ol>\n<h2>\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0432 \u043f\u043e \u043f\u043e\u0432\u043e\u0434\u0443 Nimble<\/h2>\n<p>Nimble \u2014 \u044d\u0442\u043e \u043c\u0435\u0442\u0447\u0435\u0440. \u041c\u0435\u0442\u0447\u0435\u0440 \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0438 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432. \u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 Nimble \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 <code>expect(calculator.result).to(equal(3))<\/code> (6) \u0438 <code>expect(calculator.result) == 0<\/code> (11). \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 <code>==<\/code> \u0438 <code>.to(equal())<\/code> \u044d\u043a\u0432\u0438\u0432\u0430\u043b\u0435\u043d\u0442\u044b. \u041d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, <code>==<\/code> \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u0447\u0443\u0442\u044c \u043b\u0443\u0447\u0448\u0435, \u043d\u043e \u0442\u0443\u0442 \u0443\u0436\u0435 \u0434\u0435\u043b\u043e \u0432\u043a\u0443\u0441\u0430, \u043a\u0430\u0436\u0434\u044b\u0439 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442, \u0447\u0442\u043e \u0435\u043c\u0443 \u0443\u0434\u043e\u0431\u043d\u0435\u0435.<\/p>\n<p>\u0415\u0449\u0435 \u0438\u0437 \u0432\u0430\u0436\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 Nimble \u0441\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 <code>toEventually()<\/code>. \u041e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438 \u0432\u044b\u0437\u043e\u0432\u0430\u043c\u0438 \u0438 \u0436\u0434\u0430\u0442\u044c \u043c\u043e\u043c\u0435\u043d\u0442\u0430, \u043a\u043e\u0433\u0434\u0430 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u0441\u0442\u0430\u043d\u0435\u0442 \u0432\u0435\u0440\u043d\u044b\u043c. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0435\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p>\u0410 \u0435\u0449\u0435 \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c <a href=\"https:\/\/github.com\/Quick\/Nimble\"><u>\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e<\/u><\/a>, \u0442\u0430\u043c \u043c\u043d\u043e\u0433\u043e \u0432\u0441\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 Nimble \u0435\u0441\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439, \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043d\u0435\u0442\u043e\u0447\u043d\u044b\u0435 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0447\u0438\u0441\u0435\u043b \u0441 \u043f\u043b\u0430\u0432\u0430\u044e\u0449\u0435\u0439 \u0442\u043e\u0447\u043a\u043e\u0439), \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \u0438 \u043c\u043d\u043e\u0433\u043e \u0434\u0440\u0443\u0433\u043e\u0435.<\/p>\n<h2>\u0427\u0442\u043e \u0431\u0443\u0434\u0435\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c?<\/h2>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u0443\u0434\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044c, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u0439 \u0437\u0430 \u044d\u043a\u0440\u0430\u043d \u0432\u043e\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u0444\u0430\u043a\u0442\u043e\u0432. \u0412 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043c\u043e\u0434\u0443\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0441\u043b\u043e\u0438\u0441\u0442\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0441 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435\u043c View, Presenter \u0438 Service. \u0422\u0443\u0442 \u043c\u043e\u0433\u043b\u0430 \u0431\u044b \u0431\u044b\u0442\u044c \u043b\u044e\u0431\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 View \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u0434\u0435\u043b\u0438\u0442\u044c \u043e\u0442 \u043b\u043e\u0433\u0438\u043a\u0438. \u042d\u0442\u043e \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0432\u0435\u0449\u0438, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441 UIKit, \u043e\u0447\u0435\u043d\u044c \u043d\u0435\u0443\u0434\u043e\u0431\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c.\u00a0<\/p>\n<p>\u041d\u0430\u043c \u0432\u0430\u0436\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0438\u0437 View-\u0441\u043b\u043e\u044f, \u0447\u0442\u043e\u0431\u044b \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0448\u0438\u0431\u043e\u043a \u0432 \u043d\u0435\u043c \u0431\u044b\u043b\u0430 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430. \u041a\u0440\u043e\u043c\u0435 \u044d\u0442\u043e\u0433\u043e, \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 <code>Date()<\/code> \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0430 \u0432 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 <code>DateProvider<\/code>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 <code>Date()<\/code> \u00a0\u2014 \u044d\u0442\u043e \u043d\u0435\u044f\u0432\u043d\u0430\u044f \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c\u0430\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0432\u043b\u0438\u044f\u0442\u044c \u043d\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.\u00a0<\/p>\n<p>\u0420\u0430\u0431\u043e\u0442\u0443 \u0441 \u043b\u043e\u043a\u0430\u0446\u0438\u0435\u0439 (\u0442\u0443\u0442 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f) \u0442\u043e\u0436\u0435 \u043b\u0443\u0447\u0448\u0435 \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u043c \u0438 \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0438\u0437 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u043a\u043e\u0434\u0430. \u0410 \u0432\u043e\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0431\u0430\u0437\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 (\u0442\u0443\u0442 \u0442\u043e\u0436\u0435 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f), <code>UserDefault<\/code> \u0438 \u0434\u0440\u0443\u0433\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432 \u043a\u043e\u0434\u0435, \u0433\u043b\u0430\u0432\u043d\u043e\u0435 \u043d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0442\u044c \u043e\u0447\u0438\u0449\u0430\u0442\u044c \u044d\u0442\u0438 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0435\u0440\u0435\u0434 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u0442\u0435\u0441\u0442\u0430. <\/p>\n<h2>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043c\u043e\u0434\u0443\u043b\u044f<\/h2>\n<p>\u041c\u043e\u0434\u0443\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442. \u041f\u0440\u0438 \u0437\u0430\u0445\u043e\u0434\u0435 \u043d\u0430 \u044d\u043a\u0440\u0430\u043d \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u043d\u043e\u043f\u043a\u0430 \u201c\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u201d.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043d\u0435\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043b\u043e\u0430\u0434\u0435\u0440 \u0438 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u043e\u043f\u044b\u0442\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0441\u0435\u0442\u0438.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442 \u0438 \u0435\u0433\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438. \u041f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u201c\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435\u201d \u043e\u0442\u043a\u0440\u043e\u0435\u0442\u0441\u044f \u044d\u043a\u0440\u0430\u043d \u0441 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439. \u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0442\u0430\u0442\u044c\u0438 \u044d\u043a\u0440\u0430\u043d \u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043d\u0430\u0441 \u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442, \u0441\u0444\u043e\u043a\u0443\u0441\u0438\u0440\u0443\u0435\u043c\u0441\u044f \u043d\u0430 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0440\u0438 \u043d\u0435\u0443\u0434\u0430\u0447\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0430\u043b\u0435\u0440\u0442 \u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435. \u041f\u0440\u0438 \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u0438 \u0430\u043b\u0435\u0440\u0442\u0430 \u0441\u043d\u043e\u0432\u0430 \u0432\u0438\u0434\u043d\u043e \u043a\u043d\u043e\u043f\u043a\u0443 \u201c\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u201d.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u043a\u043e\u0434\u0435. \u041d\u0430\u0448 \u043c\u043e\u0434\u0443\u043b\u044c \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0447\u0430\u0441\u0442\u0435\u0439: View, Presenter, Service, Router \u0438 NetworkTransport. \u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043a\u0430\u0436\u0434\u0443\u044e \u0438\u0437 \u043d\u0438\u0445 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e. \u041f\u0440\u0438\u043d\u0435\u0441\u0435\u043c \u0432 \u0436\u0435\u0440\u0442\u0432\u0443 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0440\u043e\u0448\u0438\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0440\u0430\u0434\u0438 \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e\u0441\u0442\u0438 \u0441\u0442\u0430\u0442\u044c\u0438, \u0442\u0430\u043a \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u043a\u0430\u0436\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043a\u043e\u0434 \u043c\u043e\u0433 \u0431\u044b \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u0435\u0435, \u0442\u043e, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u043e\u043d\u043e \u0442\u0430\u043a \u0438 \u0435\u0441\u0442\u044c. <\/p>\n<h2>Network<\/h2>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 NetworkTransport \u0438 Service. \u0415\u0441\u0442\u044c \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b NetworkTransport, \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u044e\u0449\u0438\u0439 NetworkTransportImpl (\u0441\u0430\u043c \u043a\u043e\u0434 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u043d\u0435 \u0431\u0443\u0434\u0443, \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u043e\u043d \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 URLSession), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043c\u0435\u0435\u0442 \u0445\u043e\u0434\u0438\u0442\u044c \u0432 \u0441\u0435\u0442\u044c \u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u043c\u0438. <\/p>\n<pre><code class=\"swift\">struct Request {     enum Method {         case get         case post     }     let method: Method     let url: String     let params: [String: String] }   protocol NetworkTransport {     func load&lt;T: Decodable>(         request: Request,         onResult: @escaping ((Result&lt;T, Error>) -> Void)     ) }<\/code><\/pre>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0432\u043d\u0443\u0442\u0440\u0438 InfoService. \u042d\u0442\u043e \u0441\u0435\u0440\u0432\u0438\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u043d\u0430\u0435\u0442, \u043f\u043e \u043a\u0430\u043a\u043e\u043c\u0443 URL \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c\u0441\u044f \u0438 \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441.<\/p>\n<pre><code class=\"swift\">final class InfoService {     private let networkTransport: NetworkTransport<\/code><\/pre>\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-394579","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/394579","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=394579"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/394579\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=394579"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=394579"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=394579"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}