Qua bài này, các bạn sẽ nắm được:
- Khi nào cần dùng URLSession
- URLSession là gì
- Fetch data dùng URLSession qua ví dụ đơn giản
Khi nào cần dùng URLSession
1 2 |
Rất đơn giản<span class="token punctuation">,</span> bạn muốn <span class="token string">"chiếc"</span> app của bạn tương tác với Internet<span class="token punctuation">,</span> nghĩa là bạn đang muốn tạo HTTP request<span class="token punctuation">,</span> URLSession là <span class="token number">1</span> lựa chọn |
URLSession là 1 framework được chính Apple phát triển và implement sẵn trong iOS. Rất nhiều developer dùng thư viện từ bên thứ 3 như Alamofire nhưng bạn sẽ sớm nhận ra rằng, bạn không cần thiết quá phụ thuộc vào 1 thư viện để tạo 1 HTTP request đơn giản, URLSession sẽ có tất cả những thứ mà bạn muốn.
URLSession là gì
URLSession là một class cung cấp API để tương tác với các giao thức như HTTP, HTTPS
- URLSession tạo ra một session , hiểu đơn giản nó giống như 1 tab đang mở trong trình duyệt của bạn, nó chứa rất nhiều các phương thức HTTP request mà người dùng có thể tương tác
- URLsession được sử dụng để tạo ra đối tượng URLSessionTask, thứ mà bạn có thể lấy dữ liệu từ Internet, tải xuống hay upload file lên webservices. Có 3 loại Task:
- URLSessionDataTask: Sử dụng tác vụ này cho các yêu cầu HTTP GET để truy xuất dữ liệu từ máy chủ vào bộ nhớ.
- URLSessionUploadTask: Sử dụng tác vụ này để tải tệp từ đĩa lên dịch vụ web, thường thông qua phương thức HTTP POST hoặc PUT.
- URLSessionDownloadTask: Sử dụng tác vụ này để tải tệp từ dịch vụ từ xa đến vị trí tệp tạm thời. Bạn cũng có thể tạm dừng, tiếp tục và hủy bỏ task. URLSessionDownloadTask có khả năng tạm dừng bổ sung để nối lại trong tương lai.
- Bạn có thể cấu hình lại một session với URLSesssionConfiguration để phục vụ cho từng mục đích
- .default: Tạo một đối tượng cấu hình mặc định sử dụng các đối tượng lưu trữ bộ đệm, thông tin xác thực và bộ nhớ cache .
- .ephemeral: Tương tự như cấu hình mặc định, ngoại trừ tất cả dữ liệu liên quan đến phiên được lưu trữ trong bộ nhớ. Hãy nghĩ về điều này như một phiên “riêng tư”
- .background: Cho phép phiên thực hiện các tác vụ tải lên hoặc tải xuống trong nền. Chuyển tiếp tục ngay cả khi ứng dụng bị treo hoặc chấm dứt bởi hệ thống. URLSessionConfiguration cũng cho phép bạn định cấu hình các thuộc tính phiên như giá trị hết thời gian, chính sách bộ đệm và các header HTTP bổ sung. Tham khảo tài liệu để biết danh sách đầy đủ các tùy chọn cấu hình
Fetch data dùng URLSession
Vào phần thực hành luôn nhé, các bước để lấy dữ liệu từ webservice:
- Khởi tạo HTTP Request với URLSession
- Dùng URLSessionDataTask gửi yêu cầu để lấy dữ liệu
- In ra các loại dữ liệu được trả về
- Kiểm tra có đúng dữ liệu ta cần và Convert dữ liệu sang JSON
Khởi tạo HTTP Request với URLSession
Chúng ta cần 1 session và 1 URL như thế này
1 2 3 |
<span class="token keyword">let</span> session <span class="token operator">=</span> <span class="token builtin">URLSession</span><span class="token punctuation">.</span>shared <span class="token comment">//1</span> <span class="token keyword">let</span> url <span class="token operator">=</span> <span class="token function">URL</span><span class="token punctuation">(</span>string<span class="token punctuation">:</span> <span class="token string">"..."</span><span class="token punctuation">)</span><span class="token operator">!</span> <span class="token comment">//2</span> |
- Tạo 1 tham chiếu tới Class URLSession với cấu hình mặc định
- Tạo url với kiểu URL, tham số là string
URL dùng ở VD này là uers.json, copy đường dẫn này và paste vào trong string
Dùng URLSessionDataTask gửi yêu cầu lấy dữ liệu
Tạo 1 dataTask với function dataTask(with:completionHandler:)
:
1 2 3 4 5 |
<span class="token keyword">let</span> task <span class="token operator">=</span> session<span class="token punctuation">.</span><span class="token function">dataTask</span><span class="token punctuation">(</span><span class="token keyword">with</span><span class="token operator">:</span> url<span class="token punctuation">,</span> completionHandler<span class="token operator">:</span> <span class="token punctuation">{</span> data<span class="token punctuation">,</span> response<span class="token punctuation">,</span> error <span class="token keyword">in</span> <span class="token comment">// Do something...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> |
dataTask(with:completionHandler:)
có 2 tham số truyền vào: url được khởi tạo ở trên và 1 completionHandle
1 2 |
completionHandle là gì?<span class="token punctuation">,</span> bạn có thể hiểu đơn giản nó là <span class="token number">1</span> clousure mà bắt lại <span class="token number">3</span> tham số mà bạn muốn dùng lại: <span class="token keyword">data</span><span class="token punctuation">,</span> response và error<span class="token punctuation">.</span> |
- data: có kiểu là Data, là dữ liệu bạn muốn lấy từ webservice,
- response: có kiểu là URLResponse, cho ta biết thêm thông tin về phản hồi của request có thể bao gồm độ dài, encoding, HTTP status code, …
- error: sẽ chứa thông tin lỗi xảy ra nếu request không thành công. Nếu request thành công, error = nil
Đến đây, 1 request vẫn chưa hoàn thành, nó chỉ mới được khởi tạo, để bắt đầu gửi yêu cầu hãy thêm:
1 2 |
task<span class="token punctuation">.</span>resume<span class="token punctuation">(</span><span class="token punctuation">)</span> |
In ra các loại dữ liệu được trả về
Trăm nghe không bằng mắt thấy, bạn hãy in các kiểu dữ liệu bắt trong completionHandle
và xem nó là gì nhé
1 2 3 4 5 6 |
<span class="token keyword">let</span> task <span class="token operator">=</span> session<span class="token punctuation">.</span><span class="token function">dataTask</span><span class="token punctuation">(</span>with<span class="token punctuation">:</span> url<span class="token punctuation">)</span> <span class="token punctuation">{</span> data<span class="token punctuation">,</span> response<span class="token punctuation">,</span> error <span class="token keyword">in</span> <span class="token function">print</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token function">print</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span> <span class="token function">print</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Chúng ta có gì nào?:
- data:
Optional(321 bytes)
, bởi vì data có kiểu là Data - response: có kiểu NSHTTPURLResponse là subclass của URLResponse. Bao gồm
Status Code : 200
(mình sẽ nói thêm ở dưới) và HTTP headers (bạn có thể tham khảo tại đây) - error:
nil
. May quá không có lỗi gì ✌️
Kiểm tra có đúng dữ liệu ta cần
Khi thực hiện một request HTTP, bạn cần xác thực lại ít nhất các điều sau:
- Liệu có xảy ra lỗi nào không? => check error
- HTTP response code có trả về theo đúng mong đợi => check response
- dữ liệu trả về có đúng format? => check data hay convert data sang JSON
Check có lỗi hay không ?
1 2 3 4 5 6 |
<span class="token keyword">if</span> <span class="token builtin">error</span> <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token comment">// OH NO! An error occurred... </span> self<span class="token punctuation">.</span><span class="token function">handleClientError</span><span class="token punctuation">(</span><span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> |
Nếu có lỗi thì gọi 1 hàm để giải quyết lỗi này và thoát. Hoặc bạn có thể tham khảo thêm các cách xử lý: throw an error
Check response có OK ?
1 2 3 4 5 6 |
<span class="token keyword">guard</span> <span class="token keyword">let</span> httpResponse <span class="token operator">=</span> response <span class="token keyword">as</span><span class="token operator">?</span> <span class="token builtin">HTTPURLResponse</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token number">299</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>httpResponse<span class="token punctuation">.</span>statusCode<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">self</span><span class="token punctuation">.</span><span class="token function">handleServerError</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> |
- Ở đây chúng ta xét xem liệu response có đúng kiểu HTTP Response và Status code có nằm trong khoảng 200 – 299 hay không. Nếu không thì sẽ thoát luôn
- Status code là trạng thái của request, VD {404, 400, 401, vvv..} là lỗi, còn nằm trong khoản {200…299} request của bạn thành công.
Check data, convert sang JSON
1 2 3 4 |
<span class="token keyword">if</span> <span class="token keyword">let</span> json <span class="token operator">=</span> <span class="token keyword">try</span><span class="token operator">?</span> JSONSerialization<span class="token punctuation">.</span><span class="token function">jsonObject</span><span class="token punctuation">(</span><span class="token parameter"><span class="token keyword">with</span><span class="token operator">:</span> data<span class="token operator">!</span><span class="token punctuation">,</span> options<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span>json<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Sử dụng optional binding để ép kiểu data sang chuỗi JSON bằng cách dùng function jsonObject(with:options:)
của class JSONSerialization
. Bằng cách đọc từng kí tự trong có trong data và chuyển nó sang chuỗi JSON, giống như bạn đọc sách và câu chuyện đó sẽ xuất hiện trong đầu bạn.
Một cách khác mà apple khuyên bạn nên sử dụng:
1 2 3 4 5 6 7 |
<span class="token keyword">do</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> json <span class="token operator">=</span> <span class="token keyword">try</span> <span class="token builtin">JSONSerialization</span><span class="token punctuation">.</span><span class="token function">jsonObject</span><span class="token punctuation">(</span>with<span class="token punctuation">:</span> data<span class="token operator">!</span><span class="token punctuation">,</span> options<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token function">print</span><span class="token punctuation">(</span>json<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"JSON error: <span class="token interpolation"><span class="token delimiter variable">\(</span>error<span class="token punctuation">.</span>localizedDescription<span class="token delimiter variable">)</span></span>"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Ở đoạn code trên, nếu trong try
bắt được lỗi thì nó sẽ được in ra trong catch
Oke cùng xem kết quả json in ra là gì, có giống với uers.json mà bạn mong đợi không:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<span class="token punctuation">(</span> <span class="token punctuation">{</span> age <span class="token operator">=</span> <span class="token number">5000</span><span class="token punctuation">;</span> <span class="token string">"first_name"</span> <span class="token operator">=</span> <span class="token class-name">Ford</span><span class="token punctuation">;</span> <span class="token string">"last_name"</span> <span class="token operator">=</span> <span class="token class-name">Prefect</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> age <span class="token operator">=</span> <span class="token number">999</span><span class="token punctuation">;</span> <span class="token string">"first_name"</span> <span class="token operator">=</span> <span class="token class-name">Zaphod</span><span class="token punctuation">;</span> <span class="token string">"last_name"</span> <span class="token operator">=</span> <span class="token class-name">Beeblebrox</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> age <span class="token operator">=</span> <span class="token number">42</span><span class="token punctuation">;</span> <span class="token string">"first_name"</span> <span class="token operator">=</span> <span class="token class-name">Arthur</span><span class="token punctuation">;</span> <span class="token string">"last_name"</span> <span class="token operator">=</span> <span class="token class-name">Dent</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> age <span class="token operator">=</span> <span class="token number">1234</span><span class="token punctuation">;</span> <span class="token string">"first_name"</span> <span class="token operator">=</span> <span class="token class-name">Trillian</span><span class="token punctuation">;</span> <span class="token string">"last_name"</span> <span class="token operator">=</span> <span class="token class-name">Astra</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
Tổng kết
Awesome ! dưới đây là toàn bộ đoạn code mà cả buổi nay đã học, qua đó chúng ta biết được
- Muốn tại HTTP Request thì dùng URLSession
- URLSession là framework Apple tích hợp sẵn trong iOS, cung cấp API tương tác với phương thức HTTP, HTTS request
- Cách tạo 1 URLSession
- Cách kiểm tra error, response, data
- Cách convert sang JSON
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<span class="token keyword">let</span> session <span class="token operator">=</span> <span class="token builtin">URLSession</span><span class="token punctuation">.</span>shared <span class="token keyword">let</span> url <span class="token operator">=</span> <span class="token function">URL</span><span class="token punctuation">(</span>string<span class="token punctuation">:</span> <span class="token string">"..."</span><span class="token punctuation">)</span><span class="token operator">!</span> <span class="token keyword">let</span> task <span class="token operator">=</span> session<span class="token punctuation">.</span><span class="token function">dataTask</span><span class="token punctuation">(</span>with<span class="token punctuation">:</span> url<span class="token punctuation">)</span> <span class="token punctuation">{</span> data<span class="token punctuation">,</span> response<span class="token punctuation">,</span> error <span class="token keyword">in</span> <span class="token keyword">if</span> error <span class="token operator">!=</span> <span class="token constant">nil</span> <span class="token operator">||</span> data <span class="token operator">==</span> <span class="token constant">nil</span> <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"Client error!"</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> response <span class="token operator">=</span> response <span class="token keyword">as</span><span class="token operator">?</span> <span class="token builtin">HTTPURLResponse</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token number">299</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span>statusCode<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"Server error!"</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> mime <span class="token operator">=</span> response<span class="token punctuation">.</span>mimeType<span class="token punctuation">,</span> mime <span class="token operator">==</span> <span class="token string">"application/json"</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"Wrong MIME type!"</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token keyword">do</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> json <span class="token operator">=</span> <span class="token keyword">try</span> <span class="token builtin">JSONSerialization</span><span class="token punctuation">.</span><span class="token function">jsonObject</span><span class="token punctuation">(</span>with<span class="token punctuation">:</span> data<span class="token operator">!</span><span class="token punctuation">,</span> options<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token function">print</span><span class="token punctuation">(</span>json<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"JSON error: <span class="token interpolation"><span class="token delimiter variable">\(</span>error<span class="token punctuation">.</span>localizedDescription<span class="token delimiter variable">)</span></span>"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> task<span class="token punctuation">.</span><span class="token function">resume</span><span class="token punctuation">(</span><span class="token punctuation">)</span> |
Tài liệu tham khảo:
https://learnappmaking.com/urlsession-swift-networking-how-to/ https://www.raywenderlich.com/567-urlsession-tutorial-getting-started https://learnappmaking.com/swift-optionals-how-to/#optional-binding