Reputation: 111
I just started using combine swift to handle the data request and response.
The Json data returned is a nested data which I will only need one part of it. such as:
{
"page": 1,
"data": [
{
"id": 1,
"title": "news-1",
"content": "content 1"
},
{
"id": 2,
"title": "news-2",
"content": "content 2"
},
{
"id": 3,
"title": "news-3",
"content": "content 3"
}
],
"time": 202021313,
"description" :"xxxx"
}
I will need to use the data
array.
Fetch functions below:
func fetchData() throws -> URLSession.DataTaskPublisher {
let headers = [
"Content-Type": "application/json",
"cache-control": "no-cache",
]
guard let url = URL(string: endpointStr ) else {
throw APIError.invalidEndpoint
}
var request = URLRequest(url: url,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
return session.dataTaskPublisher(for: request)
}
let publisher = try? fetchData()
let decoder = JSONDecoder()
let cancellable = publisher?
.receive(on: DispatchQueue.main)
.map {
$0.data
}
.decode(type: DataModel.self, decoder: decoder)
.sink(receiveCompletion: { (completion) in
switch completion {
case .failure(let error):
print("Error:")
print(error)
case .finished:
print("DONE - get Publisher")
}
}, receiveValue: { data in
print(data.title)
})
The data it returned is the complete json data, is there any elegant way to get only the array of data and convert into an array of [DataModel] and handle the data in receiveValue.
I have tried to edit map with no luck:
.map {
if let dataString = String(data: $0.data, encoding: .utf8) {
let dataDic = APIService.convertToDictionary(text: dataString)
if let DataArray = dataDic?["data"] {
return listDataDic!
}
return $0.data
}
Upvotes: 1
Views: 1733
Reputation: 1505
Please clarify if i've misunderstood the question but what if you use another model to decode your [DataModel]
and then map to the decoded [DataModel]
array?
Here is a unit test example. Response
is the new model that decodes the [DataModel]
array into something you can work with.
import XCTest
import Combine
let data = """
{
"page": 1,
"data": [
{
"id": 1,
"title": "news-1",
"content": "content 1"
},
{
"id": 2,
"title": "news-2",
"content": "content 2"
},
{
"id": 3,
"title": "news-3",
"content": "content 3"
}
],
"time": 202021313,
"description": "xxxx"
}
""".data(using: .utf8)!
class Response: Codable {
var data: [DataModel]
}
class DataModel: Codable {
let id: Int
let title: String
let content: String
}
class Test: XCTestCase {
func testDecodeDataModel() {
let e = expectation(description: "finished expectation")
let decoder = JSONDecoder()
let cancellable = Just(data)
.decode(type: Response.self, decoder: decoder)
.map { $0.data }
.sink(receiveCompletion: { (completion) in
// handle completion..
}, receiveValue: { dataArray in
print(dataArray.count) // here you can work with your [DataModel] array
e.fulfill()
})
wait(for: [e], timeout: 1)
}
}
Upvotes: 4