{"id":2624,"date":"2018-04-14T11:49:12","date_gmt":"2018-04-14T07:49:12","guid":{"rendered":"https:\/\/blog.alwawee.ru\/?p=2624"},"modified":"2018-04-14T12:01:43","modified_gmt":"2018-04-14T08:01:43","slug":"asynchronous-requests-chaining-using-flatmap-operator-in-rxswift","status":"publish","type":"post","link":"https:\/\/blog.alwawee.ru\/?p=2624","title":{"rendered":"Asynchronous requests chaining using FlatMap operator in RxSwift"},"content":{"rendered":"<p>One of the main features of using RxSwift and RxCocoa or FRP is the possibility to chain several asynchronous requests and avoid callback hell. It can be achieved by using FlatMap operator and creating your own Observables from your asynchronous jobs like network requests or heavy calculations. But sometimes it is not simple to understand, how to do it. In this post I would like to show an example from one of my projects. The same can be applied to Objective C + ReactiveCocoa, the idea is the same. <\/p>\n<p>First of all we should create Observables from different asynchronous requests. Then we can chain them using FlatMap operator. My example concerns a classic case &#8211; login and profile fetching. In many apps after logging in a user you have to request his profile in a separate request. <\/p>\n<p>Usually you use MVVM with RxSwift. In a ViewModel you can create observables. In my case login() method creates an Observable from Alamofire request. In loadProfile() method Observable is created from ProfileService. <\/p>\n<pre>\r\n\/\/\/ Logins user and saves credentials\r\n\/\/\/\r\n\/\/\/ - Returns: true if login was successfull; error in other cases\r\nfunc login() -> Observable<Bool> {\r\n    \r\n    return Observable<Bool>.create { (observer) -> Disposable in\r\n        \r\n        let params = [\"username\": self.nameText.value,\r\n                      \"password\": self.passwordText.value]\r\n        \r\n        self.inProgress.value = true\r\n        \r\n        Alamofire.request(APPURL.Login,\r\n                          method: .post,\r\n                          parameters: params)\r\n            .responseJSON(completionHandler: { (dataResponse) in\r\n                \r\n                self.inProgress.value = false\r\n                \r\n                switch dataResponse.result {\r\n                case .success(let value):\r\n                    \r\n                    guard let jsonDictionary = value as? Dictionary<String, Any> else {\r\n                        let userInfo: [String : Any] =\r\n                            [NSLocalizedDescriptionKey : \"Unable to login with the provided credentials\"]\r\n                        let error = NSError(domain:\"\", code:0, userInfo:userInfo)\r\n                        return observer.onError(error)\r\n                    }\r\n                    \r\n                    guard let authToken = jsonDictionary[\"auth_token\"] as? String else {\r\n                        let userInfo: [String : Any] =\r\n                            [NSLocalizedDescriptionKey : \"Unable to login with the provided credentials\"]\r\n                        let error = NSError(domain:\"\", code:0, userInfo:userInfo)\r\n                        return observer.onError(error)\r\n                    }\r\n                    \r\n                    self.saveAuthToken(authToken)\r\n                    \r\n                    observer.onNext(true)\r\n                    observer.onCompleted()\r\n                    \r\n                case .failure(let error):\r\n                    \r\n                    print(\"Could not login due to \\(error)\")\r\n                    observer.onError(error)\r\n                    \r\n                }\r\n            })\r\n        \r\n        return Disposables.create()\r\n    }\r\n}\r\n\r\n\/\/\/ Loads profile\r\n\/\/\/\r\n\/\/\/ - Returns: profile object if success, otherwise error\r\nfunc loadProfile() -> Observable<Profile> {\r\n\r\n    return Observable<Profile>.create({ (observer) -> Disposable in\r\n        \r\n        self.inProgress.value = true\r\n        \r\n        ProfileService.shared.getProfile {\r\n            \r\n            self.inProgress.value = false\r\n            \r\n            guard let profile = ProfileService.shared.profile else {\r\n                let userInfo: [String : Any] =\r\n                    [NSLocalizedDescriptionKey : \"Unable to load profile\"]\r\n                let error = NSError(domain:\"\", code:0, userInfo:userInfo)\r\n                return observer.onError(error)\r\n            }\r\n            \r\n            observer.onNext(profile)\r\n            observer.onCompleted()\r\n        }\r\n        \r\n        return Disposables.create()\r\n    })\r\n}\r\n<\/pre>\n<p>This is how you chain login and loadProfile requests. When they finish, you can do some job with a result profile. In this case I check a trial period and navigate to one of the views depending on a result. <\/p>\n<pre>\r\n@IBAction func signMeInAction(_ sender: AnyObject) {\r\n    self.viewModel.login()\r\n    .flatMap {result in\r\n        self.viewModel.loadProfile()\r\n    }\r\n    .subscribe(onNext: { profile in\r\n        let trialNotExpired = self.viewModel.checkTrialNotExpired(for: profile)\r\n        if trialNotExpired {\r\n            let firstAppLaunch = self.viewModel.checkFirstAppLaunch()\r\n            if firstAppLaunch {\r\n                self.performSegue(withIdentifier: Segue.FromLoginToInstruction, sender: nil)\r\n            }\r\n            else {\r\n                self.navigationController?.popToRootViewController(animated: true)\r\n            }\r\n        }\r\n        else {\r\n            self.showTrialExpirePopUp()\r\n        }\r\n    },\r\n    onError: { error in\r\n        SCLAlertView().showError(\"Error\", subTitle:error.localizedDescription)\r\n    })\r\n    .disposed(by: disposeBag)\r\n}\r\n<\/pre>\n<p>Note that error for both requests is handled in one place using SCLAlertView. FlatMap operator maps a result of a first request to a second request and also flattens Observables. If you use just Map instead of FlatMap, the result of chaining will be an Observable of Observables. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the main features of using RxSwift and RxCocoa or FRP is the possibility to chain several asynchronous requests and avoid callback hell. It can be achieved by using FlatMap operator and creating your own Observables from your asynchronous jobs like network requests or heavy calculations. But sometimes it is not simple to understand, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[204,203,202,199],"class_list":["post-2624","post","type-post","status-publish","format-standard","hentry","category-ios","tag-flatmap","tag-frp","tag-rxswift","tag-swift"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Asynchronous requests chaining using FlatMap operator in RxSwift - Denis Kutlubaev<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.alwawee.ru\/?p=2624\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Asynchronous requests chaining using FlatMap operator in RxSwift - Denis Kutlubaev\" \/>\n<meta property=\"og:description\" content=\"One of the main features of using RxSwift and RxCocoa or FRP is the possibility to chain several asynchronous requests and avoid callback hell. It can be achieved by using FlatMap operator and creating your own Observables from your asynchronous jobs like network requests or heavy calculations. But sometimes it is not simple to understand, [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.alwawee.ru\/?p=2624\" \/>\n<meta property=\"og:site_name\" content=\"Denis Kutlubaev\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/alwaweemobile\" \/>\n<meta property=\"article:published_time\" content=\"2018-04-14T07:49:12+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-04-14T08:01:43+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/blog.alwawee.com\/?attachment_id=1982\" \/>\n<meta name=\"author\" content=\"Denis Kutlubaev\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Denis Kutlubaev\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/blog.alwawee.ru\\\/?p=2624#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.alwawee.ru\\\/?p=2624\"},\"author\":{\"name\":\"Denis Kutlubaev\",\"@id\":\"https:\\\/\\\/blog.alwawee.ru\\\/#\\\/schema\\\/person\\\/58f1f1f57155b48afab83ba53b3e26a7\"},\"headline\":\"Asynchronous requests chaining using FlatMap operator in RxSwift\",\"datePublished\":\"2018-04-14T07:49:12+00:00\",\"dateModified\":\"2018-04-14T08:01:43+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blog.alwawee.ru\\\/?p=2624\"},\"wordCount\":264,\"commentCount\":0,\"keywords\":[\"FlatMap\",\"FRP\",\"RxSwift\",\"Swift\"],\"articleSection\":[\"iOS\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/blog.alwawee.ru\\\/?p=2624#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blog.alwawee.ru\\\/?p=2624\",\"url\":\"https:\\\/\\\/blog.alwawee.ru\\\/?p=2624\",\"name\":\"Asynchronous requests chaining using FlatMap operator in RxSwift - Denis Kutlubaev\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.alwawee.ru\\\/#website\"},\"datePublished\":\"2018-04-14T07:49:12+00:00\",\"dateModified\":\"2018-04-14T08:01:43+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/blog.alwawee.ru\\\/#\\\/schema\\\/person\\\/58f1f1f57155b48afab83ba53b3e26a7\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blog.alwawee.ru\\\/?p=2624#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/blog.alwawee.ru\\\/?p=2624\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/blog.alwawee.ru\\\/?p=2624#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/blog.alwawee.ru\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Asynchronous requests chaining using FlatMap operator in RxSwift\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/blog.alwawee.ru\\\/#website\",\"url\":\"https:\\\/\\\/blog.alwawee.ru\\\/\",\"name\":\"Denis Kutlubaev\",\"description\":\"iOS Development\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/blog.alwawee.ru\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/blog.alwawee.ru\\\/#\\\/schema\\\/person\\\/58f1f1f57155b48afab83ba53b3e26a7\",\"name\":\"Denis Kutlubaev\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/346de75d27c818be0aa7fddeada2e50e30cc15b4da6b3b8110c5d766cd2afa34?s=96&d=mm&r=pg\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/346de75d27c818be0aa7fddeada2e50e30cc15b4da6b3b8110c5d766cd2afa34?s=96&d=mm&r=pg\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/346de75d27c818be0aa7fddeada2e50e30cc15b4da6b3b8110c5d766cd2afa34?s=96&d=mm&r=pg\",\"caption\":\"Denis Kutlubaev\"},\"description\":\"iOS Developer, creator of Tornado Browser and many other popular apps\",\"sameAs\":[\"http:\\\/\\\/dennis.alwawee.com\"],\"url\":\"https:\\\/\\\/blog.alwawee.ru\\\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Asynchronous requests chaining using FlatMap operator in RxSwift - Denis Kutlubaev","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.alwawee.ru\/?p=2624","og_locale":"en_US","og_type":"article","og_title":"Asynchronous requests chaining using FlatMap operator in RxSwift - Denis Kutlubaev","og_description":"One of the main features of using RxSwift and RxCocoa or FRP is the possibility to chain several asynchronous requests and avoid callback hell. It can be achieved by using FlatMap operator and creating your own Observables from your asynchronous jobs like network requests or heavy calculations. But sometimes it is not simple to understand, [&hellip;]","og_url":"https:\/\/blog.alwawee.ru\/?p=2624","og_site_name":"Denis Kutlubaev","article_publisher":"https:\/\/www.facebook.com\/alwaweemobile","article_published_time":"2018-04-14T07:49:12+00:00","article_modified_time":"2018-04-14T08:01:43+00:00","og_image":[{"url":"http:\/\/blog.alwawee.com\/?attachment_id=1982","type":"","width":"","height":""}],"author":"Denis Kutlubaev","twitter_misc":{"Written by":"Denis Kutlubaev","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blog.alwawee.ru\/?p=2624#article","isPartOf":{"@id":"https:\/\/blog.alwawee.ru\/?p=2624"},"author":{"name":"Denis Kutlubaev","@id":"https:\/\/blog.alwawee.ru\/#\/schema\/person\/58f1f1f57155b48afab83ba53b3e26a7"},"headline":"Asynchronous requests chaining using FlatMap operator in RxSwift","datePublished":"2018-04-14T07:49:12+00:00","dateModified":"2018-04-14T08:01:43+00:00","mainEntityOfPage":{"@id":"https:\/\/blog.alwawee.ru\/?p=2624"},"wordCount":264,"commentCount":0,"keywords":["FlatMap","FRP","RxSwift","Swift"],"articleSection":["iOS"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/blog.alwawee.ru\/?p=2624#respond"]}]},{"@type":"WebPage","@id":"https:\/\/blog.alwawee.ru\/?p=2624","url":"https:\/\/blog.alwawee.ru\/?p=2624","name":"Asynchronous requests chaining using FlatMap operator in RxSwift - Denis Kutlubaev","isPartOf":{"@id":"https:\/\/blog.alwawee.ru\/#website"},"datePublished":"2018-04-14T07:49:12+00:00","dateModified":"2018-04-14T08:01:43+00:00","author":{"@id":"https:\/\/blog.alwawee.ru\/#\/schema\/person\/58f1f1f57155b48afab83ba53b3e26a7"},"breadcrumb":{"@id":"https:\/\/blog.alwawee.ru\/?p=2624#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.alwawee.ru\/?p=2624"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blog.alwawee.ru\/?p=2624#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.alwawee.ru\/"},{"@type":"ListItem","position":2,"name":"Asynchronous requests chaining using FlatMap operator in RxSwift"}]},{"@type":"WebSite","@id":"https:\/\/blog.alwawee.ru\/#website","url":"https:\/\/blog.alwawee.ru\/","name":"Denis Kutlubaev","description":"iOS Development","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.alwawee.ru\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blog.alwawee.ru\/#\/schema\/person\/58f1f1f57155b48afab83ba53b3e26a7","name":"Denis Kutlubaev","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/346de75d27c818be0aa7fddeada2e50e30cc15b4da6b3b8110c5d766cd2afa34?s=96&d=mm&r=pg","url":"https:\/\/secure.gravatar.com\/avatar\/346de75d27c818be0aa7fddeada2e50e30cc15b4da6b3b8110c5d766cd2afa34?s=96&d=mm&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/346de75d27c818be0aa7fddeada2e50e30cc15b4da6b3b8110c5d766cd2afa34?s=96&d=mm&r=pg","caption":"Denis Kutlubaev"},"description":"iOS Developer, creator of Tornado Browser and many other popular apps","sameAs":["http:\/\/dennis.alwawee.com"],"url":"https:\/\/blog.alwawee.ru\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/blog.alwawee.ru\/index.php?rest_route=\/wp\/v2\/posts\/2624","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.alwawee.ru\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.alwawee.ru\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.alwawee.ru\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.alwawee.ru\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2624"}],"version-history":[{"count":5,"href":"https:\/\/blog.alwawee.ru\/index.php?rest_route=\/wp\/v2\/posts\/2624\/revisions"}],"predecessor-version":[{"id":2629,"href":"https:\/\/blog.alwawee.ru\/index.php?rest_route=\/wp\/v2\/posts\/2624\/revisions\/2629"}],"wp:attachment":[{"href":"https:\/\/blog.alwawee.ru\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2624"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.alwawee.ru\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2624"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.alwawee.ru\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2624"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}