How to use datataskPublisher Combine Framework

In this tutorial you will learn about making network call in iOS SwiftUI using Combine Framework & dataTaskPublisher.

Combine Framework is mainly used for managing Publisher & Subscriber. Let me explain in easy way – Subscriber who listens things which Publisher publishes. In This Tutorial AnyPublisher act as Publisher & Cancellable with .sink act as Subscriber. Note: Pub & Sub are protocols
We will be using the Github GET URL "https://api.github.com/users/janeshsutharios/repos"

Code for datataskPublisher implementation
import Foundation
import Combine

// MARK: Model for JSON Data -- For Converting Network Data object into swift readable data object
struct GithubEntity: Codable {
    var id: Int?
    var nodeID, name, fullName: String?
}

// MARK: Custom Error enum
enum WebServiceError: Error, LocalizedError {
    case unknown, customError(reason: String)
    
    var errorDescription: String? {
        switch self {
        case .unknown:
            return "---Unknown error----"
        case .customError(let reason):
            return reason
        }
    }
}

struct CombileNetworkHelper {
    
    static func fetchFromWebService() -> AnyPublisher<[GithubEntity], WebServiceError> {
        // 1: GET Service URL
        let url = URL(string: "https://api.github.com/users/janeshsutharios/repos")!
        
        let urlRequest = URLRequest(url: url)
        
        // 2: Added Publisher
        var dataPublisher: AnyPublisher<[GithubEntity], WebServiceError>
        
        // 3: DataTaskPublisher to fetch stream values
        dataPublisher = URLSession.DataTaskPublisher(request: urlRequest, session: .shared)
        
        // 4: tryMap for Creating a closure to map elements with Publisher
            .tryMap { data, response in
                guard let httpResponse = response as? HTTPURLResponse, 200..<300 ~= httpResponse.statusCode else {
                    throw WebServiceError.unknown
                }
                return data
            }
        // 5: Convert Response to Codable mode
            .decode(type: [GithubEntity].self, decoder: JSONDecoder())
        // 6: After Recieve data jump to Main Thread so it will be thread safe for UI Activities
            .receive(on: RunLoop.main)
        // 7: mapError is used to map error of custom type with closure
            .mapError { error in
                if let error = error as? WebServiceError {
                    return error
                } else {
                    return WebServiceError.customError(reason: error.localizedDescription)
                }
            }
        // 8:eraseToAnyPublisher is used to expose an instance of AnyPublisher to the downstream subscriber, rather than this publisher’s actual type
            .eraseToAnyPublisher()
        return dataPublisher
    }
}

So firstly we created Codable model for Git URL & we have created custom error block as well

Code Explanation:

We have DataTaskPublisher with shared session
.tryMap is used for Creating a closure to map elements with Publisher, here we are transforming data.
.decode(.. Mapping the response to codable type i.e. GithubEntity
.receive(on: After Recieve data jump to Main Thread so it will be thread safe for UI Activities < Just like DisatchQueue.main.async>
.mapError is used to map error of custom type with closure in our case it’s enum WebServiceError
.eraseToAnyPublisher is used to expose an instance of AnyPublisher to the downstream subscriber, rather than this publisher’s actual type So here we are mapping to [GithubEntity], WebServiceError

ContentView: where we are invoking network call –

Here we will use .sink subscriber which listen values from the Publisher AnyPublisher<[GithubEntity], WebServiceError>
On .sink(.. we have two parameters one is completion & another is receive value which is closure type


struct ContentView: View {
    
    @State var apiDataSubscriber: Cancellable? = nil
    
    func hitWebService() {
        apiDataSubscriber = CombileNetworkHelper.fetchFromWebService().sink(receiveCompletion: { completion in
            switch completion {
            case .finished:
                break
            case .failure(let error):
                print(error.localizedDescription)
            }
        }, receiveValue: { response in
            print("\n Response Recieved-->", response)
        })
    }
    
    var body: some View {
        Button(
            action: { hitWebService() },
            label: { Text("Click Me").font(.subheadline) }
        )
    }
}

Now launch the application & click the button the response will get printed. 🙂
The Xcode project file can be download from here
Hope the article was easy to understand.. Thanks.
That’s all 🙂

Leave a Comment

Your email address will not be published. Required fields are marked *