{"id":303944,"date":"2020-05-20T15:01:45","date_gmt":"2020-05-20T15:01:45","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=303944"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=303944","title":{"rendered":"Unit \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f RxSwift \u043a\u043e\u0434\u0430"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/502916\/\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e \u0432\u0430\u0448\u0435\u043c\u0443 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u044e \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/www.raywenderlich.com\/7408-testing-your-rxswift-code\" rel=\"nofollow\">&quot;Testing Your RxSwift Code&quot;<\/a> \u0430\u0432\u0442\u043e\u0440\u0430 Shai Mishali \u0441 \u0441\u0430\u0439\u0442\u0430 <a href=\"http:\/\/raywenderlich.com\" rel=\"nofollow\">raywenderlich.com<\/a>.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e RxSwift \u043a\u043e\u043d\u0446\u0435\u043f\u0442\u0443\u0430\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 &quot;\u043e\u0431\u044b\u0447\u043d\u044b\u043c \u043f\u0443\u0442\u0435\u043c&quot;. \u041e\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043c \u0441\u043c\u044b\u0441\u043b\u0435, \u0447\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0432 \u0432\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043e\u0431\u044b\u0447\u043d\u043e \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u0447\u0435\u0442\u043a\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0432\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b <em>\u043f\u043e\u0442\u043e\u043a\u043e\u043c<\/em> \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043d\u0430 \u043e\u0441\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u043c \u0432 RxSwift \u043a\u0430\u043a <code>Observable<\/code>. \u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u0434\u0430\u0441\u0442 \u0432\u0430\u043c \u043a\u043b\u044e\u0447 \u043a \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e RxSwift \u043a\u043e\u0434\u0430.<\/p>\n<p>  <\/p>\n<p><code>Observable<\/code> \u2014 \u043c\u043e\u0449\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u0430\u043c, \u043a\u0430\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443, \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u0431\u044b\u0442\u044c \u0443\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u043c \u0447\u0442\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0433\u0434\u0430 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e. \u0412\u043c\u0435\u0441\u0442\u0435 \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430\u043c\u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044d\u0442\u043e \u0434\u0430\u0435\u0442, \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 <code>Observable<\/code> \u2014 \u043d\u0435 \u0442\u0430\u043a\u0430\u044f \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430, \u043a\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 XCTAssert \u0434\u043b\u044f \u043e\u0431\u044b\u0447\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439. \u041d\u043e \u043d\u0435 \u043f\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0439\u0442\u0435 \u2014 \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u0441\u0442\u0430\u0432\u0438\u0442 \u0432\u0430\u0441 \u043d\u0430 \u043f\u0443\u0442\u044c \u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0442\u043e\u043c \u0432 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 RxSwift!<\/p>\n<p>  <\/p>\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u0443\u0447\u0438\u0442 \u0432\u0430\u0441 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c unit \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f <code>Observable<\/code> \u043f\u043e\u0442\u043e\u043a\u043e\u0432. \u0412\u044b \u043d\u0430\u0443\u0447\u0438\u0442\u0435\u0441\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u0442\u0435\u0445\u043d\u0438\u043a\u0430\u043c \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f RxSwift \u043a\u043e\u0434\u0430, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0441\u043e\u0432\u0435\u0442\u0430\u043c. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0447\u043d\u0435\u043c.<a name=\"habracut\"><\/a><\/p>\n<p>  <\/p>\n<blockquote><p><strong>\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435:<\/strong> \u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u0435\u0442 \u0447\u0442\u043e \u0432\u044b \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u043a\u0430\u043a \u0441 RxSwift, \u0442\u0430\u043a \u0438 \u0441 \u0442\u0435\u043c \u043a\u0430\u043a \u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e XCTest<\/p><\/blockquote>\n<p>  <\/p>\n<h1 id=\"nachinaem\">\u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c<\/h1>\n<p>  <\/p>\n<p>\u0420\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u0441\u0435\u0431\u044f, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u0434\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0438\u0437\u043c\u0435\u043d\u044f\u044e\u0449\u0438\u043c\u0441\u044f \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c, \u0442\u0430\u043a \u0447\u0442\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043a\u0430\u043a \u0440\u0430\u0437 \u0441 \u0442\u0430\u043a\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c.<\/p>\n<p>  <\/p>\n<p>\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 <a href=\"https:\/\/github.com\/Almaz5200\/Raytronome\" rel=\"nofollow\">\u0441\u0442\u0430\u0440\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442<\/a>. \u0412\u044b \u043d\u0430\u0439\u0434\u0435\u0442\u0435 \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0443\u0440\u043e\u043a\u0430: <strong>Raytronome<\/strong> \u2014 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435-\u043c\u0435\u0442\u0440\u043e\u043d\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0432\u044b \u0431\u044b \u043c\u043e\u0433\u043b\u0438 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0437\u0430\u043d\u044f\u0442\u0438\u0439 \u043c\u0443\u0437\u044b\u043a\u043e\u0439. \u041a\u0430\u043a \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u043d\u044f\u0442\u044c, \u0438\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e \u0447\u0442\u043e \u043c\u0435\u0442\u0440\u043e\u043d\u043e\u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c, \u0432\u0430\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u043e\u0438\u0442 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c.<\/p>\n<p>  <\/p>\n<p>\u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 <strong>Raytronome.xcworkspace<\/strong>. \u041f\u043e\u0441\u043b\u0435 \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 <strong>Main.storyboard<\/strong>. \u0412\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435, \u0447\u0442\u043e \u044d\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u0432\u0441\u0435\u0433\u043e \u043e\u0434\u043d\u0438\u043c \u044d\u043a\u0440\u0430\u043d\u043e\u043c. <\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u0431\u0435\u0440\u0438\u0442\u0435 \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 <strong>Play<\/strong> \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043c\u0435\u0442\u0440\u043e\u043d\u043e\u043c. \u0412\u044b \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c <strong>\u0442\u0430\u043a\u0442\u043e\u0432\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440<\/strong> (Signature) \u0438 <strong>\u0442\u0435\u043c\u043f<\/strong> (Tempo).<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/uw\/ur\/aj\/uwurajdvgv4x3ujtqomtvuupfe4.gif\"><\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e UIViewController \u2014 <strong>MetronomeViewController.swift<\/strong>, \u0430 <strong>MetronomeViewModel.swift<\/strong> \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0432\u0441\u044e \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u044b \u0438 \u0431\u0443\u0434\u0435\u0442\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b.<\/p>\n<p>  <\/p>\n<h1 id=\"problema-testirovaniya-observable\">\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f Observable<\/h1>\n<p>  <\/p>\n<p>\u0411\u044b\u0441\u0442\u0440\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u043c \u043e\u0441\u043d\u043e\u0432\u044b RxSwift \u0438 <code>Observable<\/code> \u043f\u043e\u0442\u043e\u043a\u043e\u0432.<\/p>\n<p>  <\/p>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043f\u043e\u0442\u043e\u043a\u0430\u043c\u0438 \u043a\u0430\u0440\u0434\u0438\u043d\u0430\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u0440\u043e\u0441\u0442\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438, \u0437\u0430\u0434\u0430\u0447\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u0441\u0442\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b \u0438 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b; \u0443 \u043d\u0438\u0445 \u043d\u0435\u0442 \u043d\u0438\u043a\u043e\u0433\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. <code>Observable<\/code> \u043f\u043e\u0442\u043e\u043a\u0438, \u0441 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b, \u0432\u044b\u0434\u0430\u044e\u0442 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b (\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f) \u0432\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/w0\/yu\/1f\/w0yu1fxhehm_qpm8dl-jx0_vrco.png\"><\/p>\n<p>  <\/p>\n<p>\u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u043f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432\u0430\u043c \u0447\u0430\u0441\u0442\u043e \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0434\u043d\u0443 \u0438\u0437 \u0434\u0432\u0443\u0445 \u0432\u0435\u0449\u0435\u0439:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0442\u043e\u043a \u0432\u044b\u0434\u0430\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b, \u0432\u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043a\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442<\/li>\n<li>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0442\u043e\u043a \u0432\u044b\u0434\u0430\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f. \u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f &quot;\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c&quot; \u044d\u0442\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u0442\u043e\u043a \u0438\u0445 \u0432\u044b\u0434\u0430\u043b<\/li>\n<\/ul>\n<p>  <\/p>\n<h1 id=\"opredelyaem-chto-testirovat\">\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0447\u0442\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/h1>\n<p>  <\/p>\n<p>\u041e\u0431\u044b\u0447\u043d\u043e \u0445\u043e\u0440\u043e\u0448\u0435\u0439 \u0438\u0434\u0435\u0435\u0439 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0443\u043c\u0430\u0442\u044c \u043e \u0442\u043e\u043c, <em>\u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e<\/em> \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041a\u0430\u043a \u0431\u044b\u043b\u043e \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u0442\u043e \u0440\u0430\u043d\u0435\u0435, \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>MetronomeViewModel<\/code>, ViewModel \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443 \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0443\u044e\u0441\u044f \u043a \u0432\u0430\u0448\u0435\u043c\u0443 \u043c\u0435\u0442\u0440\u043e\u043d\u043e\u043c\u0443. <\/p>\n<p>  <\/p>\n<p>\u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 <strong>MetronomeViewModel.swift<\/strong>. \u0421\u043c\u043e\u0442\u0440\u044f \u043d\u0430 ViewModel \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0432\u044b\u0445\u043e\u0434\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0442 \u0437\u0430 \u0440\u0430\u0437\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u0438 \u043b\u043e\u0433\u0438\u043a\u0438: <strong>\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c,<\/strong> <strong>\u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c<\/strong> (\u0442\u0430\u043a\u0442\u043e\u0432\u043e\u0433\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430), <strong>\u0442\u0430\u043a\u0442\u043e\u0432\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440<\/strong>, <strong>\u0442\u0435\u043c\u043f<\/strong> \u0432 \u0432\u0438\u0434\u0435 \u0441\u0442\u0440\u043e\u043a, \u0447\u0438\u0441\u043b\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044f, \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044f, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0442\u043e\u043a\u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u0435 \u0437\u0430 \u0431\u0438\u0442<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/o4\/hw\/yw\/o4hwywxmgdtuw_zi657spebhry0.png\"><\/p>\n<p>  <\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434\u0443\u043c\u0430\u0435\u043c \u043e \u0442\u043e\u043c \u0447\u0442\u043e \u043d\u0430\u043c \u0437\u0430\u0445\u043e\u0447\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 UI. \u041c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u043e:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0427\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c \u0438 \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c \u0438\u043c\u0435\u044e\u0442 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <strong>4<\/strong><\/li>\n<li>\u0422\u0430\u043a\u0442\u043e\u0432\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e <strong>4\/4<\/strong><\/li>\n<li>\u0422\u0435\u043c\u043f \u0438\u043c\u0435\u0435\u0442 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <strong>120<\/strong><\/li>\n<li>\u041d\u0430\u0436\u0430\u0442\u0438\u0435 \u043a\u043d\u043e\u043f\u043a\u0438 Play\/Pause \u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 isPlaying \u043c\u0435\u0442\u0440\u043e\u043d\u043e\u043c\u0430<\/li>\n<li>\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044f, \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044f, \u043b\u0438\u0431\u043e \u0442\u0435\u043c\u043f\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u043e\u0432.<\/li>\n<li>\u0423\u0434\u0430\u0440\u044b \u043c\u0435\u0442\u0440\u043e\u043d\u043e\u043c\u0430 &quot;\u0431\u044c\u044e\u0442&quot; \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c.<\/li>\n<li>\u0411\u0438\u0442 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f\u043c\u0438 <code>.even<\/code> \u0438 <code>.odd<\/code> \u2014 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u044d\u0442\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0441\u0432\u0435\u0440\u0445\u0443 \u044d\u043a\u0440\u0430\u043d\u0430<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u0432\u0443\u043c\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 RxSwift, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f <strong>RxBlocking<\/strong> \u0438 <strong>RxTest<\/strong>. \u041a\u0430\u0436\u0434\u0430\u044f \u0438\u0437 \u043d\u0438\u0445 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u0440\u0430\u0437\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438 \u043a\u043e\u043d\u0446\u0435\u043f\u0442\u044b \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u0430\u0448\u0438\u0445 \u043f\u043e\u0442\u043e\u043a\u043e\u0432. \u042d\u0442\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0443\u0436\u0435 \u0432\u0445\u043e\u0434\u044f\u0442 \u0432 \u0432\u0430\u0448 \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442<\/p>\n<p>  <\/p>\n<h1 id=\"ispolzovanie-rxblocking\">\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <strong>RxBlocking<\/strong><\/h1>\n<p>  <\/p>\n<p>\u0421\u0442\u0430\u0440\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u043f\u043e\u043b\u0443\u043f\u0443\u0441\u0442\u043e\u0439 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0442\u0430\u0440\u0433\u0435\u0442 \u0441 \u0444\u0430\u0439\u043b\u043e\u043c <strong>RaytronomeTests.swift<\/strong>.<\/p>\n<p>  <\/p>\n<p>\u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0435\u0433\u043e \u0438 \u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435\u0441\u044c; \u043e\u043d \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 <strong>RxSwift<\/strong>, <strong>RxCocoa<\/strong>, <strong>RxTest<\/strong> \u0438 <strong>RxBlocking<\/strong>, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u043f\u043e\u043b\u0435 <code>viewModel<\/code> \u0438 \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>setUp()<\/code> \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 <code>MetronomeViewModel<\/code> \u043f\u0435\u0440\u0435\u0434 \u043a\u0430\u0436\u0434\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u043c<\/p>\n<p>  <\/p>\n<p>\u0412\u0430\u0448\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c, \u0447\u0442\u043e \u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c \u0438 \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442\u0441\u044f \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f <code>4<\/code>. \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0434\u0443\u043c\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e \u043f\u0435\u0440\u0432\u043e\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u044b\u0434\u0430\u0441\u0442 \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043f\u043e\u0442\u043e\u043a\u043e\u0432. \u0417\u0432\u0443\u0447\u0438\u0442 \u043a\u0430\u043a \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0434\u043b\u044f <strong>RxBlocking<\/strong>!<\/p>\n<p>  <\/p>\n<p><strong>RxBlocking<\/strong> \u2014 \u044d\u0442\u043e \u043e\u0434\u043d\u0430 \u0438\u0437 \u0434\u0432\u0443\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u0441 RxSwift, \u0438 \u043e\u043d \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0438\u0434\u0435\u0435: \u043e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u0430\u043c \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0432\u0430\u0448 <code>Observable<\/code> \u043f\u043e\u0442\u043e\u043a \u0432 <code>BlockingObservable<\/code>, \u043e\u0441\u043e\u0431\u044b\u0439 Observable \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043f\u043e\u0442\u043e\u043a, \u043e\u0436\u0438\u0434\u0430\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u0430.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/3m\/ui\/ps\/3muipsggpbx9johfvg-qt4ynrbm.jpeg\"><\/p>\n<p>  <\/p>\n<p>\u041e\u043d \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u0432 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f\u0445, \u043a\u043e\u0433\u0434\u0430 \u0432\u044b \u043b\u0438\u0431\u043e \u0438\u043c\u0435\u0435\u0442\u0435 \u0434\u0435\u043b\u043e \u0441 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e \u2014 \u0442\u043e \u0435\u0441\u0442\u044c \u0442\u043e\u0439, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0442\u0434\u0430\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 <code>completed<\/code> \u0438\u043b\u0438 <code>error<\/code> \u2014 \u043b\u0438\u0431\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0435 \u0447\u0438\u0441\u043b\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432<\/p>\n<p>  <\/p>\n<p><strong>RxBlocking<\/strong> \u0434\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432, \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u0430\u043c\u044b\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435: <\/p>\n<p>  <\/p>\n<ul>\n<li><code>toArray()<\/code>: \u0416\u0434\u0435\u0442 \u043a\u043e\u043d\u0446\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0432\u0441\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u043c.<\/li>\n<li><code>first()<\/code>: \u0416\u0434\u0435\u0442 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0435\u0433\u043e.<\/li>\n<li><code>last()<\/code>: \u0416\u0434\u0435\u0442 \u043a\u043e\u043d\u0446\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0438\u0437 \u043d\u0435\u0435.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0439 \u0441\u0438\u0442\u0443\u0446\u0430\u0438\u0438 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u043c \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 <code>first()<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u043a \u043a\u043b\u0430\u0441\u0441\u0443 <code>RaytronomeTests<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">func testNumeratorStartsAt4() throws {   XCTAssertEqual(try viewModel.numeratorText.toBlocking().first(), &quot;4&quot;)   XCTAssertEqual(try viewModel.numeratorValue.toBlocking().first(), 4) }  func testDenominatorStartsAt4() throws {   XCTAssertEqual(try viewModel.denominatorText.toBlocking().first(), &quot;4&quot;) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 <code>toBlocking()<\/code> \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 \u043f\u043e\u0442\u043e\u043a \u0432 <code>BlockingObservable<\/code>, \u0430 \u043f\u043e\u0442\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 <code>first()<\/code> \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u043e\u0436\u0434\u0430\u0442\u044c \u0438 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u044b\u0434\u0430\u043d\u043d\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442. \u0414\u0430\u043b\u0435\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c XCTAssert \u043a\u0430\u043a \u0438 \u0432 \u043b\u044e\u0431\u043e\u043c \u043e\u0431\u044b\u0447\u043d\u043e\u043c \u0442\u0435\u0441\u0442\u0435.<\/p>\n<p>  <\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u0435\u0442\u043e\u0434\u044b \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442 \u0432 \u0441\u0435\u0431\u044f <code>throws<\/code>, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u044b <strong>RxBlocking<\/strong> \u043c\u043e\u0433\u0443\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438. \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 <code>throws<\/code> \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u0432\u0438\u0434\u0430 <code>try!<\/code> \u0438 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043f\u0430\u0434\u0430\u044e\u0449\u0438\u0439 \u0442\u0435\u0441\u0442.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0436\u043c\u0438\u0442\u0435 <strong>Command-U<\/strong> \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/5m\/gw\/ej\/5mgwej1mv9lla8rklxni2pudbdu.png\"><\/p>\n<p>  <\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0443\u043f\u0440\u0430\u0436\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0435\u0449\u0435 \u0434\u0432\u0430 \u0442\u0435\u0441\u0442\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442 \u0447\u0442\u043e <code>signatureText<\/code> \u0438\u043c\u0435\u0435\u0442 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>4\/4<\/code>, \u0430 <code>tempoText<\/code> \u0438\u043c\u0435\u0435\u0442 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>120 BPM<\/code>. \u042d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0431\u0443\u0434\u0443\u0442 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436\u0438 \u043d\u0430 \u0442\u0435\u0441\u0442\u044b \u0432\u044b\u0448\u0435<\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e, \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043f\u043e\u0434 \u0441\u043f\u043e\u0439\u043b\u0435\u0440\u043e\u043c \u043d\u0438\u0436\u0435 <\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"swift\">func testSignatureStartsAt4By4() throws {   XCTAssertEqual(try viewModel.signatureText.toBlocking().first(), &quot;4\/4&quot;) }  func testTempoStartsAt120() throws {   XCTAssertEqual(try viewModel.tempoText.toBlocking().first(), &quot;120 BPM&quot;) }<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<h1 id=\"plyusy-i-minusy-rxblocking\">\u041f\u043b\u044e\u0441\u044b \u0438 \u043c\u0438\u043d\u0443\u0441\u044b RxBlocking<\/h1>\n<p>  <\/p>\n<p>\u041a\u0430\u043a \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, <strong>RxBlocking<\/strong> \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u0435\u043d \u0438 \u043f\u0440\u043e\u0441\u0442 \u0432 \u043e\u0441\u0432\u043e\u0435\u043d\u0438\u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0440\u043e\u0434\u0435 &quot;\u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442&quot; \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u043e\u043d\u0446\u0435\u043f\u0442\u044b \u0432 \u0445\u043e\u0440\u043e\u0448\u043e \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u044d\u0442\u043e \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0432\u0430\u043c \u0441\u0442\u043e\u0438\u0442 \u0437\u043d\u0430\u0442\u044c:<\/p>\n<p>  <\/p>\n<ol>\n<li>\u041e\u043d \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u043d\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0433\u043e \u0447\u0438\u0441\u043b\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432, \u0447\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442, \u0438\u043b\u0438 \u0432\u0441\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430, \u0442\u043e <strong>RxBlocking<\/strong> \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u0441\u0435\u0431\u044f \u043e\u0442\u043b\u0438\u0447\u043d\u043e. \u041e\u0434\u043d\u0430\u043a\u043e, \u0432 \u0431\u043e\u043b\u0435\u0435 \u0447\u0430\u0441\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u2014 \u043f\u0440\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438 \u0441 \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u044b\u043c\u0438 \u043f\u043e\u0442\u043e\u043a\u0430\u043c\u0438 \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <strong>RxBlocking<\/strong> \u043d\u0435 \u0434\u0430\u0441\u0442 \u0432\u0430\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e\u0439 \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u0438<\/li>\n<li><strong>RxBlocking<\/strong> \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044f \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043f\u043e\u0442\u043e\u043a \u0438 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u0434\u0430. \u0415\u0441\u043b\u0438 \u0432\u0430\u0448 <code>Observable<\/code> \u0432\u044b\u0434\u0430\u0435\u0442 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u043b\u0433\u0438\u0435 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043a\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0432\u0430\u0448 <code>BlockingObservable<\/code> \u0431\u0443\u0434\u0435\u0442 \u0438\u0445 \u0436\u0434\u0430\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f.<\/li>\n<li>\u041a\u043e\u0433\u0434\u0430 \u0432\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0432\u0430\u0436\u043d\u043e \u0432\u0440\u0435\u043c\u044f, <strong>RxBlocking<\/strong> \u043d\u0435 \u043f\u043e\u043c\u043e\u0436\u0435\u0442, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u0437\u0430\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043b\u0438\u0448\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0431\u0435\u0437 \u0438\u0445 \u0432\u0440\u0435\u043c\u0435\u043d\u0438.<\/li>\n<li>\u041f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u044f\u0442 \u043e\u0442 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 <strong>RxBlocking<\/strong> \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043f\u043e\u0442\u043e\u043a.<\/li>\n<\/ol>\n<p>  <\/p>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u0430\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u043e\u0438\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0443\u043f\u0438\u0440\u0430\u044e\u0442\u0441\u044f \u043f\u043e\u0447\u0442\u0438 \u0432\u043e \u0432\u0441\u0435 \u0438\u0437 \u044d\u0442\u0438\u0445 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \u041d\u0430\u0436\u0430\u0442\u0438\u0435 \u043a\u043d\u043e\u043f\u043a\u0438 <strong>Play\/Pause<\/strong> \u0434\u043e\u043b\u0436\u043d\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u0432 \u043f\u043e\u0442\u043e\u043a\u0435 <code>isPlaying<\/code>, \u0430 \u044d\u0442\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 (<code>tappedPlayPause<\/code> \u0432\u0445\u043e\u0434). \u0422\u0430\u043a\u0436\u0435 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u044b\u0437\u043e\u0432\u043e\u0432.<\/p>\n<p>  <\/p>\n<h1 id=\"rxtest\"><strong>RxTest<\/strong><\/h1>\n<p>  <\/p>\n<p>\u041a\u0430\u043a \u0431\u044b\u043b\u043e \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u0442\u043e \u0440\u0430\u043d\u0435\u0435, <strong>RxBlocking<\/strong> \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u0435\u043d, \u043d\u043e \u0435\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0442\u044c \u043a\u043e\u0433\u0434\u0430 \u0434\u043e\u0445\u043e\u0434\u0438\u0442 \u0434\u043e \u0442\u043e\u043d\u043a\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430, \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0438 \u0441\u0432\u044f\u0437\u0435\u0439 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c\u0438 \u0442\u0440\u0438\u0433\u0433\u0435\u0440\u0430\u043c\u0438.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u044d\u0442\u0438\u0445 \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 <strong>RxTest<\/strong>!<\/p>\n<p>  <\/p>\n<p><strong>RxTest<\/strong> \u043a\u0430\u0440\u0434\u0438\u043d\u0430\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 <strong>RxBlocking<\/strong>, \u0433\u043b\u0430\u0432\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0442\u0435\u043c, \u0447\u0442\u043e \u043e\u043d \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043e\u043a \u0432 \u0441\u0432\u043e\u0438\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044f\u0445, \u0438 \u0442\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043e\u043d \u0434\u0430\u0435\u0442 \u043e \u0432\u0430\u0448\u0438\u0445 \u043f\u043e\u0442\u043e\u043a\u0430\u0445. \u042d\u0442\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0438\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043e\u043d \u0434\u0430\u0435\u0442 \u0441\u0432\u043e\u0439 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u2014 <code>TestScheduler<\/code>.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/z6\/xg\/b3\/z6xgb30u1-4y4p7liyihbf3u8e0.jpeg\"><\/p>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434 \u0441\u0442\u043e\u0438\u0442 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u0442\u043e\u043c \u0447\u0442\u043e \u0436\u0435 \u0442\u0430\u043a\u043e\u0435 <strong>\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a<\/strong><\/p>\n<p>  <\/p>\n<h1 id=\"ponimanie-planirovschikov\">\u041f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u043e\u0432<\/h1>\n<p>  <\/p>\n<p><strong>\u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a<\/strong> \u2014 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0431\u043e\u043b\u0435\u0435 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0439 \u043a\u043e\u043d\u0446\u0435\u043f\u0442 RxSwift, \u043d\u043e \u0442\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435 \u043e\u0447\u0435\u043d\u044c \u0432\u0430\u0436\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u0447\u0442\u043e \u044d\u0442\u043e \u0442\u0430\u043a\u043e\u0435, \u043a\u0430\u043a \u043e\u043d\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442, \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043b\u0443\u0447\u0448\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u0438\u0445 \u0440\u043e\u043b\u044c \u0432 \u0432\u0430\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u0430\u0445<\/p>\n<p>  <\/p>\n<p>RxSwift \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0438 \u043a\u0430\u043a \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u0443\u043f\u043e\u0440\u044f\u0434\u043e\u0447\u0438\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043f\u043e\u044f\u0432\u0438\u0432\u0448\u0438\u0445\u0441\u044f \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u044d\u0442\u0438\u0445 \u0437\u0430\u0434\u0430\u0447. <\/p>\n<p>  <\/p>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0430\u0434\u0430\u0442\u044c \u0432\u043e\u043f\u0440\u043e\u0441: &quot;\u0417\u0430\u0447\u0435\u043c \u043e\u0431 \u044d\u0442\u043e\u043c \u0437\u043d\u0430\u0442\u044c?&quot;<\/p>\n<p>  <\/p>\n<p><strong>RxTest<\/strong> \u0434\u0430\u0435\u0442 \u0441\u0432\u043e\u0439 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u2014 <code>TestScheduler<\/code> \u2014 \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u041e\u043d \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u043e\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044f \u0432\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u043e\u043a\u043d\u0443\u0442\u044b\u0435 <code>Observable<\/code> \u0438 <code>Observer<\/code>, \u0442\u0430\u043a \u0447\u0442\u043e \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 &quot;\u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c&quot; \u0438\u0445 \u0438 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. <\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0433\u043b\u0443\u0431\u0436\u0435 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430\u0445 \u2014 <a href=\"https:\/\/github.com\/ReactiveX\/RxSwift\/blob\/master\/Documentation\/Schedulers.md\" rel=\"nofollow\">\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f<\/a> \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043d\u0430 \u044d\u0442\u0443 \u0442\u0435\u043c\u0443.<\/p>\n<p>  <\/p>\n<h1 id=\"testiruem-sobytiya-zavisimye-ot-vremeni\">\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0435 \u043e\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/h1>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <code>TestScheduler<\/code>. \u0412\u044b \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u0435 <code>DisposeBag<\/code> \u0432 \u0432\u0430\u0448 \u043a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f <code>Disposable<\/code> \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0432\u0430\u0448\u0438\u043c\u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438. \u041d\u0438\u0436\u0435 \u043f\u043e\u043b\u044f <code>viewModel<\/code> \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u043e\u043b\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">var scheduler: TestScheduler! var disposeBag: DisposeBag!<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u0442\u043e\u043c, \u0432 \u043a\u043e\u043d\u0446\u0435 \u043c\u0435\u0442\u043e\u0434\u0430 <code>setUp()<\/code>, \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u044b\u0445 <code>TestScheduler<\/code> \u0438 <code>DisposeBag<\/code> \u043f\u0435\u0440\u0435\u0434 \u043a\u0430\u0436\u0434\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u043c:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">scheduler = TestScheduler(initialClock: 0) disposeBag = DisposeBag()<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440 <code>TestScheduler<\/code> \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 <code>initialClock<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 &quot;\u0441\u0442\u0430\u0440\u0442\u043e\u0432\u043e\u0435 \u0432\u0440\u0435\u043c\u044f&quot; \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430. \u041d\u043e\u0432\u044b\u0439 DisposeBag \u043f\u043e\u0437\u0430\u0431\u043e\u0442\u0438\u0442\u0441\u044f \u043e \u0442\u043e\u043c \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u0432\u0441\u0435\u0445 \u043f\u043e\u0434\u043f\u0438\u0441\u043e\u043a. \u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u043f\u0440\u043e\u0448\u043b\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u043c.<\/p>\n<p>  <\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b!<\/p>\n<p>  <\/p>\n<p>\u0412\u0430\u0448 \u043f\u0435\u0440\u0432\u044b\u0439 \u0442\u0435\u0441\u0442 &quot;\u043d\u0430\u0436\u043c\u0435\u0442&quot; \u043a\u043d\u043e\u043f\u043a\u0443 <strong>Play\/Pause<\/strong> \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0438 \u0443\u0431\u0435\u0434\u0438\u0442\u0441\u044f, \u0447\u0442\u043e \u0432\u044b\u0445\u043e\u0434 <code>isPlaying<\/code> \u0440\u0435\u0430\u0433\u0438\u0440\u0443\u0435\u0442 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e:<\/p>\n<p>  <\/p>\n<ol>\n<li>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u043e\u043a <code>Observable<\/code> \u043f\u043e\u0442\u043e\u043a\u0430 \u0432\u044b\u0434\u0430\u044e\u0449\u0438\u0439 \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u0435 &quot;\u043d\u0430\u0436\u0430\u0442\u0438\u044f&quot; \u0432\u043e \u0432\u0445\u043e\u0434 tappedPlayPause.<\/li>\n<li>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u043e\u043a <code>Observer<\/code>&#8216;\u0430 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0432\u044b\u0445\u043e\u0434\u0430 <code>isPlaying<\/code>.<\/li>\n<li>\u0423\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f \u0447\u0442\u043e \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u043c.<\/li>\n<\/ol>\n<p>  <\/p>\n<p>\u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u043c, \u043d\u043e \u0432\u044b \u0443\u0434\u0438\u0432\u0438\u0442\u0435\u0441\u044c \u043a\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e \u044d\u0442\u043e \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435!<\/p>\n<p>  <\/p>\n<p>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u0435\u0449\u0438 \u043f\u0440\u043e\u0449\u0435 \u043e\u0431\u044c\u044f\u0441\u043d\u0438\u0442\u044c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435. \u041d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0432\u043e\u0433\u043e <strong>RxTest<\/strong>-\u0442\u0435\u0441\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">func testTappedPlayPauseChangesIsPlaying() {   \/\/ 1   let isPlaying = scheduler.createObserver(Bool.self)    \/\/ 2   viewModel.isPlaying     .drive(isPlaying)     .disposed(by: disposeBag)    \/\/ 3   scheduler.createColdObservable([.next(10, ()),                                   .next(20, ()),                                   .next(30, ())])            .bind(to: viewModel.tappedPlayPause)            .disposed(by: disposeBag)    \/\/ 4   scheduler.start()    \/\/ 5   XCTAssertEqual(isPlaying.events, [     .next(0, false),     .next(10, true),     .next(20, false),     .next(30, true)   ]) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0435 \u043f\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0439\u0442\u0435, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u043e\u0436\u043d\u044b\u043c. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043f\u043e \u0447\u0430\u0441\u0442\u044f\u043c:<\/p>\n<p>  <\/p>\n<ol>\n<li>\u0412\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f<code>TestScheduler<\/code> \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f <code>TestableObserver<\/code> \u0441 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0442\u043e\u0433\u043e \u0442\u0438\u043f\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c <code>Observable<\/code> \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u2014 \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e Bool. \u041e\u0434\u043d\u043e \u0438\u0437 \u0433\u043b\u0430\u0432\u043d\u044b\u0445 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432 \u044d\u0442\u043e\u0433\u043e <code>Observaer<\/code> \u044d\u0442\u043e \u0442\u043e \u2014 \u0447\u0442\u043e \u043e\u043d \u0438\u043c\u0435\u0435\u0442 \u043f\u043e\u043b\u0435 events, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043b\u044e\u0431\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0443\u0434\u0430 \u043f\u043e\u043f\u0430\u0434\u0430\u044e\u0442.<\/li>\n<li><code>drive()<\/code> &quot;\u043a\u043e\u043f\u0438\u0440\u0443\u0435\u0442&quot; \u0432\u0441\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0443\u0442 \u0432 \u043f\u043e\u043b\u0435 <code>viewModel.isPlaying<\/code> \u0432 \u043d\u043e\u0432\u044b\u0439 <code>TestableObserver<\/code>. \u042d\u0442\u043e \u0442\u043e \u043c\u0435\u0441\u0442\u043e \u0433\u0434\u0435 \u0432\u044b &quot;\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0435&quot; \u0441\u043e\u0431\u044b\u0442\u0438\u044f.<\/li>\n<li>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u043e\u043a <code>Observable<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0434\u0430\u0435\u0442 \u0432\u044b\u0445\u043e\u0434 \u043e\u0442 \u0442\u0440\u0435\u0445 &quot;\u043d\u0430\u0436\u0430\u0442\u0438\u0439&quot; \u0432 \u0432\u0445\u043e\u0434 <code>tappedPlayPause<\/code>. \u0415\u0449\u0435 \u0440\u0430\u0437, \u044d\u0442\u043e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u0438\u043f <code>Observable<\/code>, \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u043c\u044b\u0439 <code>TestableObservable<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0432\u0430\u0448 <code>TestScheduler<\/code> \u0434\u043b\u044f \u0432\u044b\u0437\u043e\u0432\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043d\u0430 \u0435\u0433\u043e &quot;\u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439&quot; \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043b\u0438\u043d\u0438\u0438.<\/li>\n<li>\u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c <code>start()<\/code> \u043d\u0430 \u0432\u0430\u0448\u0435\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u043c \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0435. \u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0432 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043e\u0436\u0438\u0434\u0430\u044e\u0449\u0438\u0435 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0448\u0430\u0433\u0430\u0445<\/li>\n<li>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043c\u0435\u0442\u043e\u0434\u0430 XCTAssertEqual \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439\u0441\u044f \u0432 <strong>RxTest<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0432\u0430\u043c \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432 isPlaying \u0438\u0434\u0435\u043d\u0442\u0438\u0447\u043d\u044b \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u043c \u043a\u0430\u043a \u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u0445, \u0442\u0430\u043a \u0438 \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. <code>10<\/code>, <code>20<\/code> \u0438 <code>30<\/code> \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0432\u0430\u0448 \u0432\u0445\u043e\u0434, \u0430 <code>0<\/code> \u2014 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0445 \u0432\u044b\u0445\u043e\u0434 <code>isPlaying<\/code>.<\/li>\n<\/ol>\n<p>  <\/p>\n<p>\u0417\u0430\u043f\u0443\u0442\u0430\u043b\u0438\u0441\u044c? \u041f\u043e\u0434\u0443\u043c\u0430\u0439\u0442\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c: \u0432\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0435 \u043c\u043e\u043a \u043f\u043e\u0442\u043e\u043a\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438 \u0441\u043a\u0430\u0440\u043c\u043b\u0438\u0432\u0430\u0435\u0442\u0435 \u0435\u0433\u043e \u0432\u043e \u0432\u0445\u043e\u0434 viewModel \u0432 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b \u0432\u0440\u0435\u043c\u0435\u043d\u0438. \u041f\u043e\u0442\u043e\u043c \u0432\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0435, \u0447\u0442\u043e \u0432\u044b\u0445\u043e\u0434 \u043e\u0442\u0434\u0430\u043b \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e\u0435 \u0432\u0440\u0435\u043c\u044f.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/i8\/gd\/xg\/i8gdxgqa6rlbihe_iwmugcpopxc.png\"><\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u0433\u043e\u043d\u0438\u0442\u0435 \u0441\u0432\u043e\u0438 \u0442\u0435\u0441\u0442\u044b \u0435\u0449\u0435 \u0440\u0430\u0437 \u043d\u0430\u0436\u0430\u0442\u0438\u0435\u043c <strong>Command-u<\/strong>. \u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c 5 \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/5m\/gw\/ej\/5mgwej1mv9lla8rklxni2pudbdu.png\"><\/p>\n<p>  <\/p>\n<h1 id=\"ponimaem-znacheniya-vremeni\">\u041f\u043e\u043d\u0438\u043c\u0430\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/h1>\n<p>  <\/p>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0432\u044b \u0437\u0430\u043c\u0435\u0442\u0438\u043b\u0438, \u0447\u0442\u043e \u0440\u0430\u043d\u0435\u0435 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 <code>0<\/code>, 10, 20 \u0438 30 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0438 \u0437\u0430\u0434\u0443\u043c\u0430\u043b\u0438\u0441\u044c \u043e \u0442\u043e\u043c \u0447\u0442\u043e \u044d\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442, \u0438 \u043a\u0430\u043a \u043e\u043d\u0438 \u0441\u043e\u043e\u0442\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u0441 <em>\u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c<\/em> \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c.<\/p>\n<p>  <\/p>\n<p><strong>RxTest<\/strong> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 (<code>Date<\/code>) \u0432 \u0442\u043e, \u0447\u0442\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f <code>VirtualTimeUnit<\/code> (\u0432\u044b\u0440\u0430\u0436\u0435\u043d\u043d\u044b\u0439 \u0447\u0435\u0440\u0435\u0437 <code>Int<\/code>).<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0441 <strong>RxSwift<\/strong> \u0432\u0440\u0435\u043c\u044f \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043b\u044e\u0431\u044b\u043c \u2014 \u043e\u043d\u0438 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b \u0438 <code>TestScheduler<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0438\u0445 \u0434\u043b\u044f \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043a\u0430\u043a \u043b\u044e\u0431\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a.<\/p>\n<p>  <\/p>\n<p>\u0412\u0430\u0436\u043d\u0430\u044f \u0432\u0435\u0449\u044c, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0441\u0442\u043e\u0438\u0442 \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0432 \u0433\u043e\u043b\u043e\u0432\u0435 \u2014 \u044d\u0442\u043e \u0442\u043e, \u0447\u0442\u043e \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043d\u0435 \u0441\u043e\u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u0441 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c, \u0442\u043e \u0435\u0441\u0442\u044c, <code>10<\/code> \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 10 \u0441\u0435\u043a\u0443\u043d\u0434, \u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0442\u0440\u0430\u0436\u0430\u0435\u0442 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f. \u0412\u044b \u0443\u0437\u043d\u0430\u0435\u0442\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u043e \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u043e\u0441\u0442\u044f\u0445 \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0430 \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435.<\/p>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0433\u043b\u0443\u0431\u043e\u043a\u043e\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 <code>TestScheduler<\/code>, \u043f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b \u0432\u0430\u043c \u043d\u0435 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u0442\u0435\u0441\u0442\u043e\u0432?<\/p>\n<p>  <\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0440\u0438 \u0442\u0435\u0441\u0442\u0430 \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">func testModifyingNumeratorUpdatesNumeratorText() {   let numerator = scheduler.createObserver(String.self)    viewModel.numeratorText            .drive(numerator)            .disposed(by: disposeBag)    scheduler.createColdObservable([.next(10, 3),                                   .next(15, 1)])            .bind(to: viewModel.steppedNumerator)            .disposed(by: disposeBag)    scheduler.start()    XCTAssertEqual(numerator.events, [     .next(0, &quot;4&quot;),     .next(10, &quot;3&quot;),     .next(15, &quot;1&quot;)   ]) }  func testModifyingDenominatorUpdatesNumeratorText() {   let denominator = scheduler.createObserver(String.self)    viewModel.denominatorText            .drive(denominator)            .disposed(by: disposeBag)    \/\/ Denominator (\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c) - \u044d\u0442\u043e 2 \u0432 \u0441\u0442\u0435\u043f\u0435\u043d\u0438 `steppedDenominator + 1`.   \/\/ f(1, 2, 3, 4) = 4, 8, 16, 32   scheduler.createColdObservable([.next(10, 2),                                   .next(15, 4),                                   .next(20, 3),                                   .next(25, 1)])           .bind(to: viewModel.steppedDenominator)           .disposed(by: disposeBag)    scheduler.start()    XCTAssertEqual(denominator.events, [     .next(0, &quot;4&quot;),     .next(10, &quot;8&quot;),     .next(15, &quot;32&quot;),     .next(20, &quot;16&quot;),     .next(25, &quot;4&quot;)   ]) }  func testModifyingTempoUpdatesTempoText() {   let tempo = scheduler.createObserver(String.self)    viewModel.tempoText            .drive(tempo)            .disposed(by: disposeBag)    scheduler.createColdObservable([.next(10, 75),                                   .next(15, 90),                                   .next(20, 180),                                   .next(25, 60)])            .bind(to: viewModel.tempo)            .disposed(by: disposeBag)    scheduler.start()    XCTAssertEqual(tempo.events, [     .next(0, &quot;120 BPM&quot;),     .next(10, &quot;75 BPM&quot;),     .next(15, &quot;90 BPM&quot;),     .next(20, &quot;180 BPM&quot;),     .next(25, &quot;60 BPM&quot;)   ]) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u042d\u0442\u0438 \u0442\u0440\u0438 \u0442\u0435\u0441\u0442\u0430 \u0434\u0435\u043b\u0430\u044e\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li><code>testModifyingNumeratorUpdatesNumeratorText<\/code>: \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044f \u0442\u0435\u043a\u0441\u0442 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0432\u0435\u0440\u043d\u043e.<\/li>\n<li><code>testModifyingDenominatorUpdatesNumeratorText<\/code>: \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044f \u0442\u0435\u043a\u0441\u0442 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0432\u0435\u0440\u043d\u043e.<\/li>\n<li><code>testModifyingTempoUpdatesTempoText<\/code>: \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0442\u0435\u043c\u043f\u0430 \u0442\u0435\u043a\u0441\u0442 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0432\u0435\u0440\u043d\u043e<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0432\u044b \u0443\u0436\u0435 \u0447\u0443\u0432\u0441\u0442\u0432\u0443\u0435\u0442\u0435 \u0441\u0435\u0431\u044f \u043a\u043e\u043c\u0444\u043e\u0440\u0442\u043d\u043e \u0441 \u044d\u0442\u0438\u043c \u043a \u043a\u043e\u0434\u043e\u043c, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u0438\u043b\u044c\u043d\u043e \u043f\u043e\u0445\u043e\u0436 \u043d\u0430 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u0442\u0435\u0441\u0442. \u0412\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0441\u044f \u043d\u0430 <code>3<\/code>, \u0430 \u0437\u0430\u0442\u0435\u043c \u043d\u0430 <code>1<\/code>. \u0418 \u0432\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0435, \u0447\u0442\u043e <code>numeratorText<\/code> \u043e\u0442\u0434\u0430\u0435\u0442 <code>&quot;4&quot;<\/code> (\u0441\u0442\u0430\u0440\u0442\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435), <code>&quot;3&quot;<\/code>, \u0438, \u0432 \u0438\u0442\u043e\u0433\u0435, <code>&quot;1&quot;<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0445\u043e\u0436\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0432\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0435, \u0447\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044f \u043c\u0435\u043d\u044f\u0435\u0442 <code>denominatorText<\/code> \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u0432\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0435 \u0447\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0442\u0435\u043c\u043f\u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u043e\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441 \u043a\u043e\u043d\u0446\u043e\u043c <code>BPM<\/code><\/p>\n<p>  <\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0442\u0435\u0441\u0442\u044b \u043d\u0430\u0436\u0430\u0442\u0438\u0435\u043c <strong>Command-U<\/strong>, \u0432\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435 8 \u043f\u0440\u043e\u0448\u0435\u0434\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432. \u041e\u0442\u043b\u0438\u0447\u043d\u043e!<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ku\/kz\/on\/kukzonm80a4h4ejacjvol_om6hk.png\"><\/p>\n<p>  <\/p>\n<p>\u0425\u043e\u0440\u043e\u0448\u043e, \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u0432\u044b \u0432\u0441\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442\u0435!<\/p>\n<p>  <\/p>\n<p>\u0412\u0440\u0435\u043c\u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0432\u0441\u0435 \u0441\u043b\u043e\u0436\u043d\u0438\u0442\u044c. \u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0442\u0435\u0441\u0442:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">func testModifyingSignatureUpdatesSignatureText() {   \/\/ 1   let signature = scheduler.createObserver(String.self)    viewModel.signatureText            .drive(signature)            .disposed(by: disposeBag)    \/\/ 2   scheduler.createColdObservable([.next(5, 3),                                   .next(10, 1),                                    .next(20, 5),                                   .next(25, 7),                                    .next(35, 12),                                    .next(45, 24),                                   .next(50, 32)                                 ])            .bind(to: viewModel.steppedNumerator)            .disposed(by: disposeBag)    \/\/ Denominator (\u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c) - \u044d\u0442\u043e 2 \u0432 \u0441\u0442\u0435\u043f\u0435\u043d\u0438 `steppedDenominator + 1`.   \/\/ f(1, 2, 3, 4) = 4, 8, 16, 32   scheduler.createColdObservable([.next(15, 2), \/\/ switch to 8ths                                   .next(30, 3), \/\/ switch to 16ths                                   .next(40, 4)  \/\/ switch to 32nds                                 ])            .bind(to: viewModel.steppedDenominator)            .disposed(by: disposeBag)    \/\/ 3   scheduler.start()    \/\/ 4   XCTAssertEqual(signature.events, [     .next(0, &quot;4\/4&quot;),     .next(5, &quot;3\/4&quot;),     .next(10, &quot;1\/4&quot;),      .next(15, &quot;1\/8&quot;),     .next(20, &quot;5\/8&quot;),     .next(25, &quot;7\/8&quot;),      .next(30, &quot;7\/16&quot;),     .next(35, &quot;12\/16&quot;),      .next(40, &quot;12\/32&quot;),     .next(45, &quot;24\/32&quot;),     .next(50, &quot;32\/32&quot;)   ]) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0413\u043b\u0443\u0431\u043e\u043a\u043e \u0432\u0437\u0434\u043e\u0445\u043d\u0438\u0442\u0435! \u0422\u0443\u0442 \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e \u0443\u0436\u0430\u0441\u043d\u043e\u0433\u043e \u0438\u043b\u0438 \u043f\u0443\u0433\u0430\u044e\u0449\u0435\u0433\u043e, \u044d\u0442\u043e \u043b\u0438\u0448\u044c \u0447\u0443\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0434\u043b\u0438\u043d\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b \u0443\u0436\u0435 \u043f\u0438\u0441\u0430\u043b\u0438. \u0412\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0441\u0440\u0430\u0437\u0443 \u0432\u043e \u0432\u0445\u043e\u0434\u044b <code>steppedNumerator<\/code> \u0438 <code>steppedDenominator<\/code> \u043f\u043e \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u0438\u0434\u043e\u0432 \u0442\u0430\u043a\u0442\u043e\u0432, \u0430 \u0434\u0430\u043b\u0435\u0435 \u0432\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0435 \u0447\u0442\u043e \u0432\u044b\u0445\u043e\u0434 <code>signatureText<\/code> \u0432\u044b\u0434\u0430\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0442\u0430\u043a\u0442\u044b. <\/p>\n<p>  <\/p>\n<p>\u042d\u0442\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0435\u0441\u043b\u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u0442\u0435\u0441\u0442 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/qx\/vq\/yc\/qxvqycmpxpymhqk7pslkndextpu.png\"><\/p>\n<p>  <\/p>\n<p>\u041d\u0435 \u0441\u0442\u0435\u0441\u043d\u044f\u0439\u0442\u0435\u0441\u044c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432\u0441\u0435 \u0442\u0435\u0441\u0442\u044b \u0435\u0449\u0435 \u0440\u0430\u0437. \u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u0432\u0430\u0441 9 \u0440\u0430\u0431\u043e\u0447\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432!<\/p>\n<p>  <\/p>\n<p>\u0414\u0430\u043b\u0435\u0435, \u0432\u044b \u043f\u043e\u0431\u0435\u0434\u0438\u0442\u0435 \u0435\u0449\u0435 \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u043a\u0435\u0439\u0441.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0434\u0443\u043c\u0430\u0439\u0442\u0435 \u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438:<\/p>\n<p>  <\/p>\n<ol>\n<li>\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441 \u0442\u0430\u043a\u0442\u0430 <code>4\/4<\/code><\/li>\n<li>\u0412\u044b \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0435\u0441\u044c \u043d\u0430 \u0442\u0430\u043a\u0442 <code>24\/32<\/code>.<\/li>\n<li>\u0414\u0430\u043b\u0435\u0435 \u0432\u044b \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 &quot;-&quot; \u043d\u0430 \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u0435; \u044d\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0442 \u0443\u043f\u0430\u0441\u0442\u044c \u0434\u043e 16\/16, \u043f\u043e\u0442\u043e\u043c 8\/8, \u0438, \u043d\u0430\u043a\u043e\u043d\u0435\u0446, 4\/4, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e 24\/16, 24\/8 \u0438 24\/4 \u2014 \u044d\u0442\u043e \u043d\u0435 \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u043c\u0435\u0442\u0440\u043e\u043d\u043e\u043c\u0430.<\/li>\n<\/ol>\n<p>  <\/p>\n<p><strong>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/strong>: \u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0442\u0430\u043a\u0442\u043e\u0432 \u0438\u043c\u0435\u044e\u0442 \u043c\u0435\u0441\u0442\u043e \u0431\u044b\u0442\u044c \u0432 \u043c\u0443\u0437\u044b\u043a\u0435, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0438\u0445 \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043c\u0435\u0442\u0440\u043e\u043d\u043e\u043c\u0430<\/p>\n<p>  <\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0442\u0435\u0441\u0442 \u0434\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">func testModifyingDenominatorUpdatesNumeratorValueIfExceedsMaximum() {   \/\/ 1   let numerator = scheduler.createObserver(Double.self)    viewModel.numeratorValue            .drive(numerator)            .disposed(by: disposeBag)    \/\/ 2    \/\/ Denominator (\u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c) - \u044d\u0442\u043e 2 \u0432 \u0441\u0442\u0435\u043f\u0435\u043d\u0438 `steppedDenominator + 1`.   \/\/ f(1, 2, 3, 4) = 4, 8, 16, 32   scheduler.createColdObservable([       .next(5, 4), \/\/ switch to 32nds       .next(15, 3), \/\/ switch to 16ths       .next(20, 2), \/\/ switch to 8ths       .next(25, 1)  \/\/ switch to 4ths       ])       .bind(to: viewModel.steppedDenominator)       .disposed(by: disposeBag)    scheduler.createColdObservable([.next(10, 24)])            .bind(to: viewModel.steppedNumerator)            .disposed(by: disposeBag)    \/\/ 3   scheduler.start()    \/\/ 4   XCTAssertEqual(numerator.events, [     .next(0, 4), \/\/ \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f 4\/4     .next(10, 24), \/\/ \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f 24\/32     .next(15, 16), \/\/ \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f 16\/16     .next(20, 8), \/\/ \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f 8\/8     .next(25, 4) \/\/ \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f 4\/4   ]) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0443\u0442\u0430\u043d\u043d\u043e, \u043d\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u0442\u0430\u043a\u043e\u0433\u043e, \u0447\u0435\u0433\u043e \u0432\u044b \u0431\u044b \u043d\u0435 \u043f\u043e\u043d\u044f\u043b\u0438! \u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043f\u043e \u0447\u0430\u0441\u0442\u044f\u043c:<\/p>\n<p>  <\/p>\n<ol>\n<li>\u041a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u043e, \u0432\u044b \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0435 \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f <code>TestableObserver<\/code> \u0438 &quot;\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f&quot; \u0432\u044b\u0445\u043e\u0434\u0430 <code>numeratorValue<\/code> \u0432 \u043d\u0435\u0433\u043e<\/li>\n<li>\u0422\u0443\u0442 \u0432\u0441\u0435 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0436\u043d\u0435\u0435, \u043d\u043e \u0432\u0437\u0433\u043b\u044f\u0434 \u043d\u0430 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u0432\u0441\u0435 \u043f\u043e\u043d\u044f\u0442\u043d\u0435\u0435. \u0412\u044b \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0435 \u0441 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043d\u0430 \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c <code>32<\/code>, \u0430 \u043f\u043e\u0442\u043e\u043c \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0435\u0441\u044c \u043d\u0430 \u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c <code>24<\/code> (\u0432\u043e \u0432\u0442\u043e\u0440\u043e\u043c \u043f\u043e\u0442\u043e\u043a\u0435). \u042d\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0442\u0430\u043a\u0442 <code>24\/32<\/code>. \u0414\u0430\u043b\u0435\u0435 \u0432\u044b \u0443\u043c\u0435\u043d\u044c\u0448\u0430\u0435\u0442\u0435 \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c \u0448\u0430\u0433 \u0437\u0430 \u0448\u0430\u0433\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 <code>numeratorValue<\/code>.<\/li>\n<li>\u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c <code>schelduer<\/code><\/li>\n<li>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0447\u0442\u043e \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u0448\u0430\u0433\u0443 \u0432\u044b\u0434\u0430\u044e\u0442\u0441\u044f \u0432\u0435\u0440\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f <code>numeratorValue<\/code><\/li>\n<\/ol>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/zg\/6-\/am\/zg6-am5rxzmkzbi2try6cbkfwfw.png\"><\/p>\n<p>  <\/p>\n<p>\u0412\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u0442\u0435\u0441\u0442! \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0442\u0435\u0441\u0442\u044b \u043d\u0430\u0436\u0430\u0442\u0438\u0435\u043c <strong>Command-U<\/strong>:<\/p>\n<p>  <\/p>\n<blockquote><p>XCTAssertEqual failed: (&quot;[next(4.0) @ 0, next(24.0) @ 10]&quot;) is not equal to (&quot;[next(4.0) @ 0, next(24.0) @ 10, next(16.0) @ 15, next(8.0) @ 20, next(4.0) @ 25]&quot;) &#8212;<\/p><\/blockquote>\n<p>\u041e \u043d\u0435\u0442! \u0422\u0435\u0441\u0442 \u043f\u0440\u043e\u0432\u0430\u043b\u0438\u043b\u0441\u044f.<\/p>\n<p>  <\/p>\n<p>\u0421\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043a\u0430\u0436\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0432\u044b\u0445\u043e\u0434 <code>numeratorValue<\/code> \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0440\u0430\u0432\u0435\u043d <code>24<\/code>, \u0434\u0430\u0436\u0435 \u043a\u043e\u0433\u0434\u0430 \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c \u043f\u0430\u0434\u0430\u0435\u0442, \u0447\u0442\u043e \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0430\u0441 \u0441 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u043c\u0438 \u0442\u0435\u043c\u043f\u0430\u043c\u0438 \u0432\u0440\u043e\u0434\u0435 <code>24\/16<\/code> \u0438 <code>24\/4<\/code>. \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u0430\u043c\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u041f\u043e\u0434\u043d\u0438\u043c\u0438\u0442\u0435 \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c, \u0434\u043e \u0442\u0430\u043a\u0442\u0430 \u0432 <code>4\/8<\/code>.<\/li>\n<li>\u0414\u0430\u043b\u0435\u0435 \u043f\u043e\u0434\u043d\u0438\u043c\u0438\u0442\u0435 \u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0430\u043a\u0442 \u0441\u0442\u0430\u043b <code>7\/8<\/code>.<\/li>\n<li>\u041e\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c. \u0412 \u0442\u0435\u043e\u0440\u0438\u0438 \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0442\u0430\u043a\u0442 <code>4\/4<\/code>, \u043d\u043e \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432\u044b \u0432\u0438\u0434\u0438\u0442\u0435 <code>7\/4<\/code> \u2014 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u043c\u0435\u0442\u0440\u043e\u043d\u043e\u043c\u0430!<\/li>\n<\/ul>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/tk\/ca\/xi\/tkcaxiwuqco3fh7q8c5y7tadho4.gif\"><\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0445\u043e\u0436\u0435, \u0432\u044b \u043d\u0430\u0448\u043b\u0438 \u0431\u0430\u0433. :]<\/p>\n<p>  <\/p>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u0432\u044b\u0431\u043e\u0440 \u0438 \u043f\u043e\u043f\u0440\u0430\u0432\u0438\u043c \u0435\u0433\u043e.<\/p>\n<p>  <\/p>\n<p>\u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 <strong>MetronomeViewModel.swift<\/strong> \u0438 \u043d\u0430\u0439\u0434\u0438\u0442\u0435 \u044d\u0442\u043e\u0442 \u043a\u0443\u0441\u043e\u043a \u043a\u043e\u0434\u0430, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u0439 \u0437\u0430 \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 <code>numeratorValue<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">numeratorValue = steppedNumerator   .distinctUntilChanged()   .asDriver(onErrorJustReturn: 0)<\/code><\/pre>\n<p>  <\/p>\n<p>\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u0435\u0433\u043e \u043d\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">numeratorValue = steppedNumerator   .distinctUntilChanged()   .asDriver(onErrorJustReturn: 0)<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431 \u043f\u0440\u043e\u0441\u0442\u043e \u0431\u0440\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>steppedNumerator<\/code> \u0438 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0435\u0433\u043e, \u0432\u044b \u0441\u043e\u0432\u043c\u0435\u0449\u0430\u0435\u0442\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>steppedNumerator<\/code> \u0438 <code>maxNumerator<\/code> \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0435 \u043c\u0435\u043d\u044c\u0448\u0435\u0435.<\/p>\n<p>  <\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0432\u0430\u0448\u0438 \u0442\u0435\u0441\u0442\u044b \u0441\u043d\u043e\u0432\u0430 \u043d\u0430\u0436\u0430\u0442\u0438\u0435\u043c <strong>Command-U<\/strong>, \u0438 \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c 10 \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u044b\u0445 \u0443\u0434\u0430\u0447\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432. \u041f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430!<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/q_\/qs\/un\/q_qsun6owwgji9wsrqkjarak3si.jpeg\"><\/p>\n<p>  <\/p>\n<h1 id=\"testirovanie-chuvstvitelnyh-ko-vremeni-sobytiy\">\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043a\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439<\/h1>\n<p>  <\/p>\n<p>\u0412\u044b \u043f\u0440\u043e\u0448\u043b\u0438 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0434\u0430\u043b\u0435\u043a\u043e \u0432 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 <code>viewModel<\/code>. \u0415\u0441\u043b\u0438 \u0432\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0430 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u043a\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u0442\u043e \u0432\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435 \u0446\u0438\u0444\u0440\u0443 \u043e\u043a\u043e\u043b\u043e 78%. \u0412\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u043d\u044f\u0442\u044c \u0435\u0433\u043e \u0434\u043e \u0441\u0430\u043c\u043e\u0433\u043e \u0432\u0435\u0440\u0445\u0430!<\/p>\n<p>  <\/p>\n<p><strong>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/strong>: \u0414\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u043a\u043e\u0434\u0430, \u043d\u0430\u0436\u043c\u0438\u0442\u0435 <strong>Edit Scheme&#8230;<\/strong> \u0432 \u0434\u0440\u043e\u043f-\u0434\u0430\u0443\u043d\u0435 \u0441\u043e \u0441\u0445\u0435\u043c\u0430\u043c\u0438, \u0438, \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 <strong>Tests<\/strong>, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0432\u043a\u043b\u0430\u0434\u043a\u0443 <strong>Options<\/strong> \u0438 \u043e\u0442\u043c\u0435\u0442\u044c\u0442\u0435 <strong>Code Coverage<\/strong>. \u0412\u044b\u0431\u0435\u0440\u0435\u0442\u0435 <strong>Gather coverage for some targets<\/strong> \u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0442\u0430\u0440\u0433\u0435\u0442 <strong>Raytronome<\/strong> \u0432 \u044d\u0442\u043e\u0442 \u0441\u043f\u0438\u0441\u043e\u043a. \u041f\u043e\u0441\u043b\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432 \u0432 <strong>Report Navigator<\/strong> \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043f\u0440\u043e\u0446\u0435\u043d\u0442 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f \u043a\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u0430\u043c\u0438<\/p>\n<p>  <\/p>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0432\u0441\u0435\u0433\u043e \u0434\u0432\u0435 \u0432\u0435\u0449\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043a\u0440\u044b\u0442\u044c. \u041f\u0435\u0440\u0432\u0430\u044f \u2014 \u0441\u0430\u043c \u0431\u0438\u0442 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c\u044b\u0439 \u043c\u0435\u0442\u0440\u043e\u043d\u043e\u043c\u043e\u043c.<\/p>\n<p>  <\/p>\n<p>\u041c\u044b \u0445\u043e\u0442\u0438\u043c \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043f\u0440\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u043c \u0442\u0430\u043a\u0442\u0435\/\u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438, \u0431\u0438\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0440\u0430\u0432\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u044b, \u0438 \u0447\u0442\u043e \u0441\u0430\u043c \u0431\u0438\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 (\u043f\u0435\u0440\u0432\u044b\u0439 \u0443\u0434\u0430\u0440 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445)<\/p>\n<p>  <\/p>\n<p>\u0412\u044b \u043d\u0430\u0447\u043d\u0435\u0442\u0435 \u0441 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044f \u2014 <code>32<\/code>. \u0412\u0435\u0440\u043d\u0438\u0442\u0435\u0441\u044c \u043a <strong>RaytronomeTests.swift<\/strong> \u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0442\u0435\u0441\u0442: <\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">func testBeatBy32() {   \/\/ 1   viewModel = MetronomeViewModel(initialMeter: Meter(signature: &quot;4\/32&quot;),                                  autoplay: true,                                  beatScheduler: scheduler)    \/\/ 2   let beat = scheduler.createObserver(Beat.self)   viewModel.beat.asObservable()     .take(8)     .bind(to: beat)     .disposed(by: disposeBag)    \/\/ 3   scheduler.start()    XCTAssertEqual(beat.events, []) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u043a\u0430 \u0447\u0442\u043e \u044d\u0442\u043e\u0442 \u0442\u0435\u0441\u0442 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c. \u041d\u043e \u0432\u0441\u0435-\u0436\u0435 \u0440\u0430\u0437\u043e\u0431\u044c\u0435\u043c \u0435\u0433\u043e \u043d\u0430 \u0447\u0430\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<ol>\n<li>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430 \u0432\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u0442\u0435 <code>viewModel<\/code> \u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0447\u0435\u0442\u0447\u0438\u043a \u0441 \u0442\u0430\u043a\u0442\u043e\u043c <code>4\/32<\/code>, \u0438 \u0441\u043a\u0430\u0436\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u0438 \u043d\u0430\u0447\u0430\u0442\u044c \u0431\u0438\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438, \u0447\u0442\u043e \u0438\u0437\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0442 \u0432\u044b\u0437\u043e\u0432\u0430 <code>tappedPlayPause<\/code> \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438.<br \/>  \u0422\u0440\u0435\u0442\u0438\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u0442\u043e\u0436\u0435 \u0432\u0430\u0436\u0435\u043d. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, viewModel \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 <code>SerialDispatchQueueScheduler<\/code> \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0431\u0438\u0442\u0430, \u043d\u043e \u043f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0431\u0438\u0442\u0430 \u0432\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0442\u0443\u0434\u0430 <code>TestScheduler<\/code>, \u0442\u0430\u043a \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0431\u0438\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043d\u0435\u043c.<\/li>\n<li>\u0421\u043e\u0437\u0434\u0430\u0435\u043c <code>TestableObserver<\/code> \u0434\u043b\u044f \u0442\u0438\u043f\u0430 <code>Beat<\/code> \u0438 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c 8 \u043f\u0435\u0440\u0432\u044b\u0445 \u0435\u0433\u043e \u0443\u0434\u0430\u0440\u043e\u0432 \u0438\u0437 \u0432\u044b\u0445\u043e\u0434\u0430 <code>beat<\/code>. <code>8<\/code> \u0443\u0434\u0430\u0440\u043e\u0432 \u2014 \u0434\u0432\u0430 \u0446\u0438\u043a\u043b\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e.<\/li>\n<li>\u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c <code>scheduler<\/code>. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0432\u044b \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u0442\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0441 \u043f\u0443\u0441\u0442\u044b\u043c \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u043c, \u0437\u043d\u0430\u044f, \u0447\u0442\u043e \u0442\u0435\u0441\u0442 \u043f\u0440\u043e\u0432\u0430\u043b\u0438\u0442\u0441\u044f \u2014 \u0433\u043b\u0430\u0432\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u043a\u0430\u043a\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438 \u0432\u0440\u0435\u043c\u044f \u0432\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435.<\/li>\n<\/ol>\n<p>  <\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0442\u0435\u0441\u0442\u044b \u043d\u0430\u0436\u0430\u0442\u0438\u0435\u043c <strong>Command-U<\/strong>. \u0412\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<blockquote><p>XCTAssertEqual failed: (&quot;[next(first) @ 1, next(regular) @ 2, next(regular) @ 3, next(regular) @ 4, next(first) @ 5, next(regular) @ 6, next(regular) @ 7, next(regular) @ 8, completed @ 8]&quot;) is not equal to (&quot;[]&quot;) \u2014<\/p><\/blockquote>\n<p>\u0421\u0443\u0434\u044f \u043f\u043e \u0432\u0441\u0435\u043c\u0443 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432\u044b\u0434\u0430\u044e\u0442 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u043d\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u0442\u0440\u0430\u043d\u043d\u043e, \u043d\u0435 \u0442\u0430\u043a \u043b\u0438? \u041f\u0440\u043e\u0441\u0442\u043e \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0437 \u0447\u0438\u0441\u0435\u043b \u043e\u0442 1 \u0434\u043e 8.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f, \u0447\u0442\u043e \u0447\u0442\u043e-\u0442\u043e \u043d\u0435 \u0442\u0430\u043a, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0447\u0435\u0442\u0447\u0438\u043a \u0441 <strong>4\/32<\/strong> \u043d\u0430 <strong>4\/4<\/strong>. \u042d\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0432\u044b\u0434\u0430\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432 \u0434\u0440\u0443\u0433\u043e\u0435 \u0432\u0440\u0435\u043c\u044f, \u043a\u0430\u043a \u0438 \u0441\u0430\u043c \u0431\u0438\u0442.<\/p>\n<p>  <\/p>\n<p>\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u0435 <code>Meter(signature: &quot;4\/32&quot;)<\/code> \u043d\u0430 <code>Meter(signature: &quot;4\/4&quot;)<\/code> \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0442\u0435\u0441\u0442\u044b \u0441\u043d\u043e\u0432\u0430 \u043d\u0430\u0436\u0430\u0442\u0438\u0435\u043c <strong>Command-U<\/strong>. \u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0442\u0430\u043a\u0443\u044e-\u0436\u0435 \u043e\u0448\u0438\u0431\u043a\u0443 \u0441 <em>\u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e<\/em> \u0442\u0430\u043a\u0438\u043c-\u0436\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c.<\/p>\n<p>  <\/p>\n<p>\u0412\u043e\u0443, \u044d\u0442\u043e \u0441\u0442\u0440\u0430\u043d\u043d\u043e! \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0432\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432 \u0442\u0430\u043a\u043e\u0435-\u0436\u0435 \u0432\u0440\u0435\u043c\u044f, \u043a\u0430\u043a \u0438 \u0432 \u043f\u0435\u0440\u0432\u044b\u0439 \u0440\u0430\u0437. \u041a\u0430\u043a \u0442\u0430\u043a \u0432\u044b\u0448\u043b\u043e, \u0447\u0442\u043e \u0440\u0430\u0437\u043d\u044b\u0435 \u0442\u0430\u043a\u0442\u044b \u0432\u044b\u0434\u0430\u043b\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432 \u043e\u0434\u043d\u043e \u0432\u0440\u0435\u043c\u044f? \u0427\u0442\u043e-\u0436, \u044d\u0442\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 <code>VirtualTimeUnit<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0436\u0435 \u0431\u044b\u043b \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u0442 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u043d\u044c\u0448\u0435. <\/p>\n<p>  <\/p>\n<h1 id=\"vybiraem-tochnost\">\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c<\/h1>\n<p>  <\/p>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0442\u0435\u043c\u043f \u0432 <code>120 BPM<\/code>, \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c <code>4<\/code> (\u043a\u0430\u043a \u0434\u043b\u044f <code>4\/4<\/code>), \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0443\u0434\u0430\u0440 \u0431\u0438\u0442\u0430 \u043a\u0430\u0436\u0434\u044b\u0435 <code>0.5<\/code> \u0441\u0435\u043a\u0443\u043d\u0434. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044c <code>32<\/code>, \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0443\u0434\u0430\u0440 \u043a\u0430\u0436\u0434\u044b\u0435 <code>0.0625<\/code> \u0441\u0435\u043a\u0443\u043d\u0434.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u043d\u0438\u044f \u0442\u043e\u0433\u043e, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u2014 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043b\u0443\u0447\u0448\u0435 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u0442\u043e\u043c \u043a\u0430\u043a <code>TestScheduler<\/code> \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f, \u0432 \u0441\u0432\u043e\u0438 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0435 <code>VirtualTimeUnit<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442\u0435 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u044f \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0435 \u0441\u0435\u043a\u0443\u043d\u0434\u044b \u043d\u0430 \u043d\u0435\u0447\u0442\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u043c\u043e\u0435 <code>resolution<\/code> \u0438 \u043e\u043a\u0440\u0443\u0433\u043b\u044f\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0430\u0432\u0435\u0440\u0445. <code>resolution<\/code> \u2014 \u0447\u0430\u0441\u0442\u044c <code>TestScheduler<\/code> \u0438 \u0438\u043c\u0435\u0435\u0442 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>1<\/code>.<\/p>\n<p>  <\/p>\n<p><code>0.0625\/1<\/code> \u043e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u043d\u043e\u0435 \u043d\u0430\u0432\u0435\u0440\u0445 \u0440\u0430\u0432\u043d\u043e <code>1<\/code>, \u043d\u043e \u043e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435 <code>0.5\/1<\/code> \u0442\u0430\u043a\u0436\u0435 \u0434\u0430\u0435\u0442 1, \u0447\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0442\u043e\u0447\u043d\u043e \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0430 \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0440\u043e\u0434\u0430. <\/p>\n<p>  <\/p>\n<p>\u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c <code>resolution<\/code>, \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0430 \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0440\u043e\u0434\u0430.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b\u0448\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 <code>viewModel<\/code>, \u043d\u0430 \u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u0432\u0430\u0448\u0435\u0433\u043e \u0442\u0435\u0441\u0442\u0430, \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">scheduler = TestScheduler(initialClock: 0, resolution: 0.01)<\/code><\/pre>\n<p>  <\/p>\n<p>\u042d\u0442\u043e \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442 <code>resolution<\/code> \u0438 \u0434\u0430\u0441\u0442 \u0431\u043e\u043b\u0435\u0435 \u0432\u044b\u0441\u043e\u043a\u0443\u044e \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0438 \u043e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0438 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/p>\n<p>  <\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u0438 <code>resoulution<\/code><\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/r0\/8m\/fo\/r08mfosmvebci7ztzq-dv_zdxqe.png\"><\/p>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0441\u0432\u043e\u0439 \u0441\u0447\u0435\u0442\u0447\u0438\u043a \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043d\u0430 <code>4\/32<\/code> \u0432 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0435 viewModel \u0438 \u0441\u043d\u043e\u0432\u0430 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0442\u0435\u0441\u0442\u044b \u043d\u0430\u0436\u0430\u0442\u0438\u0435\u043c <strong>Command-U<\/strong>.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b \u043d\u0430\u043a\u043e\u043d\u0435\u0446 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u0431\u043e\u043b\u0435\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u0442\u043c\u0435\u0442\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c.<\/p>\n<p>  <\/p>\n<blockquote><p>XCTAssertEqual failed: (&quot;[next(first) @ 6, next(regular) @ 12, next(regular) @ 18, next(regular) @ 24, next(first) @ 30, next(regular) @ 36, next(regular) @ 42, next(regular) @ 48, completed @ 48]&quot;) is not equal to (&quot;[]&quot;) \u2014<\/p><\/blockquote>\n<p>\u0423\u0434\u0430\u0440\u044b \u043d\u0430\u043a\u043e\u043d\u0435\u0446 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043a\u043e\u043c \u0432 <code>6<\/code>. \u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 <code>XCTAssertEqual<\/code> \u0441 \u044d\u0442\u0438\u043c:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">XCTAssertEqual(beat.events, [   .next(6, .first),   .next(12, .regular),   .next(18, .regular),   .next(24, .regular),   .next(30, .first),   .next(36, .regular),   .next(42, .regular),   .next(48, .regular),   .completed(48) ])<\/code><\/pre>\n<p>  <\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0442\u0435\u0441\u0442\u044b \u0435\u0449\u0435 \u0440\u0430\u0437 \u043d\u0430\u0436\u0430\u0442\u0438\u0435\u043c <strong>Command-U<\/strong>, \u0438 \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u0442\u0435\u0441\u0442 \u043d\u0430\u043a\u043e\u043d\u0435\u0446 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442. \u041e\u0442\u043b\u0438\u0447\u043d\u043e!<\/p>\n<p>  <\/p>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0430\u043a\u043e\u0439-\u0436\u0435 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u0438\u0442\u0430 <code>4\/4<\/code> \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436\u0435.<\/p>\n<p>  <\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0442\u0435\u0441\u0442:<\/p>\n<p>  <\/p>\n<pre><code class=\"swift\">func testBeatBy4() {   scheduler = TestScheduler(initialClock: 0, resolution: 0.1)    viewModel = MetronomeViewModel(initialMeter: Meter(signature: &quot;4\/4&quot;),                                  autoplay: true,                                  beatScheduler: scheduler)    let beat = scheduler.createObserver(Beat.self)   viewModel.beat.asObservable()     .take(8)     .bind(to: beat)     .disposed(by: disposeBag)    scheduler.start()    XCTAssertEqual(beat.events, [     .next(5, .first),     .next(10, .regular),     .next(15, .regular),     .next(20, .regular),     .next(25, .first),     .next(30, .regular),     .next(35, .regular),     .next(40, .regular),     .completed(40)   ]) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u0442\u0443\u0442 \u2014 \u044d\u0442\u043e \u0442\u043e, \u0447\u0442\u043e \u0432\u044b \u043f\u043e\u0434\u043d\u044f\u043b\u0438 <code>resolution<\/code>, \u0434\u043e <code>0.1<\/code>, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u0434\u0430\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u0443\u044e \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c \u0434\u043b\u044f \u0437\u043d\u0430\u043c\u0435\u043d\u0430\u0442\u0435\u043b\u044f <code>4<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0441\u0432\u043e\u0438 \u0442\u0435\u0441\u0442\u044b \u0441\u043d\u043e\u0432\u0430 \u043d\u0430\u0436\u0430\u0442\u0438\u0435\u043c <strong>Command-U<\/strong>, \u0438 \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u0432\u0441\u0435 12 \u0442\u0435\u0441\u0442\u043e\u0432 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0442!<\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0430 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u043a\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u0442\u043e \u0432\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435, \u0447\u0442\u043e \u0443 \u0432\u0430\u0441 <code>99.25%<\/code> \u043f\u043e\u043a\u0440\u044b\u0442\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0434\u043b\u044f <code>MetronomeViewModel<\/code>, \u0447\u0442\u043e \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u043e. \u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0432\u044b\u0445\u043e\u0434 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u043d\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0438: <code>beatType<\/code>.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/av\/5f\/np\/av5fnpbdvhymdisbsujrwswf344.png\"><\/p>\n<p>  <\/p>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 <code>beatType<\/code> \u2014 \u043d\u0435\u043f\u043b\u043e\u0445\u043e\u0435 \u0438\u0441\u043f\u044b\u0442\u0430\u043d\u0438\u0435 \u0434\u043b\u044f \u0441\u0435\u0431\u044f \u043d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435, \u0442\u0430\u043a \u043a\u0430\u043a \u0442\u0435\u0441\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436 \u043d\u0430 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0435 \u0434\u0432\u0430 \u0442\u0435\u0441\u0442\u0430, \u0437\u0430 \u0442\u0435\u043c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c, \u0447\u0442\u043e <code>beatType<\/code> \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043c\u0435\u0436\u0434\u0443 <code>.even<\/code> \u0438 <code>.odd<\/code>. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u0442\u0435\u0441\u0442 \u0441\u0430\u043c\u0438. \u0415\u0441\u043b\u0438 \u0432\u044b \u043f\u043e\u0447\u0443\u0432\u0441\u0442\u0432\u0443\u0435\u0442\u0435, \u0447\u0442\u043e \u0437\u0430\u0441\u0442\u0440\u044f\u043b\u0438, \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043f\u043e\u0434 \u0441\u043f\u043e\u0439\u043b\u0435\u0440\u043e\u043c:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"swift\">func testBeatTypeAlternates() {   scheduler = TestScheduler(initialClock: 0, resolution: 0.1)    viewModel = MetronomeViewModel(initialMeter: Meter(signature: &quot;4\/4&quot;),                                  autoplay: true,                                  beatScheduler: scheduler)    let beatType = scheduler.createObserver(BeatType.self)   viewModel.beatType.asObservable()     .take(8)     .bind(to: beatType)     .disposed(by: disposeBag)    scheduler.start()    XCTAssertEqual(beatType.events, [     .next(5, .even),     .next(10, .odd),     .next(15, .even),     .next(20, .odd),     .next(25, .even),     .next(30, .odd),     .next(35, .even),     .next(40, .odd),     .completed(40)   ]) }<\/code><\/pre>\n<\/div><\/div>\n<p>  <\/p>\n<h1 id=\"chto-delat-dalshe\">\u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435?<\/h1>\n<p>  <\/p>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u043a\u043d\u043e\u043f\u043a\u0443 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u0441\u0442\u0430\u0442\u044c\u0438. \u0412\u044b \u0437\u043d\u0430\u0435\u0442\u0435 \u0432\u0441\u0435, \u0447\u0442\u043e \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0447\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 RxSwift. \u0412\u044b \u0443\u0437\u043d\u0430\u043b\u0438 \u043a\u0430\u043a \u043f\u043e\u043b\u0435\u0437\u0435\u043d <strong>RxBlocking<\/strong> \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0445 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0435\u0439 \u0433\u0434\u0435 \u0432\u044b \u043d\u0435 \u0437\u0430\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u043d\u044b \u0432\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u0442\u043e\u0433\u0434\u0430 \u043a\u0430\u043a <strong>RxTest<\/strong> \u0434\u0430\u0435\u0442 \u0432\u0430\u043c \u043e\u0433\u0440\u043e\u043c\u043d\u0443\u044e \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u044c \u0438 \u043c\u043e\u0449\u044c \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p>  <\/p>\n<p>\u0418 \u0432\u044b \u0434\u0430\u0436\u0435 \u0434\u043e\u0442\u0440\u043e\u043d\u0443\u043b\u0438\u0441\u044c \u0434\u043e \u0431\u043e\u043b\u0435\u0435 \u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043a <code>Scheduler<\/code>, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0443\u0437\u043d\u0430\u043b\u0438 \u043a\u0430\u043a <code>TestScheduler<\/code> \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f.<\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u0442\u044c \u0435\u0449\u0435 \u043c\u043d\u043e\u0433\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0443\u0437\u043d\u0430\u0442\u044c \u043a\u0430\u043a \u0432 <strong>RxSwift<\/strong>, \u0442\u0430\u043a \u0438 \u0432 <strong>RxBlocking<\/strong> \u2014 \u0438\u0445 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044e\u044e \u0440\u0430\u0431\u043e\u0442\u0443, \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u044b \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u041b\u0443\u0447\u0448\u0435\u0435 \u043c\u0435\u0441\u0442\u043e \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u044f \u2014 <a href=\"https:\/\/github.com\/ReactiveX\/RxSwift\/blob\/master\/Documentation\/UnitTests.md\" rel=\"nofollow\">\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f <strong>RxSwift<\/strong><\/a>, \u0430 \u0442\u0430\u043a\u0436\u0435 <a href=\"https:\/\/github.com\/ReactiveX\/RxSwift\/tree\/master\/RxBlocking\" rel=\"nofollow\">\u0441\u043f\u0438\u0441\u043e\u043a \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432 <strong>RxBlocking<\/strong><\/a>.<\/p>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043e\u0441\u0442\u0430\u043b\u0438\u0441\u044c \u0432\u043e\u043f\u0440\u043e\u0441\u044b, \u0438\u043b\u0438 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u043f\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044e \u0441\u0442\u0430\u0442\u044c\u0438 \u2014 \u0434\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438, \u0438\u043b\u0438 \u0432 \u043e\u0431\u0441\u0443\u0436\u0434\u0435\u043d\u0438\u0438 \u043a <a href=\"https:\/\/www.raywenderlich.com\/7408-testing-your-rxswift-code\" rel=\"nofollow\">\u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a>. \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u0435!<strong>\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/strong>, \u0434\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u043c <a href=\"https:\/\/www.raywenderlich.com\/7408-testing-your-rxswift-code\" rel=\"nofollow\">\u0441\u0442\u0430\u0442\u044c\u0438<\/a> \u0441 \u0441\u0430\u0439\u0442\u0430 <a href=\"http:\/\/raywenderlich.com\" rel=\"nofollow\">raywenderlich.com<\/a>.<\/p>\n<\/div>\n<p> \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\/post\/502916\/\"> https:\/\/habr.com\/ru\/post\/502916\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/502916\/\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e \u0432\u0430\u0448\u0435\u043c\u0443 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u044e \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/www.raywenderlich.com\/7408-testing-your-rxswift-code\" rel=\"nofollow\">&quot;Testing Your RxSwift Code&quot;<\/a> \u0430\u0432\u0442\u043e\u0440\u0430 Shai Mishali \u0441 \u0441\u0430\u0439\u0442\u0430 <a href=\"http:\/\/raywenderlich.com\" rel=\"nofollow\">raywenderlich.com<\/a>.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e RxSwift \u043a\u043e\u043d\u0446\u0435\u043f\u0442\u0443\u0430\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 &quot;\u043e\u0431\u044b\u0447\u043d\u044b\u043c \u043f\u0443\u0442\u0435\u043c&quot;. \u041e\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043c \u0441\u043c\u044b\u0441\u043b\u0435, \u0447\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0432 \u0432\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043e\u0431\u044b\u0447\u043d\u043e \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u0447\u0435\u0442\u043a\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0432\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b <em>\u043f\u043e\u0442\u043e\u043a\u043e\u043c<\/em> \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043d\u0430 \u043e\u0441\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u043c \u0432 RxSwift \u043a\u0430\u043a <code>Observable<\/code>. \u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u0434\u0430\u0441\u0442 \u0432\u0430\u043c \u043a\u043b\u044e\u0447 \u043a \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e RxSwift \u043a\u043e\u0434\u0430.<\/p>\n<p>  <\/p>\n<p><code>Observable<\/code> \u2014 \u043c\u043e\u0449\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u0430\u043c, \u043a\u0430\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443, \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u0431\u044b\u0442\u044c \u0443\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u043c \u0447\u0442\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0433\u0434\u0430 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e. \u0412\u043c\u0435\u0441\u0442\u0435 \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430\u043c\u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044d\u0442\u043e \u0434\u0430\u0435\u0442, \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 <code>Observable<\/code> \u2014 \u043d\u0435 \u0442\u0430\u043a\u0430\u044f \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430, \u043a\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 XCTAssert \u0434\u043b\u044f \u043e\u0431\u044b\u0447\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439. \u041d\u043e \u043d\u0435 \u043f\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0439\u0442\u0435 \u2014 \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u0441\u0442\u0430\u0432\u0438\u0442 \u0432\u0430\u0441 \u043d\u0430 \u043f\u0443\u0442\u044c \u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0442\u043e\u043c \u0432 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 RxSwift!<\/p>\n<p>  <\/p>\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u0443\u0447\u0438\u0442 \u0432\u0430\u0441 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c unit \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f <code>Observable<\/code> \u043f\u043e\u0442\u043e\u043a\u043e\u0432. \u0412\u044b \u043d\u0430\u0443\u0447\u0438\u0442\u0435\u0441\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u0442\u0435\u0445\u043d\u0438\u043a\u0430\u043c \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f RxSwift \u043a\u043e\u0434\u0430, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0441\u043e\u0432\u0435\u0442\u0430\u043c. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0447\u043d\u0435\u043c.<\/p>\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-303944","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/303944","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=303944"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/303944\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=303944"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=303944"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=303944"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}