Skip to content

Commit

Permalink
Merge pull request #3 from Student-Center/feat/network
Browse files Browse the repository at this point in the history
[WEAV-17] Alamofire 네트워크 레이어 구축
  • Loading branch information
jisu15-kim authored Aug 27, 2024
2 parents 9703328 + 61ba228 commit 82fa40b
Show file tree
Hide file tree
Showing 12 changed files with 470 additions and 24 deletions.
33 changes: 22 additions & 11 deletions Projects/App/Sources/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,29 @@ public struct ContentView: View {
public init() {}

public var body: some View {
Text("Hello, World!")
.padding()
.onAppear {
ThisIsCoreKit.something()
ThisIsNetworkKit.something()
print(ThisIsNetworkKit.thisServer)
VStack {
Text("Hello, World!")
.padding()
.onAppear {
ThisIsCoreKit.something()
ThisIsNetworkKit.something()
print(ThisIsNetworkKit.thisServer)
}

MainView()

SampleComponent()
.foregroundStyle(Color.tempColor)
}
.onAppear {
Task {
do {
try await ExampleEndpoint.example.request()
} catch {
print(error)
}
}

MainView()

SampleComponent()
.foregroundStyle(Color.tempColor)
}
}
}

Expand Down
27 changes: 27 additions & 0 deletions Projects/Core/CoreKit/Sources/AuthStatus.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// AuthStatus.swift
// NetworkKit
//
// Created by 김지수 on 8/25/24.
// Copyright © 2024 com.studentcenter. All rights reserved.
//

import Foundation

public class AuthState: ObservableObject {

public enum State {
case loggedIn
case loggedOut
}

public static let shared = AuthState()

private init() {}

@Published public private(set) var current: State = .loggedOut

public func updateAuthState(to status: State) {
self.current = status
}
}
48 changes: 48 additions & 0 deletions Projects/Core/NetworkKit/Sources/AuthService/AuthEndpoint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// AuthEndpoint.swift
// NetworkKit
//
// Created by 김지수 on 8/25/24.
// Copyright © 2024 com.studentcenter. All rights reserved.
//

import Foundation
import Alamofire

public enum AuthEndpoint {
case refreshToken
}

extension AuthEndpoint: EndpointType {

public var method: HTTPMethod {
switch self {
case .refreshToken: return .post
}
}

public var path: String {
switch self {
case .refreshToken: return "auth/refresh-token"
}
}

public var parameters: Encodable? {
switch self {
case .refreshToken:
if let refreshToken = TokenManager.refreshToken {
return ["refreshToken": refreshToken]
}
return nil
}
}

public var body: Encodable? {
return nil
}
}

struct TokenResponse: Decodable {
let accessToken: String
let refreshToken: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// ExampleEndpoint.swift
// NetworkKit
//
// Created by 김지수 on 8/24/24.
// Copyright © 2024 com.studentcenter. All rights reserved.
//

import Foundation
import Alamofire

public enum ExampleEndpoint {
case example
}

extension ExampleEndpoint: EndpointType {


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

public var path: String {
switch self {
case .example: return "api/"
}
}

public var parameters: Encodable? {
return nil
}

public var body: Encodable? {
return nil
}
}
19 changes: 19 additions & 0 deletions Projects/Core/NetworkKit/Sources/NetworkCore/EndpointType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// EndpointType.swift
// NetworkKit
//
// Created by 김지수 on 8/24/24.
// Copyright © 2024 com.studentcenter. All rights reserved.
//

import Foundation
import Alamofire

public protocol EndpointType {
var method: HTTPMethod { get }
var path: String { get }
var parameters: Encodable? { get }
var body: Encodable? { get }
func request<T: Decodable>(_ type: T.Type) async throws -> T
func request() async throws
}
70 changes: 70 additions & 0 deletions Projects/Core/NetworkKit/Sources/NetworkCore/Interceptor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// Interceptor.swift
// NetworkKit
//
// Created by 김지수 on 8/25/24.
// Copyright © 2024 com.studentcenter. All rights reserved.
//

import Foundation
import CoreKit
import Alamofire

class AuthInterceptor: RequestInterceptor {

private let retryLimit = 3
private let retryDelay: TimeInterval = 1.5

func adapt(
_ urlRequest: URLRequest,
for session: Session,
completion: @escaping (Result<URLRequest, Error>) -> Void
) {
var urlRequest = urlRequest

// AccessToken 추가
if let token = TokenManager.accessToken {
urlRequest.setValue(
"Bearer \(token)",
forHTTPHeaderField: "Authorization"
)
}

completion(.success(urlRequest))
}

func retry(
_ request: Request,
for session: Session,
dueTo error: Error,
completion: @escaping (RetryResult) -> Void
) {
guard let response = request.task?.response as? HTTPURLResponse,
response.statusCode == 401,
request.retryCount < retryLimit else {
completion(.doNotRetry)
return
}

// refresh 토큰 갱신 후 retry
Task {
do {
try await refreshAccessToken()
completion(.retryWithDelay(retryDelay))
} catch {
await MainActor.run {
AuthState.shared.updateAuthState(to: .loggedOut)
}
completion(.doNotRetry)
}
}
}

private func refreshAccessToken() async throws {
let endpoint = AuthEndpoint.refreshToken
let response = try await endpoint.request(TokenResponse.self)

TokenManager.accessToken = response.accessToken
TokenManager.refreshToken = response.refreshToken
}
}
77 changes: 77 additions & 0 deletions Projects/Core/NetworkKit/Sources/NetworkCore/Logger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//
// Logger.swift
// NetworkKit
//
// Created by 김지수 on 8/25/24.
// Copyright © 2024 com.studentcenter. All rights reserved.
//

import Foundation
import Alamofire

class NetworkLogger: EventMonitor {
var queue: DispatchQueue = DispatchQueue(label: "myNetworkLogger")

func requestDidFinish(_ request: Request) {
print("")
print("======================== 👉 Network Request Log 👈 ==========================")
debugPrint("✅ [URL] : \(request.request?.url?.absoluteString ?? "")")
debugPrint("✅ [Method] : \(request.request?.httpMethod ?? "")")
debugPrint("✅ [Headers] : \(request.request?.allHTTPHeaderFields ?? [:])")

if let body = request.request?.httpBody?.toPrettyPrintedString {
debugPrint("✅ [Body] : \(body)")
} else {
debugPrint("✅ [Body] : body 없음")
}
print("==============================================================================")
print("")
}

func request<Value>(
_ request: DataRequest,
didParseResponse response: DataResponse<Value, AFError>
) {
print("")
print("======================== 👉 Network Response Log 👈 ========================")

switch response.result {
case .success:
debugPrint("✅ [StatusCode] : \(response.response?.statusCode ?? 0)")
case .failure:
debugPrint("🚨 요청 실패")
}


if let statusCode = response.response?.statusCode {
switch statusCode {
case 400..<500:
debugPrint("🚨 클라이언트 오류")
case 500..<600:
debugPrint("🚨 서버 오류")
default:
break
}
}

if let response = response.data?.toPrettyPrintedString {
debugPrint("✅ [Response] : \(response)")
}

print("============================================================================")
print("")
}
}


fileprivate 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
}
}
Loading

0 comments on commit 82fa40b

Please sign in to comment.