Download Json from using @escaping or Combine in SwiftUI

Updated:

Download Json from using @escaping or Combine in SwiftUI

Downland Json with escaping

This part is going to use background threads, escaping clousre and codable protocol to convert an web data into data that we can use in our App

It’s going to use a free public API to download some just fake test data into the App. Downloading data from the internet into your App, It’s very important and used all the time

Free fake API for testing : https://jsonplaceholder.typicode.com/


import SwiftUI
// MARK: -  MODEL
struct PostModel: Identifiable, Codable {
let userId: Int
let id: Int
let title: String
let body: String
}

// MARK: -  VIEWMODEL
class DownloadWithEscapingViewModel: ObservableObject {
// MARK: -  PROPERTY
@Published var posts: [PostModel] = []
// MARK: -  INIT
init() {
getPost()
}
// MARK: -  FUNCTION
func getPost() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }

downloadData(fromURL: url) { returnedData in
  if let data = returnedData {
    guard let newPosts = try? JSONDecoder().decode([PostModel].self, from: data) else { return }
    // print(newPosts)
    // run main thread with weak self
    DispatchQueue.main.async { [weak self] in
      self?.posts = newPosts
    }
  } else {
    print("No Data returned")
  }
}
}

func downloadData(fromURL url: URL, completionHandler: @escaping (_ data: Data?) -> ()) {
URLSession.shared.dataTask(with: url) { data, response, error in
  guard
    let data = data,
    error == nil,
    let response = response as? HTTPURLResponse,
    response.statusCode >= 200 && response.statusCode < 300 else {
      print("Error downloading data")
      completionHandler(nil)
      return
    }
  completionHandler(data)
}.resume() // it's resumed because you can pause or suspend a task an then resume it again

}
}

// MARK: -  VIEW
struct DownloadWithEscapingBootCamp: View {
// MARK: -  PROPERTY
@StateObject var vm = DownloadWithEscapingViewModel()
// MARK: -  BODY
var body: some View {
List {
ForEach(vm.posts) { post in
  VStack (alignment: .leading){
    Text(post.title)
      .font(.headline)

    Text(post.body)
      .foregroundColor(.gray)
  } //: VSTACK
  .frame(maxWidth: .infinity, alignment: .leading)
} //: LOOP
} //: LIST
.listStyle(.plain)
}
}

스크린샷

Downland Json with Combine

This is going to same thing that we did in the above download json by using @escaping. It’s going to write the code a little differenlty to use the combine framework

Combine is new framework from apple that take advantage of using publishers and subscribers.

@escaping way is only options with Download Json from the Internet until iOS 12. If you building an app that is going to be campatible with older iOS version before iOS 13, Only way to use @escaping.

But from iOS 13, Apple release Combine framework is probably the way to go because as you’ll see it is very efficient and code is pretty sleek

Combine from Apple Developer : https://developer.apple.com/documentation/combine

  • Publisher : It is basically an object that is going to deliver us some values over time.

  • Subscruber : Evety time the publisher produces more data and it publishes a new piece of data we can use that in the app

  • Compare to order stuff from online with delivery service and Combine Process logic (both are feels like similar process)

    • Order stuff
    1. sign up for monthly subcription fo package to be delivered
    2. the company would make the package behind the scene
    3. receive the package at your front door
    4. make sure the box isn’t damaged
    5. open and make sure the item is correct
    6. use the itme!!
    7. cancellable at any time!!
    • Combine Process logic
    1. Create the publisher
    2. subscrube publisher on background thread
    3. recieve on main thread
    4. tryMap (check that the data is good)
    5. decode (decode data into PostModels)
    6. sink (put the item into our app)
    7. store (cancel subscription if needed)
import SwiftUI
import Combine

// MARK: -  MODEL
struct PostModel: Identifiable, Codable {
	let userId: Int
	let id: Int
	let title: String
	let body: String
}

// MARK: -  VIEWMODEL
class DownloadWithCombineViewModel: ObservableObject {
// MARK: -  PROPERTY
@Published var posts: [PostModel] = []
var cancellables = Set<AnyCancellable>()
// MARK: -  INIT
init() {
getPosts()
}
// MARK: -  FUNCTION
func getPosts() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }

// 1. Create the publisher
URLSession.shared.dataTaskPublisher(for: url)
// 2. subscrube publisher on background thread
.subscribe(on: DispatchQueue.global(qos: .background)) // acutally don't need to subscibe to work background thread cause it automatically gois on the background thread
// 3. recieve on main thread
.receive(on: DispatchQueue.main)
// 4. tryMap (check that the data is good)
/*
.tryMap { (data, response) -> Data in
guard let response = response as? HTTPURLResponse,
      response.statusCode >= 200 && response.statusCode < 300 else {
        throw URLError(.badServerResponse)
      }
return data
}
*/
.tryMap(handleOutput)
// 5. decode (decode data into PostModels)
.decode(type: [PostModel].self, decoder: JSONDecoder())
// if there was an error in getting these post models and failing completion make default one
.replaceError(with: [])
// 6. sink (put the item into our app)
.sink(receiveValue: { [weak self] returnedPosts in
self?.posts = returnedPosts
})
// 7. store (cancel subscription if needed)
.store(in: &cancellables)
}

func handleOutput(output: URLSession.DataTaskPublisher.Output) throws -> Data {
guard let response = output.response as? HTTPURLResponse,
  response.statusCode >= 200 && response.statusCode < 300 else {
    throw URLError(.badServerResponse)
  }
return output.data
}
}

struct DowanloadWithCombine: View {
// MARK: -  PROPERTY
@StateObject var vm = DownloadWithCombineViewModel()
// MARK: -  BODY
var body: some View {
List{
ForEach(vm.posts) { post in
  VStack(alignment: .leading) {
    Text(post.title)
      .font(.headline)
    Text(post.body)
      .foregroundColor(.gray)
  } //: VSTACK
  .frame(maxWidth: .infinity, alignment: .leading)
} //: LOOP
} //: LIST
.listStyle(.plain)
}
}

스크린샷


🗃 Reference

SwiftUI Thinking - https://www.youtube.com/watch?v=fdxFp5vU6MQ&list=PLwvDm4VfkdpiagxAXCT33Rkwnc5IVhTar&index=24

Categories:

Updated:

Leave a comment