{"id":402497,"date":"2024-06-29T16:32:38","date_gmt":"2024-06-29T16:32:38","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=402497"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T16:32:38","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=402497","title":{"rendered":"<span>\u0412\u043d\u0435\u0434\u0440\u044f\u0435\u043c Snapshot testing \u0432 UI-\u0442\u0435\u0441\u0442\u044b iOS<\/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\/w1560\/getpro\/habr\/upload_files\/00c\/613\/bc1\/00c613bc1035eb5a2346331632ee5ff6.png\" width=\"772\" height=\"430\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/00c\/613\/bc1\/00c613bc1035eb5a2346331632ee5ff6.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0425\u0430\u0431\u0440, \u043f\u0440\u0438\u0432\u0435\u0442!<\/p>\n<p>\u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0411\u043e\u0440\u0438\u0441. \u042f AQA iOS-engineer \u0432 Vivid Money.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0445\u043e\u0447\u0443 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u0440\u043e snapshot \u0442\u0435\u0441\u0442\u044b \u0438 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0432 \u0441\u0432\u043e\u0451\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<p>\u0414\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u043c iOS-\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430\u043c, \u043b\u0438\u0431\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0435\u0448\u0438\u043b\u0438 \u0438\u0437\u0443\u0447\u0438\u0442\u044c XCUITest \u0438 \u043f\u043e\u043a\u0440\u044b\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 ui-\u0442\u0435\u0441\u0442\u0430\u043c\u0438.<\/p>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c:<\/p>\n<ol>\n<li>\n<p>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 snapshot \u0442\u0435\u0441\u0442\u044b \u0438 \u043a\u043e\u0433\u0434\u0430 \u0438\u0445 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a\u0438\u0435 \u0435\u0441\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u044d\u0442\u0430\u043f\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0438 \u043f\u043e\u043a\u0430\u0436\u0443 \u043a\u0430\u043a \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c \u0438\u0445 \u0443 \u0441\u0435\u0431\u044f \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<\/li>\n<\/ol>\n<h2>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Snapshot \u0442\u0435\u0441\u0442\u044b?<\/h2>\n<p>Snapshot \u0442\u0435\u0441\u0442\u044b \u2014 \u044d\u0442\u043e \u0442\u0435\u0441\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u043b\u0430\u044e\u0442 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u044d\u043a\u0440\u0430\u043d\u0430 (\u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442) \u0438 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u044e\u0442 \u0441 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0433\u043e\u043d\u0430 \u0442\u0435\u0441\u0442\u043e\u0432. \u0414\u0435\u043b\u0430\u0442\u044c Snapshot \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u0432\u0441\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u2014 \u043f\u043b\u043e\u0445\u0430\u044f \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0430, \u0442\u0430\u043a \u043a\u0430\u043a \u0435\u0441\u043b\u0438 \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u0434\u0438\u0437\u0430\u0439\u043d, \u0432\u0430\u0448\u0438 \u0442\u0435\u0441\u0442\u044b \u043c\u043e\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e \u0441\u0442\u0430\u043d\u0443\u0442 \u043a\u0440\u0430\u0441\u043d\u044b\u043c\u0438, \u0438 \u0432\u0430\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0432 \u0441\u0440\u043e\u0447\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043c\u0435\u043d\u044f\u0442\u044c \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0435 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u044b. \u042f \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Snapshot \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432\u0435\u0440\u0441\u0442\u043a\u0438, \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0442\u044f\u0436\u0435\u043b\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u043f\u043e accessibilityidentifier (\u0433\u0440\u0430\u0444\u0438\u043a \u0446\u0435\u043d, \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044c \u0438 \u043f\u0440\u043e\u0447\u0438\u0435).<\/p>\n<h2>\u041a\u0430\u043a\u0438\u0435 \u0435\u0441\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438?<\/h2>\n<p>\u0412 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c, \u043f\u0440\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0438 Snapshot \u0442\u0435\u0441\u0442\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0434\u0432\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438:<\/p>\n<ol>\n<li>\n<p><a href=\"https:\/\/github.com\/uber\/ios-snapshot-test-case\">iOSSnapshotTestCase (previously FBSnapshotTestCase)<\/a>.<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/pointfreeco\/swift-snapshot-testing\">SnapshotTesting<\/a>.<\/p>\n<\/li>\n<\/ol>\n<p>\u0418\u0445 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0440\u0430\u0437\u043b\u0438\u0447\u0438\u044f:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p><strong>\u0424\u0438\u0447\u0438<\/strong><\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p><strong>iOSSnapshotTestCase<\/strong><\/p>\n<\/td>\n<td>\n<p><strong>SnapshotTesting<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u042f\u0437\u044b\u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>Objective-C<\/p>\n<\/td>\n<td>\n<p>Swift<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Diff \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u044b<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>\u0415\u0441\u0442\u044c<\/p>\n<\/td>\n<td>\n<p>\u0415\u0441\u0442\u044c<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u0421\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u044b \u043b\u044e\u0431\u043e\u0433\u043e UI-\u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>\u041d\u0443\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u043c\u0443<\/p>\n<\/td>\n<td>\n<p>\u0415\u0441\u0442\u044c<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u0413\u0438\u0431\u043a\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u043e\u0433\u0440\u0435\u0448\u043d\u043e\u0441\u0442\u0438 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u044f \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u043e\u0432<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>2 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430<\/p>\n<\/td>\n<td>\n<p>1 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0434\u0430\u0442\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>12 \u043e\u043a\u0442\u044f\u0431\u0440\u044f 2019<\/p>\n<\/td>\n<td>\n<p>27 \u0430\u043f\u0440\u0435\u043b\u044f 2021<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u044b \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>CocoaPods, Carthage<\/p>\n<\/td>\n<td>\n<p>CocoaPods, Carthage, Swift Package Manager<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<h2>\u0412\u043d\u0435\u0434\u0440\u044f\u0435\u043c \u0441\u043d\u044d\u043f\u0448\u043e\u0442 \u0442\u0435\u0441\u0442\u044b \u0432 \u043f\u0440\u043e\u0435\u043a\u0442<\/h2>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u043e\u0432 \u043d\u0438\u0436\u0435, \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<ol>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u0440\u0433\u0435\u0442 ui-\u0442\u0435\u0441\u0442\u043e\u0432 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442. \u041a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 <a href=\"https:\/\/habr.com\/ru\/company\/vivid_money\/blog\/533180\/\">\u0441\u0442\u0430\u0442\u044c\u0435<\/a> \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 &#171;\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0441\u0440\u0435\u0434\u0443&#187;.<\/p>\n<\/li>\n<li>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 iOSSnapshotTestCase \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435. \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u0435\u0441\u0442\u044c \u0432 <a href=\"https:\/\/github.com\/uber\/ios-snapshot-test-case#installation\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438<\/a>.<\/p>\n<\/li>\n<\/ol>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u0432\u0441\u0435 \u043f\u043e\u044d\u0442\u0430\u043f\u043d\u043e:<\/p>\n<h3>\u041f\u0438\u0448\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e<\/h3>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043a\u0430\u0447\u0430\u043d\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0438 \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 FBSnapshotTestCase, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043d\u0443\u0436\u043d\u044b\u043c \u043c\u0435\u0442\u043e\u0434\u0430\u043c.<\/p>\n<pre><code class=\"swift\">import FBSnapshotTestCase  class BullseyeUITests: FBSnapshotTestCase {          let app = XCUIApplication()      override func setUp() {         super.setUp()                  app.launch()     }  }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430, \u0447\u0442\u043e\u0431\u044b \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u043b\u044e\u0431\u043e\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f ui-\u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<pre><code class=\"swift\">\/\/ \u0414\u0435\u043b\u0430\u0435\u043c \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u0432\u0441\u0435\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430 func verifyView(identifier: String, perPixelTolerance: CGFloat = 0.0, overallTolerance: CGFloat = 0.05 ) {                  let screenshot = app.screenshot().image                  FBSnapshotVerifyView(UIImageView(image: screenshot), identifier: identifier, perPixelTolerance: perPixelTolerance, overallTolerance: overallTolerance)     }  \/\/ \u0414\u0435\u043b\u0430\u0435\u043c \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430     func verifyElement(element: XCUIElement, identifier: String, perPixelTolerance: CGFloat = 0.0, overallTolerance: CGFloat = 0.05 ) {                  FBSnapshotVerifyView(UIImageView(image: element.screenshot().image), identifier: identifier, perPixelTolerance: perPixelTolerance, overallTolerance: overallTolerance)     }<\/code><\/pre>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c, \u0447\u0442\u043e \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0432 \u043c\u0435\u0442\u043e\u0434\u0430\u0445 \u0432\u044b\u0448\u0435.<\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 <strong>Tolerance.<\/strong><\/p>\n<p><strong>Tolerance<\/strong> \u2014 \u044d\u0442\u043e \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u0438\u0435 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430 \u043e\u0442 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u043e\u0433\u043e. \u0420\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e \u0448\u043a\u0430\u043b\u0435 \u043e\u0442 0 \u0434\u043e 1, \u0433\u0434\u0435  = 0,05 &#8212; \u0440\u0430\u0432\u043d\u043e 5%. \u041f\u043e \u0434\u0435\u0444\u043e\u043b\u0442\u0443 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b: perPixelTolerance \u0438 overallTolerance = 0%.<\/p>\n<ul>\n<li>\n<p><em>overallTolerance<\/em>\u00a0\u2014 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u0438\u0435 \u043e\u0431\u0449\u0435\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439;<\/p>\n<\/li>\n<li>\n<p><em>perPixelTolerance<\/em>\u00a0\u2014 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u0438\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u0438\u043a\u0441\u0435\u043b\u044f.<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043a\u0430\u0436\u0443 \u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a08\/d65\/196\/a08d65196be2d0d97d97b1c34a8f7f12.png\" width=\"552\" height=\"205\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a08\/d65\/196\/a08d65196be2d0d97d97b1c34a8f7f12.png\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c98\/7d4\/654\/c987d4654d742f819cba4a6a71c8cb2b.png\" width=\"552\" height=\"201\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c98\/7d4\/654\/c987d4654d742f819cba4a6a71c8cb2b.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0417\u0430\u0447\u0430\u0441\u0442\u0443\u044e \u0441\u043d\u044d\u043f\u0448\u043e\u0442 \u0442\u0435\u0441\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0434\u043b\u044f \u043f\u0440\u043e\u0433\u043e\u043d\u0430 \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u0434\u0435\u0432\u0430\u0439\u0441\u0430\u0445 \u0438 \u0432\u0435\u0440\u0441\u0438\u044f\u0445 \u041e\u0421. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0445 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u043e\u0432 \u043e\u0442\u043b\u0438\u0447\u0430\u043b\u043e\u0441\u044c. \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432 setUp \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u0443\u0441\u043e\u043a \u043a\u043e\u0434\u0430 \u043d\u0438\u0436\u0435:<\/p>\n<pre><code class=\"swift\">override func setUp() {         super.setUp()                  fileNameOptions = [             .OS,             .device         ]                  app.launch()     }<\/code><\/pre>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u044f \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430 \u0438\u0437 4 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432:<\/p>\n<ol>\n<li>\n<p>FBSnapshotTestCaseFileNameIncludeOption.device \u2014 \u0442\u0438\u043f \u0434\u0435\u0432\u0430\u0439\u0441\u0430 (iPhone, iPad \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435). \u0411\u0435\u0440\u0451\u0442\u0441\u044f \u0438\u0437 UIDevice.currentDevice.model.<\/p>\n<\/li>\n<li>\n<p>FBSnapshotTestCaseFileNameIncludeOption.OS \u2014 \u0432\u0435\u0440\u0441\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0411\u0435\u0440\u0451\u0442\u0441\u044f \u0438\u0437 UIDevice.currentDevice.systemVersion.<\/p>\n<\/li>\n<li>\n<p><em>FBSnapshotTestCaseFileNameIncludeOption.screenScale \u2014 \u043c\u0430\u0441\u0448\u0442\u0430\u0431 \u044d\u043a\u0440\u0430\u043d\u0430.<\/em> \u0411\u0435\u0440\u0451\u0442\u0441\u044f \u0438\u0437 <em>UIScreen.main.scale.<\/em><\/p>\n<\/li>\n<li>\n<p><em>FBSnapshotTestCaseFileNameIncludeOption.screenSize \u2014 \u0440\u0430\u0437\u043c\u0435\u0440 \u044d\u043a\u0440\u0430\u043d\u0430.<\/em> \u0411\u0435\u0440\u0451\u0442\u0441\u044f \u0438\u0437 UIApplication.sharedApplication.keyWindow.bounds.size.<\/p>\n<\/li>\n<\/ol>\n<h3>\u041f\u0438\u0448\u0435\u043c \u0441\u0432\u043e\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 snapshot \u0442\u0435\u0441\u0442<\/h3>\n<p>\u0414\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430 \u0432\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e recordMode.<\/p>\n<ul>\n<li>\n<p>\u0415\u0441\u043b\u0438 recordMode = true, \u043c\u044b \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442, \u043b\u0438\u0431\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0441\u0442\u0430\u0440\u044b\u0439;<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 recordMode = false, \u043c\u044b \u0441\u0432\u0435\u0440\u044f\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0433\u043e\u043d\u0430 \u0441 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u043c.<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u0432 setUp(), \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0430, \u0438 \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u043c \u0435\u043c\u0443 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 = true.<\/p>\n<pre><code class=\"swift\">override func setUp() {         super.setUp()          recordMode = true                  fileNameOptions = [             .OS,             .device         ]                  app.launch()     }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0438\u0448\u0435\u043c \u0441\u0430\u043c \u0442\u0435\u0441\u0442:<\/p>\n<pre><code class=\"swift\">func testCheckLeaderBoardScreen() {         app.buttons[\"LeaderboardButton\"].waitForExistence(timeout: 5)         app.buttons[\"LeaderboardButton\"].tap()         Thread.sleep(forTimeInterval: 2)         verifyView(identifier: \"imageLeaderboardScreen\")     }<\/code><\/pre>\n<p>\u0422\u0435\u0441\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u0441\u044f \u0441 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u043c failed. \u0412 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u043e\u0448\u0438\u0431\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f, \u0447\u0442\u043e \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f0c\/608\/ffb\/f0c608ffb80a91b5ae90e7b0bd96669e.png\" width=\"1690\" height=\"250\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f0c\/608\/ffb\/f0c608ffb80a91b5ae90e7b0bd96669e.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 recordMode \u043d\u0430 false, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0447\u0442\u043e \u043d\u0430\u0448 \u0442\u0435\u0441\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442.<\/p>\n<pre><code class=\"swift\">override func setUp() {         super.setUp()          recordMode = false                  fileNameOptions = [             .OS,             .device         ]                  app.launch()     }<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/852\/137\/c48\/852137c48ae1af0d89dda7c57c62b264.png\" width=\"1694\" height=\"210\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/852\/137\/c48\/852137c48ae1af0d89dda7c57c62b264.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0422\u0435\u0441\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u043b\u0441\u044f \u0441 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u043c passed. \u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u044e! \u0412\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0441\u0432\u043e\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 Snapshot \u0442\u0435\u0441\u0442.<\/p>\n<p>\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u043e recordMode \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e false, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430 \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0435\u0433\u043e \u0438\u0437 \u043c\u0435\u0442\u043e\u0434\u0430 setUp.<\/p>\n<p>\u041d\u043e \u0435\u0441\u0442\u044c \u043e\u0434\u043d\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0437\u0440\u0443\u0448\u0438\u0442\u044c \u043d\u0430\u0448\u0438 \u043f\u043b\u0430\u043d\u044b \u043f\u043e \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441\u043d\u044d\u043f\u0448\u043e\u0442\u0430\u043c\u0438 \u2014 Status bar. \u0412 \u043d\u0435\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044f \u0437\u0430\u0440\u044f\u0434 \u0431\u0430\u0442\u0430\u0440\u0435\u0438 \u0438 \u0432\u0440\u0435\u043c\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u044b! \u0412 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u043d\u0438\u0436\u0435 \u043c\u044b \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u044b, \u043a\u0430\u043a \u044d\u0442\u043e \u043e\u0434\u043e\u043b\u0435\u0442\u044c.<\/p>\n<h3>\u0423\u0431\u0438\u0440\u0430\u0435\u043c Status bar<\/h3>\n<p>\u042d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0448\u0438\u0442\u044c, \u043e\u0442\u0440\u0435\u0437\u0430\u0432 \u043d\u0430 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0435 Status bar.<\/p>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 iPhone \u0441\u0435\u0440\u0438\u0438 \u00abX\u00bb \u0443 \u043d\u0430\u0441 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 Status bar:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p>\u041c\u043e\u0434\u0435\u043b\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430<\/p>\n<\/td>\n<td>\n<p>\u0412\u044b\u0441\u043e\u0442\u0430 Status Bar<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>iPhone XS Max, iPhone 11 Pro Max, iPhone X, iPhone XS, iPhone 11 Pro, iPhone 12 Pro Max, iPhone 12 Pro<\/p>\n<\/td>\n<td>\n<p>132 px<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>iPhone XR, iPhone 11, iPhone 12<\/p>\n<\/td>\n<td>\n<p>88 px<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>iPhone 6 Plus, iPhone 6S Plus, iPhone 7 Plus, iPhone 8 Plus<\/p>\n<\/td>\n<td>\n<p>54 px<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>iPhone 6, iPhone 6S, iPhone 7, iPhone 8, iPhone 5, iPhone 5S, iPhone 5C, iPhone SE, iPhone 4, iPhone 4S<\/p>\n<\/td>\n<td>\n<p>40 px<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0434\u0432\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f:<\/p>\n<p><strong>UIDeviceExtension<\/strong> \u2014 \u0431\u0443\u0434\u0435\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0434\u0435\u0432\u0430\u0439\u0441.<\/p>\n<pre><code class=\"swift\">import UIKit  extension UIDevice {          static let modelName: String = {         var systemInfo = utsname()         uname(&amp;systemInfo)         let machineMirror = Mirror(reflecting: systemInfo.machine)         let identifier = machineMirror.children.reduce(\"\") { identifier, element in             guard let value = element.value as? Int8, value != 0 else { return identifier }             return identifier + String(UnicodeScalar(UInt8(value)))         }          func mapToDevice(identifier: String) -> String {             switch identifier {             case \"iPhone3,1\", \"iPhone3,2\", \"iPhone3,3\":     return \"iPhone 4\"             case \"iPhone4,1\":                               return \"iPhone 4s\"             case \"iPhone5,1\", \"iPhone5,2\":                  return \"iPhone 5\"             case \"iPhone5,3\", \"iPhone5,4\":                  return \"iPhone 5c\"             case \"iPhone6,1\", \"iPhone6,2\":                  return \"iPhone 5s\"             case \"iPhone7,2\":                               return \"iPhone 6\"             case \"iPhone7,1\":                               return \"iPhone 6 Plus\"             case \"iPhone8,1\":                               return \"iPhone 6s\"             case \"iPhone8,2\":                               return \"iPhone 6s Plus\"             case \"iPhone8,4\":                               return \"iPhone SE\"             case \"iPhone9,1\", \"iPhone9,3\":                  return \"iPhone 7\"             case \"iPhone9,2\", \"iPhone9,4\":                  return \"iPhone 7 Plus\"             case \"iPhone10,1\", \"iPhone10,4\":                return \"iPhone 8\"             case \"iPhone10,2\", \"iPhone10,5\":                return \"iPhone 8 Plus\"             case \"iPhone10,3\", \"iPhone10,6\":                return \"iPhone X\"             case \"iPhone11,2\":                              return \"iPhone XS\"             case \"iPhone11,4\", \"iPhone11,6\":                return \"iPhone XS Max\"             case \"iPhone11,8\":                              return \"iPhone XR\"             case \"iPhone12,1\":                              return \"iPhone 11\"             case \"iPhone12,3\":                              return \"iPhone 11 Pro\"             case \"iPhone12,5\":                              return \"iPhone 11 Pro Max\"             case \"iPhone12,8\":                              return \"iPhone SE (2nd generation)\"             case \"iPhone13,1\":                              return \"iPhone 12 mini\"             case \"iPhone13,2\":                              return \"iPhone 12\"             case \"iPhone13,3\":                              return \"iPhone 12 Pro\"             case \"iPhone13,4\":                              return \"iPhone 12 Pro Max\"             case \"i386\", \"x86_64\":                          return \"Simulator \\(mapToDevice(identifier: ProcessInfo().environment[\"SIMULATOR_MODEL_IDENTIFIER\"] ?? \"iOS\"))\"             default:                                        return identifier             }         }          return mapToDevice(identifier: identifier)     }()      }<\/code><\/pre>\n<p><strong>UIImageExtension<\/strong> \u2014 \u0431\u0443\u0434\u0435\u043c \u043e\u0431\u0440\u0435\u0437\u0430\u0442\u044c status bar \u0434\u043b\u044f \u0434\u0435\u0432\u0430\u0439\u0441\u0430.<\/p>\n<pre><code class=\"swift\">import UIKit  extension UIImage {      var removingStatusBar: UIImage? {         guard let cgImage = cgImage else {             return nil         }                  var yOffset: CGFloat = 0                  switch UIDevice.modelName {         case \"Simulator iPhone 11 Pro\",              \"Simulator iPhone XS Max\",              \"Simulator iPhone X\",              \"Simulator iPhone 12 Pro\",              \"Simulator iPhone 12 Pro Max\",              \"Simulator iPhone XS\":             yOffset = 132         case \"Simulator iPhone XR\",              \"Simulator iPhone 12\",              \"Simulator iPhone 11\":             yOffset = 88         case \"Simulator iPhone 6 Plus\",              \"Simulator iPhone 6S Plus\",              \"Simulator iPhone 7 Plus\",              \"Simulator iPhone 8 Plus\":             yOffset = 54         default:             yOffset = 40         }          let rect = CGRect(             x: 0,             y: Int(yOffset),             width: cgImage.width,             height: cgImage.height - Int(yOffset)         )          if let croppedCGImage = cgImage.cropping(to: rect) {             return UIImage(cgImage: croppedCGImage, scale: scale, orientation: imageOrientation)         }          return nil     }      }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 verifyView() \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e removingStatusBar.<\/p>\n<pre><code class=\"swift\">func verifyView(identifier: String, perPixelTolerance: CGFloat = 0.0, overallTolerance: CGFloat = 0.05 ) {                  guard let screenshotWithoutStatusBar = app.screenshot().image.removingStatusBar else {             return XCTFail(\"An error occurred while cropping the screenshot\", file: #file, line: #line)         }                  FBSnapshotVerifyView(             UIImageView(image: screenshotWithoutStatusBar),             identifier: identifier,             perPixelTolerance: perPixelTolerance,             overallTolerance: overallTolerance         )     }<\/code><\/pre>\n<h3>\u0421\u043c\u043e\u0442\u0440\u0438\u043c \u043e\u0442\u0447\u0435\u0442<\/h3>\n<p>\u041c\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c, \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u043d\u0435\u043f\u0448\u043e\u0442 \u0442\u0435\u0441\u0442. \u0415\u0441\u043b\u0438 \u0442\u0435\u0441\u0442 \u043d\u0435 \u043f\u0440\u043e\u0448\u0435\u043b, \u0442\u043e \u0432 \u043e\u0442\u0447\u0435\u0442\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2a6\/6b5\/f98\/2a66b5f98a8f65b5645573e0807577a6.png\" width=\"2526\" height=\"1238\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2a6\/6b5\/f98\/2a66b5f98a8f65b5645573e0807577a6.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c 3 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430:<\/p>\n<ul>\n<li>\n<p>Reference Image \u2014 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0432 \u043d\u0430\u0434\u0435\u0436\u0434\u0435, \u0447\u0442\u043e \u0442\u0430\u043a \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u044d\u043a\u0440\u0430\u043d \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0433\u0434\u0430 (\u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442);<\/p>\n<\/li>\n<li>\n<p>Failed Image \u2014 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u044d\u043a\u0440\u0430\u043d\u0430 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0433\u043e\u043d\u0430 \u0442\u0435\u0441\u0442\u0430 (\u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442);<\/p>\n<\/li>\n<li>\n<p>Diffed Image \u2014 \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430 \u043d\u0430 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439. \u041d\u0430 \u043d\u0435\u043c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435.<\/p>\n<\/li>\n<\/ul>\n<h2>\u0421\u0430\u043c\u043e\u0435 \u0432\u0430\u0436\u043d\u043e\u0435<\/h2>\n<ul>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 Snapshot \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432\u0435\u0440\u0441\u0442\u043a\u0438, \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0442\u044f\u0436\u0435\u043b\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u043f\u043e accessibilityidentifier;<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 iOSSnapshotTestCase, \u0435\u0441\u043b\u0438 \u043f\u043e\u0439\u043c\u0435\u0442\u0435 \u0447\u0442\u043e \u0435\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u0432\u0430\u043c \u043c\u0430\u043b\u043e, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 SnapshotTesting;<\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f \u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u043d\u044b\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c overallTolerance = 0,05; \u0435\u0441\u043b\u0438 \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u0442\u0435 \u0431\u043e\u043b\u044c\u0448\u0435, Snapshot \u0442\u0435\u0441\u0442\u044b \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u043d\u044b\u043c\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>Snapshot \u0442\u0435\u0441\u0442\u044b \u2014 \u044d\u0442\u043e \u043a\u043b\u0430\u0441\u0441\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430, \u043d\u043e \u0432\u0430\u0436\u043d\u043e \u0435\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441 \u0443\u043c\u043e\u043c. \u041d\u0435 \u0432\u0441\u0435 \u043a\u0435\u0439\u0441\u044b \u0441\u0442\u043e\u0438\u0442 \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0441\u043d\u044d\u043f\u0448\u043e\u0442\u0430\u043c\u0438.<\/p>\n<p>\u0422\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u0441\u043d\u044d\u043f\u0448\u043e\u0442 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 \u043c\u043e\u0451\u043c <a href=\"https:\/\/github.com\/BorisLys\/Snapshoot-UI-Testing-Example\/tree\/main\/Bullseye\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438<\/a>.<\/p>\n<p><strong>\u0412\u0434\u043e\u0445\u043d\u043e\u0432\u043b\u044f\u043b\u0441\u044f \u0441\u0442\u0430\u0442\u044c\u044f\u043c\u0438:<\/strong><\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/medium.com\/dev-jam\/snapshot-testing-in-swift-9d52cbec075c\">Snapshot testing in Swift<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/joesusnick.medium.com\/snapshot-xcui-testing-6a16c7ccd47b\">Snapshot XCUITestin<\/a><\/p>\n<\/li>\n<\/ul>\n<hr\/>\n<p>\u0418\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0448\u044c\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u043d\u0430 iOS? \u041f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0439\u0441\u044f \u043d\u0430 \u043c\u043e\u0439 <a href=\"https:\/\/t.me\/ios_automation_testing\">\u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c-\u043a\u0430\u043d\u0430\u043b<\/a>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u044e \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u044b \u043a\u0430\u043a \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u043c, \u0442\u0430\u043a \u0438 \u043e\u043f\u044b\u0442\u043d\u044b\u043c iOS-\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430\u043c.<\/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\/569032\/\"> https:\/\/habr.com\/ru\/articles\/569032\/<\/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<p>\u0425\u0430\u0431\u0440, \u043f\u0440\u0438\u0432\u0435\u0442!<\/p>\n<p>\u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0411\u043e\u0440\u0438\u0441. \u042f AQA iOS-engineer \u0432 Vivid Money.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0445\u043e\u0447\u0443 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u0440\u043e snapshot \u0442\u0435\u0441\u0442\u044b \u0438 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0432 \u0441\u0432\u043e\u0451\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<p>\u0414\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u043c iOS-\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430\u043c, \u043b\u0438\u0431\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0435\u0448\u0438\u043b\u0438 \u0438\u0437\u0443\u0447\u0438\u0442\u044c XCUITest \u0438 \u043f\u043e\u043a\u0440\u044b\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 ui-\u0442\u0435\u0441\u0442\u0430\u043c\u0438.<\/p>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c:<\/p>\n<ol>\n<li>\n<p>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 snapshot \u0442\u0435\u0441\u0442\u044b \u0438 \u043a\u043e\u0433\u0434\u0430 \u0438\u0445 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a\u0438\u0435 \u0435\u0441\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u044d\u0442\u0430\u043f\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0438 \u043f\u043e\u043a\u0430\u0436\u0443 \u043a\u0430\u043a \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c \u0438\u0445 \u0443 \u0441\u0435\u0431\u044f \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<\/li>\n<\/ol>\n<h2>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Snapshot \u0442\u0435\u0441\u0442\u044b?<\/h2>\n<p>Snapshot \u0442\u0435\u0441\u0442\u044b \u2014 \u044d\u0442\u043e \u0442\u0435\u0441\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u043b\u0430\u044e\u0442 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u044d\u043a\u0440\u0430\u043d\u0430 (\u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442) \u0438 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u044e\u0442 \u0441 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0433\u043e\u043d\u0430 \u0442\u0435\u0441\u0442\u043e\u0432. \u0414\u0435\u043b\u0430\u0442\u044c Snapshot \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u0432\u0441\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u2014 \u043f\u043b\u043e\u0445\u0430\u044f \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0430, \u0442\u0430\u043a \u043a\u0430\u043a \u0435\u0441\u043b\u0438 \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u0434\u0438\u0437\u0430\u0439\u043d, \u0432\u0430\u0448\u0438 \u0442\u0435\u0441\u0442\u044b \u043c\u043e\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e \u0441\u0442\u0430\u043d\u0443\u0442 \u043a\u0440\u0430\u0441\u043d\u044b\u043c\u0438, \u0438 \u0432\u0430\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0432 \u0441\u0440\u043e\u0447\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043c\u0435\u043d\u044f\u0442\u044c \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0435 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u044b. \u042f \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Snapshot \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432\u0435\u0440\u0441\u0442\u043a\u0438, \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0442\u044f\u0436\u0435\u043b\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u043f\u043e accessibilityidentifier (\u0433\u0440\u0430\u0444\u0438\u043a \u0446\u0435\u043d, \u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044c \u0438 \u043f\u0440\u043e\u0447\u0438\u0435).<\/p>\n<h2>\u041a\u0430\u043a\u0438\u0435 \u0435\u0441\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438?<\/h2>\n<p>\u0412 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c, \u043f\u0440\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0438 Snapshot \u0442\u0435\u0441\u0442\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0434\u0432\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438:<\/p>\n<ol>\n<li>\n<p><a href=\"https:\/\/github.com\/uber\/ios-snapshot-test-case\">iOSSnapshotTestCase (previously FBSnapshotTestCase)<\/a>.<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/pointfreeco\/swift-snapshot-testing\">SnapshotTesting<\/a>.<\/p>\n<\/li>\n<\/ol>\n<p>\u0418\u0445 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0440\u0430\u0437\u043b\u0438\u0447\u0438\u044f:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p><strong>\u0424\u0438\u0447\u0438<\/strong><\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p><strong>iOSSnapshotTestCase<\/strong><\/p>\n<\/td>\n<td>\n<p><strong>SnapshotTesting<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u042f\u0437\u044b\u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>Objective-C<\/p>\n<\/td>\n<td>\n<p>Swift<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Diff \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u044b<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>\u0415\u0441\u0442\u044c<\/p>\n<\/td>\n<td>\n<p>\u0415\u0441\u0442\u044c<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u0421\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u044b \u043b\u044e\u0431\u043e\u0433\u043e UI-\u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>\u041d\u0443\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u043c\u0443<\/p>\n<\/td>\n<td>\n<p>\u0415\u0441\u0442\u044c<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u0413\u0438\u0431\u043a\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u043e\u0433\u0440\u0435\u0448\u043d\u043e\u0441\u0442\u0438 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u044f \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u043e\u0432<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>2 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430<\/p>\n<\/td>\n<td>\n<p>1 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0434\u0430\u0442\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>12 \u043e\u043a\u0442\u044f\u0431\u0440\u044f 2019<\/p>\n<\/td>\n<td>\n<p>27 \u0430\u043f\u0440\u0435\u043b\u044f 2021<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u044b \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439<\/p>\n<\/td>\n<td data-colwidth=\"223\" width=\"223\">\n<p>CocoaPods, Carthage<\/p>\n<\/td>\n<td>\n<p>CocoaPods, Carthage, Swift Package Manager<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<h2>\u0412\u043d\u0435\u0434\u0440\u044f\u0435\u043c \u0441\u043d\u044d\u043f\u0448\u043e\u0442 \u0442\u0435\u0441\u0442\u044b \u0432 \u043f\u0440\u043e\u0435\u043a\u0442<\/h2>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u043e\u0432 \u043d\u0438\u0436\u0435, \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<ol>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u0440\u0433\u0435\u0442 ui-\u0442\u0435\u0441\u0442\u043e\u0432 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442. \u041a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 <a href=\"https:\/\/habr.com\/ru\/company\/vivid_money\/blog\/533180\/\">\u0441\u0442\u0430\u0442\u044c\u0435<\/a> \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 &#171;\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0441\u0440\u0435\u0434\u0443&#187;.<\/p>\n<\/li>\n<li>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 iOSSnapshotTestCase \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435. \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u0435\u0441\u0442\u044c \u0432 <a href=\"https:\/\/github.com\/uber\/ios-snapshot-test-case#installation\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438<\/a>.<\/p>\n<\/li>\n<\/ol>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u0432\u0441\u0435 \u043f\u043e\u044d\u0442\u0430\u043f\u043d\u043e:<\/p>\n<h3>\u041f\u0438\u0448\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e<\/h3>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043a\u0430\u0447\u0430\u043d\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0438 \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 FBSnapshotTestCase, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043d\u0443\u0436\u043d\u044b\u043c \u043c\u0435\u0442\u043e\u0434\u0430\u043c.<\/p>\n<pre><code class=\"swift\">import FBSnapshotTestCase  class BullseyeUITests: FBSnapshotTestCase {          let app = XCUIApplication()      override func setUp() {         super.setUp()                  app.launch()     }  }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430, \u0447\u0442\u043e\u0431\u044b \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u043b\u044e\u0431\u043e\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f ui-\u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<pre><code class=\"swift\">\/\/ \u0414\u0435\u043b\u0430\u0435\u043c \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u0432\u0441\u0435\u0433\u043e \u044d\u043a\u0440\u0430\u043d\u0430 func verifyView(identifier: String, perPixelTolerance: CGFloat = 0.0, overallTolerance: CGFloat = 0.05 ) {                  let screenshot = app.screenshot().image                  FBSnapshotVerifyView(UIImageView(image: screenshot), identifier: identifier, perPixelTolerance: perPixelTolerance, overallTolerance: overallTolerance)     }  \/\/ \u0414\u0435\u043b\u0430\u0435\u043c \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430     func verifyElement(element: XCUIElement, identifier: String, perPixelTolerance: CGFloat = 0.0, overallTolerance: CGFloat = 0.05 ) {                  FBSnapshotVerifyView(UIImageView(image: element.screenshot().image), identifier: identifier, perPixelTolerance: perPixelTolerance, overallTolerance: overallTolerance)     }<\/code><\/pre>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c, \u0447\u0442\u043e \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0432 \u043c\u0435\u0442\u043e\u0434\u0430\u0445 \u0432\u044b\u0448\u0435.<\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 <strong>Tolerance.<\/strong><\/p>\n<p><strong>Tolerance<\/strong> \u2014 \u044d\u0442\u043e \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u0438\u0435 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430 \u043e\u0442 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u043e\u0433\u043e. \u0420\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e \u0448\u043a\u0430\u043b\u0435 \u043e\u0442 0 \u0434\u043e 1, \u0433\u0434\u0435  = 0,05 &#8212; \u0440\u0430\u0432\u043d\u043e 5%. \u041f\u043e \u0434\u0435\u0444\u043e\u043b\u0442\u0443 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b: perPixelTolerance \u0438 overallTolerance = 0%.<\/p>\n<ul>\n<li>\n<p><em>overallTolerance<\/em>\u00a0\u2014 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u0438\u0435 \u043e\u0431\u0449\u0435\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439;<\/p>\n<\/li>\n<li>\n<p><em>perPixelTolerance<\/em>\u00a0\u2014 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u0438\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u0438\u043a\u0441\u0435\u043b\u044f.<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043a\u0430\u0436\u0443 \u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0417\u0430\u0447\u0430\u0441\u0442\u0443\u044e \u0441\u043d\u044d\u043f\u0448\u043e\u0442 \u0442\u0435\u0441\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0434\u043b\u044f \u043f\u0440\u043e\u0433\u043e\u043d\u0430 \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u0434\u0435\u0432\u0430\u0439\u0441\u0430\u0445 \u0438 \u0432\u0435\u0440\u0441\u0438\u044f\u0445 \u041e\u0421. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0445 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u043e\u0432 \u043e\u0442\u043b\u0438\u0447\u0430\u043b\u043e\u0441\u044c. \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432 setUp \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u0443\u0441\u043e\u043a \u043a\u043e\u0434\u0430 \u043d\u0438\u0436\u0435:<\/p>\n<pre><code class=\"swift\">override func setUp() {         super.setUp()                  fileNameOptions = [             .OS,             .device         ]                  app.launch()     }<\/code><\/pre>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u044f \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430 \u0438\u0437 4 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432:<\/p>\n<ol>\n<li>\n<p>FBSnapshotTestCaseFileNameIncludeOption.device \u2014 \u0442\u0438\u043f \u0434\u0435\u0432\u0430\u0439\u0441\u0430 (iPhone, iPad \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435). \u0411\u0435\u0440\u0451\u0442\u0441\u044f \u0438\u0437 UIDevice.currentDevice.model.<\/p>\n<\/li>\n<li>\n<p>FBSnapshotTestCaseFileNameIncludeOption.OS \u2014 \u0432\u0435\u0440\u0441\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0411\u0435\u0440\u0451\u0442\u0441\u044f \u0438\u0437 UIDevice.currentDevice.systemVersion.<\/p>\n<\/li>\n<li>\n<p><em>FBSnapshotTestCaseFileNameIncludeOption.screenScale \u2014 \u043c\u0430\u0441\u0448\u0442\u0430\u0431 \u044d\u043a\u0440\u0430\u043d\u0430.<\/em> \u0411\u0435\u0440\u0451\u0442\u0441\u044f \u0438\u0437 <em>UIScreen.main.scale.<\/em><\/p>\n<\/li>\n<li>\n<p><em>FBSnapshotTestCaseFileNameIncludeOption.screenSize \u2014 \u0440\u0430\u0437\u043c\u0435\u0440 \u044d\u043a\u0440\u0430\u043d\u0430.<\/em> \u0411\u0435\u0440\u0451\u0442\u0441\u044f \u0438\u0437 UIApplication.sharedApplication.keyWindow.bounds.size.<\/p>\n<\/li>\n<\/ol>\n<h3>\u041f\u0438\u0448\u0435\u043c \u0441\u0432\u043e\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 snapshot \u0442\u0435\u0441\u0442<\/h3>\n<p>\u0414\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430 \u0432\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e recordMode.<\/p>\n<ul>\n<li>\n<p>\u0415\u0441\u043b\u0438 recordMode = true, \u043c\u044b \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442, \u043b\u0438\u0431\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0441\u0442\u0430\u0440\u044b\u0439;<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 recordMode = false, \u043c\u044b \u0441\u0432\u0435\u0440\u044f\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0433\u043e\u043d\u0430 \u0441 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u043c.<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u0432 setUp(), \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0430, \u0438 \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u043c \u0435\u043c\u0443 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 = true.<\/p>\n<pre><code class=\"swift\">override func setUp() {         super.setUp()          recordMode = true                  fileNameOptions = [             .OS,             .device         ]                  app.launch()     }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0438\u0448\u0435\u043c \u0441\u0430\u043c \u0442\u0435\u0441\u0442:<\/p>\n<pre><code class=\"swift\">func testCheckLeaderBoardScreen() {         app.buttons[\"LeaderboardButton\"].waitForExistence(timeout: 5)         app.buttons[\"LeaderboardButton\"].tap()         Thread.sleep(forTimeInterval: 2)         verifyView(identifier: \"imageLeaderboardScreen\")     }<\/code><\/pre>\n<p>\u0422\u0435\u0441\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u0441\u044f \u0441 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u043c failed. \u0412 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u043e\u0448\u0438\u0431\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f, \u0447\u0442\u043e \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442 \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 recordMode \u043d\u0430 false, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0447\u0442\u043e \u043d\u0430\u0448 \u0442\u0435\u0441\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442.<\/p>\n<pre><code class=\"swift\">override func setUp() {         super.setUp()          recordMode = false                  fileNameOptions = [             .OS,             .device         ]                  app.launch()     }<\/code><\/pre>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0422\u0435\u0441\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u043b\u0441\u044f \u0441 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u043c passed. \u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u044e! \u0412\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0441\u0432\u043e\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 Snapshot \u0442\u0435\u0441\u0442.<\/p>\n<p>\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u043e recordMode \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e false, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u044d\u0442\u0430\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430 \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0435\u0433\u043e \u0438\u0437 \u043c\u0435\u0442\u043e\u0434\u0430 setUp.<\/p>\n<p>\u041d\u043e \u0435\u0441\u0442\u044c \u043e\u0434\u043d\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0437\u0440\u0443\u0448\u0438\u0442\u044c \u043d\u0430\u0448\u0438 \u043f\u043b\u0430\u043d\u044b \u043f\u043e \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441\u043d\u044d\u043f\u0448\u043e\u0442\u0430\u043c\u0438 \u2014 Status bar. \u0412 \u043d\u0435\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044f \u0437\u0430\u0440\u044f\u0434 \u0431\u0430\u0442\u0430\u0440\u0435\u0438 \u0438 \u0432\u0440\u0435\u043c\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u044b! \u0412 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u043d\u0438\u0436\u0435 \u043c\u044b \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u044b, \u043a\u0430\u043a \u044d\u0442\u043e \u043e\u0434\u043e\u043b\u0435\u0442\u044c.<\/p>\n<h3>\u0423\u0431\u0438\u0440\u0430\u0435\u043c Status bar<\/h3>\n<p>\u042d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0448\u0438\u0442\u044c, \u043e\u0442\u0440\u0435\u0437\u0430\u0432 \u043d\u0430 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0435 Status bar.<\/p>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 iPhone \u0441\u0435\u0440\u0438\u0438 \u00abX\u00bb \u0443 \u043d\u0430\u0441 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 Status bar:<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p>\u041c\u043e\u0434\u0435\u043b\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430<\/p>\n<\/td>\n<td>\n<p>\u0412\u044b\u0441\u043e\u0442\u0430 Status Bar<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>iPhone XS Max, iPhone 11 Pro Max, iPhone X, iPhone XS, iPhone 11 Pro, iPhone 12 Pro Max, iPhone 12 Pro<\/p>\n<\/td>\n<td>\n<p>132 px<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>iPhone XR, iPhone 11, iPhone 12<\/p>\n<\/td>\n<td>\n<p>88 px<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>iPhone 6 Plus, iPhone 6S Plus, iPhone 7 Plus, iPhone 8 Plus<\/p>\n<\/td>\n<td>\n<p>54 px<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>iPhone 6, iPhone 6S, iPhone 7, iPhone 8, iPhone 5, iPhone 5S, iPhone 5C, iPhone SE, iPhone 4, iPhone 4S<\/p>\n<\/td>\n<td>\n<p>40 px<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0434\u0432\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f:<\/p>\n<p><strong>UIDeviceExtension<\/strong> \u2014 \u0431\u0443\u0434\u0435\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0434\u0435\u0432\u0430\u0439\u0441.<\/p>\n<pre><code class=\"swift\">import UIKit  extension UIDevice {          static let modelName: String = {         var systemInfo = utsname()         uname(&amp;systemInfo)         let machineMirror = Mirror(reflecting: systemInfo.machine)         let identifier = machineMirror.children.reduce(\"\") { identifier, element in             guard let value = element.value as? Int8, value != 0 else { return identifier }             return identifier + String(UnicodeScalar(UInt8(value)))         }          func mapToDevice(identifier: String) -> String {             switch identifier {             case \"iPhone3,1\", \"iPhone3,2\", \"iPhone3,3\":     return \"iPhone 4\"             case \"iPhone4,1\":                               return \"iPhone 4s\"             case \"iPhone5,1\", \"iPhone5,2\":                  return \"iPhone 5\"             case \"iPhone5,3\", \"iPhone5,4\":                  return \"iPhone 5c\"             case \"iPhone6,1\", \"iPhone6,2\":                  return \"iPhone 5s\"             case \"iPhone7,2\":                               return \"iPhone 6\"             case \"iPhone7,1\":                               return \"iPhone 6 Plus\"             case \"iPhone8,1\":                               return \"iPhone 6s\"             case \"iPhone8,2\":                               return \"iPhone 6s Plus\"             case \"iPhone8,4\":                               return \"iPhone SE\"             case \"iPhone9,1\", \"iPhone9,3\":                  return \"iPhone 7\"             case \"iPhone9,2\", \"iPhone9,4\":                  return \"iPhone 7 Plus\"             case \"iPhone10,1\", \"iPhone10,4\":                return \"iPhone 8\"             case \"iPhone10,2\", \"iPhone10,5\":                return \"iPhone 8 Plus\"             case \"iPhone10,3\", \"iPhone10,6\":                return \"iPhone X\"             case \"iPhone11,2\":                              return \"iPhone XS\"             case \"iPhone11,4\", \"iPhone11,6\":                return \"iPhone XS Max\"             case \"iPhone11,8\":                              return \"iPhone XR\"             case \"iPhone12,1\":                              return \"iPhone 11\"             case \"iPhone12,3\":                              return \"iPhone 11 Pro\"             case \"iPhone12,5\":                              return \"iPhone 11 Pro Max\"             case \"iPhone12,8\":                              return \"iPhone SE (2nd generation)\"             case \"iPhone13,1\":                              return \"iPhone 12 mini\"             case \"iPhone13,2\":                              return \"iPhone 12\"             case \"iPhone13,3\":                              return \"iPhone 12 Pro\"             case \"iPhone13,4\":                              return \"iPhone 12 Pro Max\"             case \"i386\", \"x86_64\":                          return \"Simulator \\(mapToDevice(identifier: ProcessInfo().environment[\"SIMULATOR_MODEL_IDENTIFIER\"] ?? \"iOS\"))\"             default:                                        return identifier             }         }          return mapToDevice(identifier: identifier)     }()      }<\/code><\/pre>\n<p><strong>UIImageExtension<\/strong> \u2014 \u0431\u0443\u0434\u0435\u043c \u043e\u0431\u0440\u0435\u0437\u0430\u0442\u044c status bar \u0434\u043b\u044f \u0434\u0435\u0432\u0430\u0439\u0441\u0430.<\/p>\n<pre><code class=\"swift\">import UIKit  extension UIImage {      var removingStatusBar: UIImage? {         guard let cgImage = cgImage else {          <\/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-402497","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/402497","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=402497"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/402497\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=402497"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=402497"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=402497"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}