{"id":9227,"date":"2022-03-05T20:35:36","date_gmt":"2022-03-05T20:35:36","guid":{"rendered":"http:\/\/putridparrot.com\/blog\/?p=9227"},"modified":"2022-03-05T20:35:36","modified_gmt":"2022-03-05T20:35:36","slug":"urlsession-shared-datatask-and-http-errors-such-as-404","status":"publish","type":"post","link":"https:\/\/putridparrot.com\/blog\/urlsession-shared-datatask-and-http-errors-such-as-404\/","title":{"rendered":"URLSession.shared.dataTask and HTTP errors such as 404"},"content":{"rendered":"<p>The <em>URLSession.shared.dataTask<\/em> completionHandler passes us three pieces of information. Data, the response from an HTTP request and an error. <\/p>\n<p>Transport errors are returned within the error object but HTTP status codes which we&#8217;d possible class as errors are supplied in the response object, so I wanted both transport and HTTP status errors to be returned as an Error.<\/p>\n<p><em>Note: I&#8217;m not using URLSession.shared.data as it&#8217;s not currently supported on Linux, from what I can tell.<\/em><\/p>\n<p>The code below creates a new dataTask method that is async and returns a <em>Result&lt;Data?, Error&gt;<\/em>. Let&#8217;s first look at new dataTask method without the status code handling<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nprivate func dataTask(with request: URLRequest) async -&gt; Result&lt;Data?, Error&gt; {\r\n   await withCheckedContinuation { continuation in\r\n      URLSession.shared.dataTask(with: request) { data, response, error in\r\n         if let error = error {\r\n            continuation.resume(returning: .failure(error))\r\n            return\r\n         }\r\n         continuation.resume(returning: .success(data))\r\n    }.resume()\r\n}\r\n<\/pre>\n<p>So this creates a continuation which is used as a promise\/future\/task by calling the continuation <em>resume<\/em> method with either a success or failure.<\/p>\n<p>As already mentioned, this will not return 404&#8217;s (for example) as errors. So let&#8217;s simply add code to create a failure when certain HTTP status codes appear. We might want to differentiate between the two types of error, i.e. transport layer and HTTP, so first off let&#8217;s create our Error subclass, which looks like this<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic enum HTTPError: Error {\r\n    case transportError(Error)\r\n    case httpError(Int)\r\n}\r\n<\/pre>\n<p>This will allow us to pass back the Error from the <em>URLSession.shared.dataTask<\/em> completion handler for transport errors and the code from the HTTP status for HTTP errors.<\/p>\n<p>Now we&#8217;ll change the earlier code to look like this<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nprivate func dataTask(with request: URLRequest) async -&gt; Result&lt;Data?, Error&gt; {\r\n   await withCheckedContinuation { continuation in\r\n      URLSession.shared.dataTask(with: request) { data, response, error in\r\n         if let error = error {\r\n            continuation.resume(returning: .failure(HTTPError.transportError(error)))\r\n            return\r\n         }\r\n\r\n         let resp = response as! HTTPURLResponse\r\n         let status = resp.statusCode\r\n         guard (200...299).contains(status) else {\r\n            continuation.resume(returning: .failure(HTTPError.httpError(status)))\r\n            return\r\n         }\r\n\r\n         continuation.resume(returning: .success(data))\r\n    }.resume()\r\n}\r\n<\/pre>\n<p>Now we can check the <em>Result&lt;Data?, Error&gt;<\/em> for the error and see what type of error it is, for the example below I actually throw the error for the caller to catch<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nlet result = await dataTask(url: url!, httpMethod: &quot;POST&quot;, httpBody: httpBody)\r\nif case .failure(let error) = result  {\r\n   throw error            \r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>The URLSession.shared.dataTask completionHandler passes us three pieces of information. Data, the response from an HTTP request and an error. Transport errors are returned within the error object but HTTP status codes which we&#8217;d possible class as errors are supplied in the response object, so I wanted both transport and HTTP status errors to be returned [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[286],"tags":[],"class_list":["post-9227","post","type-post","status-publish","format-standard","hentry","category-swift"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/9227","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/comments?post=9227"}],"version-history":[{"count":4,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/9227\/revisions"}],"predecessor-version":[{"id":9231,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/9227\/revisions\/9231"}],"wp:attachment":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/media?parent=9227"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/categories?post=9227"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/tags?post=9227"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}