Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Poppool/AdminRepository\.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//
// AdminRepository\.swift
// Poppool
//
// Created by 김기현 on 1/6/25.
//

import Foundation
163 changes: 154 additions & 9 deletions Poppool/Poppool.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 73 additions & 0 deletions Poppool/Poppool/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,76 @@
////
//// SceneDelegate.swift
//// Poppool
////
//// Created by Porori on 11/24/24.
////
//
//import UIKit
//import RxKakaoSDKAuth
//import KakaoSDKAuth
//import RxSwift
//
//class SceneDelegate: UIResponder, UIWindowSceneDelegate {
//
// var window: UIWindow?
// static let appDidBecomeActive = PublishSubject<Void>()
//
// func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// guard let windowScene = (scene as? UIWindowScene) else { return }
// window = UIWindow(windowScene: windowScene)
//
// // Debug: Admin Page Test
// let provider = ProviderImpl()
// let repository = DefaultAdminRepository(provider: provider)
// let useCase = DefaultAdminUseCase(repository: repository)
// let reactor = AdminReactor(useCase: useCase)
// let adminVC = AdminViewController()
// adminVC.reactor = reactor
//
// let navigationController = UINavigationController(rootViewController: adminVC)
//
// let rootViewController = LoginController()
// rootViewController.reactor = LoginReactor()
//
// let rootVC = WaveTabBarController()
//
// let rootViewController = DetailController()
// rootViewController.reactor = DetailReactor(popUpID: 8)
//
// let rootViewController = SearchMainController()
// rootViewController.reactor = SearchMainReactor()
//
// let navigationController = UINavigationController(rootViewController: rootVC)
// let navigationController = WaveTabBarController()
//
// window?.rootViewController = navigationController
// window?.makeKeyAndVisible()
// }
//
// func sceneDidDisconnect(_ scene: UIScene) {
// }
//
// func sceneDidBecomeActive(_ scene: UIScene) {
// SceneDelegate.appDidBecomeActive.onNext(())
// }
//
// func sceneWillResignActive(_ scene: UIScene) {
// }
//
// func sceneWillEnterForeground(_ scene: UIScene) {
// }
//
// func sceneDidEnterBackground(_ scene: UIScene) {
// }
//
// func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
// if let url = URLContexts.first?.url {
// if AuthApi.isKakaoTalkLoginUrl(url) {
// _ = AuthController.rx.handleOpenUrl(url: url)
// }
// }
// }
//}
//
// SceneDelegate.swift
// Poppool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,25 @@ final class ProviderImpl: Provider {
with endpoint: E,
interceptor: RequestInterceptor? = nil
) -> Observable<R> where R == E.Response {

return Observable.create { [weak self] observer in
do {
let urlRequest = try endpoint.getUrlRequest()
Logger.log(message: "\(urlRequest) 요청 시간 :\(Date.now)", category: .network)
// self?.timeoutTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in
// IndicatorMaker.showIndicator()
// }


let request = AF.request(urlRequest, interceptor: interceptor)
.validate()
.responseData { [weak self] response in
// self?.timeoutTimer?.invalidate()
// IndicatorMaker.hideIndicator()
Logger.log(message: "\(urlRequest) 응답 시간 :\(Date.now)", category: .network)
switch response.result {
case .success(let data):

// Logger.log(message: "응답 데이터: \(String(data: data, encoding: .utf8) ?? "데이터가 없습니다.")", category: .network)
// EmptyResponse 타입이고 데이터가 비어있는 경우 성공으로 처리
if R.self == EmptyResponse.self && (data.isEmpty || data.count == 0) {
observer.onNext(EmptyResponse() as! R)
observer.onCompleted()
return
}

do {
let decodedData = try JSONDecoder().decode(R.self, from: data)
observer.onNext(decodedData)
Expand All @@ -52,7 +52,7 @@ final class ProviderImpl: Provider {
observer.onError(error)
}
}

return Disposables.create {
request.cancel()
}
Expand All @@ -63,6 +63,7 @@ final class ProviderImpl: Provider {
}
}
}


func request<E: Requestable>(
with request: E,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,28 @@
import Foundation

struct PreSignedAPIEndPoint {

static func presigned_upload(request: PresignedURLRequestDTO) -> Endpoint<PreSignedURLResponseDTO>{
static func presigned_upload(request: PresignedURLRequestDTO) -> Endpoint<PreSignedURLResponseDTO> {
Logger.log(message: "Presigned URL 생성 - Request: \(request)", category: .debug)
return Endpoint(
baseURL: Secrets.popPoolBaseUrl.rawValue,
path: "/files/upload-preSignedUrl",
method: .post,
bodyParameters: request
)
}

static func presigned_download(request: PresignedURLRequestDTO) -> Endpoint<PreSignedURLResponseDTO>{

static func presigned_download(request: PresignedURLRequestDTO) -> Endpoint<PreSignedURLResponseDTO> {
Logger.log(message: "Presigned Download URL 생성 - Request: \(request)", category: .debug)
return Endpoint(
baseURL: Secrets.popPoolBaseUrl.rawValue,
path: "/files/download-preSignedUrl",
method: .post,
bodyParameters: request
)
}

static func presigned_delete(request: PresignedURLRequestDTO) -> RequestEndpoint{

static func presigned_delete(request: PresignedURLRequestDTO) -> RequestEndpoint {
Logger.log(message: "Presigned Delete 생성 - Request: \(request)", category: .debug)
return RequestEndpoint(
baseURL: Secrets.popPoolBaseUrl.rawValue,
path: "/files/delete",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,59 +17,74 @@ class ImageCache {
}

class PreSignedService {

struct PresignedURLRequest {
var filePath: String
var image: UIImage
}

let tokenInterceptor = TokenInterceptor()

let provider = ProviderImpl()

let disposeBag = DisposeBag()

func tryDelete(targetPaths: PresignedURLRequestDTO) -> Completable {
let endPoint = PreSignedAPIEndPoint.presigned_delete(request: targetPaths)
return provider.request(with: endPoint, interceptor: tokenInterceptor)
}

func tryUpload(datas: [PresignedURLRequest]) -> Single<Void> {

Logger.log(message: "tryUpload 호출됨 - 요청 데이터 수: \(datas.count)", category: .debug)

return Single.create { [weak self] observer in
Logger.log(message: "tryUpload 내부 흐름 시작", category: .debug)

guard let self = self else {
Logger.log(message: "self가 nil입니다. 작업을 중단합니다.", category: .error)
return Disposables.create()
}

getUploadLinks(request: .init(objectKeyList: datas.map { $0.filePath } ))

// 1. 업로드 링크 요청
self.getUploadLinks(request: .init(objectKeyList: datas.map { $0.filePath }))
.subscribe { response in
Logger.log(message: "getUploadLinks 성공: \(response.preSignedUrlList)", category: .debug)

let responseList = response.preSignedUrlList
let inputList = datas

// 2. 업로드 준비
let requestList = zip(responseList, inputList).compactMap { zipResponse in
let urlResponse = zipResponse.0
let inputResponse = zipResponse.1
Logger.log(message: "업로드 준비 - URL: \(urlResponse.preSignedUrl)", category: .debug)
return self.uploadFromS3(url: urlResponse.preSignedUrl, image: inputResponse.image)
}

// 3. 병렬 업로드 실행
Single.zip(requestList)
.subscribe(onSuccess: { _ in
print("All images uploaded successfully")
Logger.log(message: "모든 이미지 업로드 성공", category: .info)
observer(.success(()))
}, onFailure: { error in
print("Image upload failed: \(error.localizedDescription)")
Logger.log(message: "이미지 업로드 실패: \(error.localizedDescription)", category: .error)
observer(.failure(error))
})
.disposed(by: self.disposeBag)

} onError: { error in
print("getUploadLinks Fail: \(error.localizedDescription)")
Logger.log(message: "getUploadLinks 실패: \(error.localizedDescription)", category: .error)
observer(.failure(error))
}
.disposed(by: disposeBag)
.disposed(by: self.disposeBag)

return Disposables.create()
}
}



func tryDownload(filePaths: [String]) -> Single<[UIImage]> {

return Single.create { [weak self] observer in
guard let self = self else {
return Disposables.create()
Expand Down Expand Up @@ -136,36 +151,38 @@ class PreSignedService {


private extension PreSignedService {

func uploadFromS3(url: String, image: UIImage) -> Single<Void> {
return Single.create { single in
if let imageData = image.jpegData(compressionQuality: 0),
let url = URL(string: url)
{
// Content-Type 헤더 설정
let url = URL(string: url) {
Logger.log(message: "S3 업로드 요청 URL: \(url.absoluteString)", category: .debug)

let headers: HTTPHeaders = [
"Content-Type": "image/jpeg"
]

AF.upload(imageData, to: url, method: .put, headers: headers)
.response { response in
Logger.log(message: "S3 업로드 응답 상태: \(response.response?.statusCode ?? -1)", category: .debug)
switch response.result {
case .success:
print("success")
Logger.log(message: "S3 업로드 성공 - URL: \(url.absoluteString)", category: .info)
single(.success(()))
case .failure(let error):
print("failure")
Logger.log(message: "S3 업로드 실패: \(error.localizedDescription)", category: .error)
single(.failure(error))
}
}
return Disposables.create()
} else {
Logger.log(message: "S3 업로드 실패 - 잘못된 URL 또는 데이터", category: .error)
single(.failure(NSError(domain: "InvalidDataOrURL", code: -1, userInfo: nil)))
return Disposables.create()
}
}
}

func downloadFromS3(url: String) -> Single<Data> {
return Single.create { single in
if let url = URL(string: url) {
Expand All @@ -177,7 +194,7 @@ private extension PreSignedService {
single(.failure(error))
}
}

return Disposables.create {
request.cancel()
}
Expand All @@ -187,13 +204,20 @@ private extension PreSignedService {
}
}
}



func getUploadLinks(request: PresignedURLRequestDTO) -> Observable<PreSignedURLResponseDTO> {
Logger.log(message: "Presigned URL 생성 요청 데이터: \(request)", category: .debug)
let provider = ProviderImpl()
let endPoint = PreSignedAPIEndPoint.presigned_upload(request: request)
return provider.requestData(with: endPoint, interceptor: tokenInterceptor)
.do(onNext: { response in
Logger.log(message: "Presigned URL 응답 데이터: \(response.preSignedUrlList)", category: .debug)
}, onError: { error in
Logger.log(message: "Presigned URL 요청 실패: \(error.localizedDescription)", category: .error)
})
}

func getDownloadLinks(request: PresignedURLRequestDTO) -> Observable<PreSignedURLResponseDTO> {
let provider = ProviderImpl()
let endPoint = PreSignedAPIEndPoint.presigned_download(request: request)
Expand Down
Loading