Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

검색키워드를 입력하면 서버 데이터를 전달받아 목록 화면에 나타냅니다. #1

Merged
merged 7 commits into from
Aug 2, 2022
114 changes: 110 additions & 4 deletions BookFinder.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion BookFinder/App/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
window?.makeKeyAndVisible()

let navigationController = UINavigationController()
navigationController.view.backgroundColor = .white
// navigationController.view.backgroundColor = .white
window?.rootViewController = navigationController

appCoordinator = AppCoordinator(navigationController: navigationController)
Expand Down
8 changes: 4 additions & 4 deletions BookFinder/Data/RemoteDB/BookFinderURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ struct BookFinderURL {
let method: HttpMethod = .get

init(
keywords: String,
searchText: String,
pageNumber: Int = 1,
itemPerPage: Int = 20,
baseURL: String = baseURL
) {
let itemPerPage = 10
let startIndex = (pageNumber - 1) * itemPerPage

var urlComponents = URLComponents(string: "\(baseURL)volumes?")
let keywordsForTitleOrAuthorsQuery = URLQueryItem(name: "q", value: "\(keywords)") // TODO: 검색 구체화 (제목, 저자로 항목 한정)
let searchTextQuery = URLQueryItem(name: "q", value: "\(searchText)") // TODO: 검색 구체화 (제목, 저자로 항목 한정)
let startIndexQuery = URLQueryItem(name: "startIndex", value: "\(startIndex)")
let maxResultsQuery = URLQueryItem(name: "maxResults", value: "\(itemPerPage)")
urlComponents?.queryItems?.append(
contentsOf: [keywordsForTitleOrAuthorsQuery, startIndexQuery, maxResultsQuery]
contentsOf: [searchTextQuery, startIndexQuery, maxResultsQuery]
)

self.url = urlComponents?.url
Expand Down
7 changes: 4 additions & 3 deletions BookFinder/Data/RemoteDB/Entities/VolumeInfoDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ struct VolumeInfoDTO: Codable {
let title: String?
let subtitle: String?
let authors: [String]?
let publisher, publishedDate, volumeInfoDescription: String?
let publisher, publishedDate, volumeDescription: String?
let industryIdentifiers: [IndustryIdentifier]?
let readingModes: ReadingModes?
let pageCount: Int?
let printType: String?
let categories: [String]?
let averageRating, ratingsCount: Int?
let averageRating: Double?
let ratingsCount: Int?
let maturityRating: String?
let allowAnonLogging: Bool?
let contentVersion: String?
Expand All @@ -29,7 +30,7 @@ struct VolumeInfoDTO: Codable {

enum CodingKeys: String, CodingKey {
case title, subtitle, authors, publisher, publishedDate
case volumeInfoDescription = "description"
case volumeDescription = "description"
case industryIdentifiers, readingModes, pageCount, printType, categories, averageRating, ratingsCount,
maturityRating, allowAnonLogging, contentVersion, panelizationSummary, imageLinks, language,
previewLink, infoLink, canonicalVolumeLink
Expand Down
13 changes: 0 additions & 13 deletions BookFinder/Data/RemoteDB/NetworkProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,6 @@ struct NetworkProvider {
}
}

// func request(api: Requestable) -> Observable<Data> {
// return Observable.create { emitter in
// guard let task = dataTask(api: api, emitter: emitter) else {
// return Disposables.create()
// }
// task.resume()
//
// return Disposables.create {
// task.cancel()
// }
// }
// }

private func dataTask<T: Codable>(api: APIProtocol, emitter: AnyObserver<T>) -> URLSessionDataTask? {
guard let urlRequest = URLRequest(api: api) else {
emitter.onError(NetworkError.urlIsNil)
Expand Down
14 changes: 14 additions & 0 deletions BookFinder/Extensions/Array+Subscript.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Array+Subscript.swift
// BookFinder
//
// Created by Hyoju Son on 2022/07/31.
//

import Foundation

extension Array {
subscript(safe index: Int) -> Element? {
return indices ~= index ? self[index] : nil
}
}
16 changes: 16 additions & 0 deletions BookFinder/Extensions/UIColor+CustomColor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// UIColor+CustomColor.swift
// BookFinder
//
// Created by Hyoju Son on 2022/07/31.
//

import UIKit

extension UIColor {
static let background: UIColor = #colorLiteral(red: 0.9524367452, green: 0.9455882907, blue: 0.9387311935, alpha: 1)
static let lightGreen: UIColor = #colorLiteral(red: 0.5567998886, green: 0.7133290172, blue: 0.6062341332, alpha: 1)
static let darkGreen: UIColor = #colorLiteral(red: 0.137904644, green: 0.3246459067, blue: 0.2771841288, alpha: 1)
static let lightGray = #colorLiteral(red: 0.9137251377, green: 0.9137259126, blue: 0.9309338927, alpha: 1) // light mode의 systemGray6
static let mediumLightGray = #colorLiteral(red: 0.8196074367, green: 0.8196083307, blue: 0.8411096334, alpha: 1) // light mode의 systemGray4
}
39 changes: 39 additions & 0 deletions BookFinder/Extensions/UIImageView+Load+Cache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// UIImageView+Load+Cache.swift
// BookFinder
//
// Created by Hyoju Son on 2022/07/31.
//

import UIKit

extension UIImageView {
func loadCachedImage(of key: String) {
let cacheKey = NSString(string: key)
if let cachedImage = ImageCacheManager.shared.object(forKey: cacheKey) {
self.image = cachedImage
return
}

DispatchQueue.global().async {
guard
let url = URL(string: key),
var urlCompoentns = URLComponents(url: url, resolvingAgainstBaseURL: false)
else { return }

urlCompoentns.scheme = "https"

guard
let imageURL = urlCompoentns.url,
let imageData = try? Data(contentsOf: imageURL),
let loadedImage = UIImage(data: imageData)
else { return }

ImageCacheManager.shared.setObject(loadedImage, forKey: cacheKey)

DispatchQueue.main.async {
self.image = loadedImage
}
}
}
}
81 changes: 68 additions & 13 deletions BookFinder/Model/BookItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,80 @@

import Foundation

struct BookItem {
final class BookItem: Hashable {
let id: String

// VolumeInformation
let title: String
let authors: [String]
let publisher: String

let publishedDate: String
let volumeDescription: String
let pageCount: Int?
let categories: [String]?
let averageRating: Int?
let averageRating: Double?
let ratingsCount: Int?
let language: String
let smallThumbnailURL: String // ImageLinks
let thumbnailURL: String
let smallThumbnailURL: String? // ImageLinks

init(
id: String?,
title: String?,
authors: [String]?,
publishedDate: String?,
averageRating: Double?,
ratingsCount: Int?,
smallThumbnailURL: String?
) {
self.id = id ?? "id 정보 없음"
self.title = title ?? "제목 없음"
self.authors = authors ?? ["저자 정보 없음"]
self.publishedDate = publishedDate ?? "출간일 정보 없음"
self.averageRating = averageRating
self.ratingsCount = ratingsCount
self.smallThumbnailURL = smallThumbnailURL // TODO: 정보없음 이미지로 교체
}

static func == (lhs: BookItem, rhs: BookItem) -> Bool {
return lhs.id == rhs.id
}

// TODO: 상세화면 부가기능 고려
// AccessInformation
let isEbookAvailable: Bool // Epub.isAvailable
let istextToSpeechAvailable: Bool // textToSpeechPermission == "ALLOWED"
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}

//struct BookItem: Hashable {
// let id: String
//
// // VolumeInformation
// let title: String
// let authors: [String]
// let publisher: String
// let publishedDate: String
// let volumeDescription: String
// let averageRating: Double?
// let ratingsCount: Int?
// let smallThumbnailURL: String // ImageLinks
// let thumbnailURL: String
//
// init(
// id: String?,
// title: String?,
// authors: [String]?,
// publisher: String?,
// publishedDate: String?,
// volumeDescription: String?,
// averageRating: Double?,
// ratingsCount: Int?,
// smallThumbnailURL: String?,
// thumbnailURL: String?
// ) {
// self.id = id ?? "id 정보 없음"
// self.title = title ?? "제목 없음"
// self.authors = authors ?? ["저자 정보 없음"]
// self.publisher = publisher ?? "출판사 정보 없음"
// self.publishedDate = publishedDate ?? "출간일 정보 없음"
// self.volumeDescription = volumeDescription ?? "상세정보 없음"
// self.averageRating = averageRating
// self.ratingsCount = ratingsCount
// self.smallThumbnailURL = smallThumbnailURL ?? "" // TODO: 정보없음 이미지로 교체
// self.thumbnailURL = thumbnailURL ?? ""
// }
//}
14 changes: 0 additions & 14 deletions BookFinder/Model/SearchResult.swift

This file was deleted.

19 changes: 5 additions & 14 deletions BookFinder/Presentation/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,13 @@ final class AppCoordinator: CoordinatorProtocol {

// MARK: - Methods
func start() {
showSearchPage()
showSearchListPage()
}

private func showSearchPage() {
private func showSearchListPage() {
guard let navigationController = navigationController else { return }
// let searchCoordinator = SearchCoordinator(navigationController: navigationController)
// childCoordinators.append(searchCoordinator)
// searchCoordinator.start()
let searchListCoordinator = SearchListCoordinator(navigationController: navigationController)
childCoordinators.append(searchListCoordinator)
searchListCoordinator.start()
}

// func removeFromChildCoordinators(coordinator: CoordinatorProtocol) {
// let updatedChildCoordinators = childCoordinators.filter { $0 !== coordinator }
// childCoordinators = updatedChildCoordinators
// }

// func popCurrentPage() {
// navigationController?.popViewController(animated: true)
// }
}
45 changes: 45 additions & 0 deletions BookFinder/Presentation/SearchListPage/SearchListCoordinator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// SearchCoordinator.swift
// BookFinder
//
// Created by Hyoju Son on 2022/07/31.
//

import UIKit

final class SearchListCoordinator: CoordinatorProtocol {
// MARK: - Properties
var navigationController: UINavigationController?
var childCoordinators = [CoordinatorProtocol]()
var type: CoordinatorType = .searchList

// MARK: - Initializers
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}

// MARK: - Methods
func start() {
showSearchListPage()
}

private func showSearchListPage() {
guard let navigationController = navigationController else { return }
let searchListViewModel = SearchListViewModel(coordinator: self)
let searchListViewController = SearchListViewController(viewModel: searchListViewModel)

navigationController.pushViewController(searchListViewController, animated: false)
}

func showDetailPage(with bookItem: BookItem) {
guard let navigationController = navigationController else { return }
// let detailCoordinator = DetailCoordinator(navigationController: navigationController)
// childCoordinators.append(detailCoordinator)
// detailCoordinator.start()
}

// func removeFromChildCoordinators(coordinator: CoordinatorProtocol) {
// let updatedChildCoordinators = childCoordinators.filter { $0 !== coordinator }
// childCoordinators = updatedChildCoordinators
// }
}
Loading