-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Adding Repository and DataSource
- Loading branch information
Showing
7 changed files
with
609 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// | ||
// RemoteDataSource.swift | ||
// MyComics | ||
// | ||
// Created by Xavier Ramos on 3/4/24. | ||
// | ||
|
||
import Foundation | ||
import Combine | ||
|
||
class RemoteDataSource { | ||
|
||
// URLs | ||
|
||
static let searchURL: String = "search/" | ||
static let getCharacterURL: String = "character/" | ||
|
||
// Variables | ||
|
||
private let baseURLString: String | ||
private let session: URLSession | ||
|
||
// MARK: - Methods | ||
|
||
init(baseURL: String = Constants.baseURL, session: URLSession = URLSession.shared) { | ||
|
||
self.baseURLString = baseURL | ||
self.session = session | ||
} | ||
|
||
func searchCharacter(value: String) -> AnyPublisher<ServerBaseArrayResponse<ServerCharacter>, Error> { | ||
|
||
let networkManager = ComicsNetworkManager(baseURL: baseURLString, session: session) | ||
|
||
let urlRequest = getSearchCharacterEndpoint(value: value) | ||
|
||
return networkManager.performRequest(urlRequest: urlRequest) | ||
} | ||
|
||
func getCharacter(id: Int) -> AnyPublisher<ServerBaseResponse<ServerCharacter>, Error> { | ||
|
||
let networkManager = ComicsNetworkManager(baseURL: baseURLString, session: session) | ||
|
||
let urlRequest = getCharacterEndpoint(id: id) | ||
|
||
return networkManager.performRequest(urlRequest: urlRequest) | ||
} | ||
} | ||
|
||
// MARK: - Endpoints | ||
|
||
extension RemoteDataSource { | ||
|
||
func getSearchCharacterEndpoint(value: String) -> URLRequest { | ||
|
||
let endpoint = "\(baseURLString)\(RemoteDataSource.searchURL)" | ||
|
||
var components = URLComponents(string: endpoint) | ||
|
||
let queryItems = [URLQueryItem(name: "api_key", value: Constants.apiKey), | ||
URLQueryItem(name: "query", value: value), | ||
URLQueryItem(name: "format", value: "json"), | ||
URLQueryItem(name: "field_list", value: "id,image,name,aliases,real_name,gender"), | ||
URLQueryItem(name: "resources", value: "character"), | ||
URLQueryItem(name: "limit", value: "100" )] | ||
|
||
components?.queryItems = queryItems | ||
|
||
let urlRequest = URLRequest(url: (components?.url!)!) | ||
|
||
return urlRequest | ||
} | ||
|
||
func getCharacterEndpoint(id: Int) -> URLRequest { | ||
|
||
let endpoint = "\(baseURLString)\(RemoteDataSource.getCharacterURL)4005-\(id)" | ||
|
||
var components = URLComponents(string: endpoint) | ||
|
||
let queryItems = [URLQueryItem(name: "api_key", value: Constants.apiKey), | ||
URLQueryItem(name: "format", value: "json"), | ||
URLQueryItem(name: "field_list", value: "id,image,name,aliases,real_name,birth,deck,gender,origin,powers")] | ||
|
||
components?.queryItems = queryItems | ||
|
||
let urlRequest = URLRequest(url: (components?.url!)!) | ||
|
||
return urlRequest | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
// Repository.swift | ||
// MyComics | ||
// | ||
// Created by Xavier Ramos on 3/4/24. | ||
// | ||
|
||
import Foundation | ||
import Combine | ||
|
||
protocol Repository { | ||
|
||
func searchCharacter(value: String) -> AnyPublisher<[Character], Error> | ||
|
||
func getCharacter(id: Int) -> AnyPublisher<Character, Error> | ||
} |
58 changes: 58 additions & 0 deletions
58
MyComics/MyComics/Repositories/RepositoryImplementation.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// | ||
// RepositoryImplementation.swift | ||
// MyComics | ||
// | ||
// Created by Xavier Ramos on 3/4/24. | ||
// | ||
|
||
import Foundation | ||
import Combine | ||
|
||
class RepositoryImplementation { | ||
|
||
private let remoteDataSource: RemoteDataSource | ||
|
||
init(remoteDataSource: RemoteDataSource = RemoteDataSource()) { | ||
self.remoteDataSource = remoteDataSource | ||
} | ||
} | ||
|
||
// MARK: - Repository | ||
|
||
extension RepositoryImplementation: Repository { | ||
|
||
func searchCharacter(value: String) -> AnyPublisher<[Character], Error> { | ||
|
||
return remoteDataSource.searchCharacter(value: value).map { serverCharacters -> [Character] in | ||
|
||
var characters: [Character] = [] | ||
|
||
for serverCharacter in serverCharacters.results { | ||
|
||
// convert to entity | ||
let character = serverCharacter.converToEntity() | ||
|
||
characters.append(character) | ||
} | ||
|
||
// Return | ||
return characters | ||
} | ||
.mapError({ $0 }) | ||
.eraseToAnyPublisher() | ||
} | ||
|
||
func getCharacter(id: Int) -> AnyPublisher<Character, Error> { | ||
|
||
return remoteDataSource.getCharacter(id: id).map { serverCharacter -> Character in | ||
|
||
// convert to entity | ||
let character = serverCharacter.results.converToEntity() | ||
|
||
// Return | ||
return character | ||
} | ||
.mapError({ $0 }) | ||
.eraseToAnyPublisher() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// | ||
// URLProtocolMock.swift | ||
// MyComicsTests | ||
// | ||
// Created by Xavier Ramos on 3/4/24. | ||
// | ||
|
||
import Foundation | ||
|
||
// https://www.hackingwithswift.com/articles/153/how-to-test-ios-networking-code-the-easy-way | ||
class URLProtocolMock: URLProtocol { | ||
|
||
// this dictionary maps URLs to test data | ||
static var testURLs = [URL?: Data]() | ||
static var response: URLResponse? | ||
static var error: Error? | ||
|
||
// say we want to handle all types of request | ||
override class func canInit(with request: URLRequest) -> Bool { | ||
|
||
return true | ||
} | ||
|
||
// ignore this method; just send back what we were given | ||
override class func canonicalRequest(for request: URLRequest) -> URLRequest { | ||
|
||
return request | ||
} | ||
|
||
override func startLoading() { | ||
|
||
// if we have a valid URL and if we have test data for that URL… | ||
if let url = request.url, let data = URLProtocolMock.testURLs[url] { | ||
// …load it immediately. | ||
self.client?.urlProtocol(self, didLoad: data) | ||
} | ||
|
||
// …and we return our response if defined… | ||
if let response = URLProtocolMock.response { | ||
self.client?.urlProtocol(self, | ||
didReceive: response, | ||
cacheStoragePolicy: .notAllowed) | ||
} | ||
// …and we return our error if defined… | ||
if let error = URLProtocolMock.error { | ||
self.client?.urlProtocol(self, didFailWithError: error) | ||
} | ||
|
||
// mark that we've finished | ||
self.client?.urlProtocolDidFinishLoading(self) | ||
} | ||
|
||
// this method is required but doesn't need to do anything | ||
override func stopLoading() { | ||
// Nothing to do | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
MyComics/MyComicsTests/Repositories/RemoteDataSourceUnitTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// | ||
// RemoteDataSourceUnitTests.swift | ||
// MyComicsTests | ||
// | ||
// Created by Xavier Ramos on 3/4/24. | ||
// | ||
|
||
import XCTest | ||
@testable import MyComics | ||
|
||
final class RemoteDataSourceUnitTests: XCTestCase { | ||
|
||
var sut: RemoteDataSource? | ||
|
||
override func setUpWithError() throws { | ||
// Put setup code here. This method is called before the invocation of each test method in the class. | ||
try super.setUpWithError() | ||
|
||
sut = RemoteDataSource(baseURL: "http://jsonplaceholder.typicode.com/") | ||
} | ||
|
||
override func tearDownWithError() throws { | ||
// Put teardown code here. This method is called after the invocation of each test method in the class. | ||
sut = nil | ||
|
||
try super.tearDownWithError() | ||
} | ||
|
||
func testGetSearchCharacterEndpoint() { | ||
|
||
// Given | ||
let request = "search/" | ||
|
||
// When | ||
let response = sut!.getSearchCharacterEndpoint(value: request) | ||
|
||
// Then | ||
XCTAssertNotNil(response) | ||
XCTAssertEqual(response.url?.absoluteString.split(separator: "?").first, "http://jsonplaceholder.typicode.com/\(request)") | ||
} | ||
|
||
func testCharacterEndpoint() { | ||
|
||
// Given | ||
let request = 1 | ||
|
||
// When | ||
let response = sut!.getCharacterEndpoint(id: request) | ||
|
||
// Then | ||
XCTAssertNotNil(response) | ||
XCTAssertEqual(response.url?.absoluteString.split(separator: "?").first, "http://jsonplaceholder.typicode.com/character/4005-\(request)") | ||
} | ||
} |
Oops, something went wrong.