Skip to content

Commit

Permalink
Merge pull request #8 from GO-SOPT-iOS-Part/feature/#7
Browse files Browse the repository at this point in the history
[Feat] #7 - 4차 과제
  • Loading branch information
ffalswo2 authored Jul 23, 2023
2 parents 1ad6e72 + 1ebe846 commit fdce511
Show file tree
Hide file tree
Showing 30 changed files with 944 additions and 98 deletions.
256 changes: 241 additions & 15 deletions SOPTving/SOPTving.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
{
"pins" : [
{
"identity" : "alamofire",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Alamofire/Alamofire.git",
"state" : {
"revision" : "bc268c28fb170f494de9e9927c371b8342979ece",
"version" : "5.7.1"
}
},
{
"identity" : "kingfisher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/onevcat/Kingfisher.git",
"state" : {
"revision" : "af4be924ad984cf4d16f4ae4df424e79a443d435",
"version" : "7.6.2"
}
},
{
"identity" : "snapkit",
"kind" : "remoteSourceControl",
Expand Down
69 changes: 69 additions & 0 deletions SOPTving/SOPTving/Data/Entity/Main/PopularMovieEntity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// PopularMovieEntity.swift
// SOPTving
//
// Created by 김민재 on 2023/05/11.
//

import UIKit

struct PopularMovieEntity: Decodable {
let page: Int
let results: [Result]
let totalPages: Int
let totalResults: Int

enum CodingKeys: String, CodingKey {
case page, results
case totalPages = "total_pages"
case totalResults = "total_results"
}
}

// MARK: - Result
struct Result: Decodable {
let adult: Bool
let backdropPath: String
let genreIDS: [Int]
let id: Int
let originalLanguage: OriginalLanguage
let originalTitle, overview: String
let popularity: Double
let posterPath: String
let releaseDate: String
let title: String
let video: Bool
let voteAverage: Double
let voteCount: Int

enum CodingKeys: String, CodingKey {
case adult
case backdropPath = "backdrop_path"
case genreIDS = "genre_ids"
case id
case originalLanguage = "original_language"
case originalTitle = "original_title"
case overview, popularity
case posterPath = "poster_path"
case releaseDate = "release_date"
case title, video
case voteAverage = "vote_average"
case voteCount = "vote_count"
}
}

enum OriginalLanguage: String, Decodable {
case en = "en"
case fr = "fr"
}

// MARK: Entity -> Model

extension PopularMovieEntity {
func toDomain() -> [Movie] {
return self.results.map {
let url = TMDBImageURL.posterURL + $0.posterPath
return Movie(title: $0.title, posterImagePath: url)
}
}
}
45 changes: 45 additions & 0 deletions SOPTving/SOPTving/Data/Network/APIEventLogger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// APIEventLogger.swift
// SOPTving
//
// Created by 김민재 on 2023/05/11.
//

import Foundation

import Alamofire

final class APIEventLogger: EventMonitor {
func requestDidFinish(_ request: Request) {

print("🛰 NETWORK Reqeust LOG")
print(request.description)

print(
"URL: " + (request.request?.url?.absoluteString ?? "") + "\n"
+ "Method: " + (request.request?.httpMethod ?? "") + "\n"
+ "Headers: " + "\(request.request?.allHTTPHeaderFields ?? [:])" + "\n"
)
print("Authorization: " + (request.request?.headers["Authorization"] ?? ""))
print("Body: " + (request.request?.httpBody?.toPrettyPrintedString ?? ""))
}

func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {
print("🛰 NETWORK Response LOG")
print(
"URL: " + (request.request?.url?.absoluteString ?? "") + "\n"
+ "Result: " + "\(response.result)" + "\n"
+ "StatusCode: " + "\(response.response?.statusCode ?? 0)" + "\n"
+ "Data: \(response.data?.toPrettyPrintedString ?? "")"
)
}
}

extension Data {
var toPrettyPrintedString: String? {
guard let object = try? JSONSerialization.jsonObject(with: self, options: []),
let data = try? JSONSerialization.data(withJSONObject: object, options: [.prettyPrinted]),
let prettyPrintedString = NSString(data: data, encoding: String.Encoding.utf8.rawValue) else { return nil }
return prettyPrintedString as String
}
}
41 changes: 41 additions & 0 deletions SOPTving/SOPTving/Data/Network/Config.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Config.swift
// SOPTving
//
// Created by 김민재 on 2023/05/11.
//

import Foundation

enum Config {
enum Keys {
enum Plist {
static let baseURL = "BASE_URL"
static let apiKey = "API_KEY"
}
}

private static let infoDictionary: [String: Any] = {
guard let dict = Bundle.main.infoDictionary else {
fatalError("plist cannot found !")
}
return dict
}()
}


extension Config {
static let baseURL: String = {
guard let key = Config.infoDictionary[Keys.Plist.baseURL] as? String else {
fatalError("Base URL is not set in plist for this configuration")
}
return key
}()

static let apiKey: String = {
guard let key = Config.infoDictionary[Keys.Plist.apiKey] as? String else {
fatalError("Base URL is not set in plist for this configuration")
}
return key
}()
}
20 changes: 20 additions & 0 deletions SOPTving/SOPTving/Data/Network/HTTPHeaderField.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// HTTPHeaderField.swift
// SOPTving
//
// Created by 김민재 on 2023/05/11.
//

import Foundation

import Alamofire

enum HTTPHeaderField: String {
case authentication = "Authorization"
case contentType = "Content-Type"
case acceptType = "Accept"
}

enum ContentType: String {
case json = "Application/json"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// PopularMovieRequestDTO.swift
// SOPTving
//
// Created by 김민재 on 2023/05/12.
//

import Foundation

struct PopularMovieRequestDTO: Encodable {
let apiKey: String
let page: Int?
let language: String?

enum CodingKeys: String, CodingKey {
case apiKey = "api_key"
case page, language
}
}

enum Language {
static let english = "en-US"
}
16 changes: 16 additions & 0 deletions SOPTving/SOPTving/Data/Network/NetworkResult.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// NetworkResult.swift
// SOPTving
//
// Created by 김민재 on 2023/05/11.
//

import Foundation

enum NetworkResult<T> {
case success(T) // 서버 통신 성공
case requestErr(T) // 요청에러 발생
case pathErr // 경로 에러
case serverErr // 서버 내부 에러
case networkErr // 네트워크 연결 실패
}
39 changes: 39 additions & 0 deletions SOPTving/SOPTving/Data/Network/Router/MovieTarget.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// MovieTarget.swift
// SOPTving
//
// Created by 김민재 on 2023/05/11.
//

import Foundation

import Alamofire

enum MovieTarget {
case getPopularMovie(_ dto: PopularMovieRequestDTO)
}

extension MovieTarget: TargetType {

var method: HTTPMethod {
switch self {
case .getPopularMovie:
return .get
}
}

var path: String {
switch self {
case .getPopularMovie(_):
return "/popular"
}
}

var parameters: RequestParams {
switch self {
case .getPopularMovie(let dto):
return .query(dto)
}
}

}
54 changes: 54 additions & 0 deletions SOPTving/SOPTving/Data/Network/Service/BaseService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// BaseService.swift
// SOPTving
//
// Created by 김민재 on 2023/05/11.
//

import Foundation

import Alamofire


class BaseService {

static let shared = BaseService()
private init() {}

func requestObject<T: Decodable>(_ target: TargetType, completion: @escaping (NetworkResult<T>) -> Void) {
let dataRequest = AF.request(target)
dataRequest.responseData { response in
switch response.result {
case .success:
guard let statusCode = response.response?.statusCode else { return }
guard let value = response.value else { return }
let networkResult = self.judgeStatus(by: statusCode, value, type: T.self)
completion(networkResult)
case .failure:
completion(.networkErr)
}

}

}

private func judgeStatus<T: Decodable>(by statusCode: Int, _ data: Data, type: T.Type) -> NetworkResult<T> {
switch statusCode {
case 200: return isValidData(data: data, type: T.self)
case 401...409: return isValidData(data: data, type: T.self)
case 500: return .serverErr
default: return .networkErr
}
}

private func isValidData<T: Decodable>(data: Data, type: T.Type) -> NetworkResult<T> {
let decoder = JSONDecoder()
guard let decodedData = try? decoder.decode(T.self, from: data) else {
print("json decoded failed !")
return .pathErr
}

return .success(decodedData)
}

}
21 changes: 21 additions & 0 deletions SOPTving/SOPTving/Data/Network/Service/MovieService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// MovieService.swift
// SOPTving
//
// Created by 김민재 on 2023/05/12.
//

import Foundation

import Alamofire


protocol MovieService {
func getPopularMovies<T: Decodable>(queryDTO: PopularMovieRequestDTO, type: T.Type, completion: @escaping (NetworkResult<T>) -> Void)
}

extension BaseService: MovieService {
func getPopularMovies<T: Decodable>(queryDTO: PopularMovieRequestDTO, type: T.Type, completion: @escaping (NetworkResult<T>) -> Void) {
requestObject(MovieTarget.getPopularMovie(queryDTO), completion: completion)
}
}
Loading

0 comments on commit fdce511

Please sign in to comment.