코드네임 :

Swift - Moya사용하기 본문

👥Club/🍀UMC🍀

Swift - Moya사용하기

비엔 Vien 2025. 8. 10. 13:48

// Request

//
//  CardInfo.swift
//  EatPic-iOS
//
//  Created by 이은정 on 8/9/25.
//

// Codable 객체

struct CreateCardRequest: Codable {
    let meal: String
    let memo: String
    let recipe: String
    let location: CreateCardRequestLocation
    let isShared: Bool
    let recipeUrl: String?
    let cardImageUrl: String?
    let hashtag: [CreateCardRequestHashtag]
    
    enum CodingKeys: String, CodingKey {
        case meal, memo, recipe, location, hashtag
        case isShared = "is_shared"
        case recipeUrl = "recipe_url"
        case cardImageUrl = "card_image_url"
    }
}

struct CreateCardRequestLocation: Codable {
    let name: String
    let latitude: Double
    let longitude: Double
}

struct CreateCardRequestHashtag: Codable {
    let hashtagId: String
    let hashtagName: String
    
    enum CodingKeys: String, CodingKey {
        case hashtagId
        case hashtagName = "hashtag_name"
    }
}

 

 

//Response

import Foundation

struct CardDetailResponse: Codable {
    let isSuccess: Bool
    let code: String
    let message: String
    let result: CardDetailResponseResult
}

struct CardDetailResponseResult: Codable {
    let cardId: Int
    let imageUrl: String
    let date: String
    let time: String
    let mealType: String
    let recipeUrl: String
    let latitude: Double
    let longitude: Double
    let locationText: String
    let memo: String
    let recipe: String
    let nextMeal: NextMeal

    enum CodingKeys: String, CodingKey {
        case cardId
        case imageUrl
        case date
        case time
        case mealType
        case recipeUrl
        case latitude
        case longitude
        case locationText
        case memo
        case recipe
        case nextMeal
    }
}

struct NextMeal: Codable {
    let cardId: Int
}
//
//  CreateCardResponse.swift
//  EatPic-iOS
//
//  Created by 이은정 on 8/9/25.
//

struct CreateCardResponse: Codable {
    let isSuccess: Bool
    let code: String
    let message: String
    let result: CreateCardResponseResult
}

struct CreateCardResponseResult: Codable {
    let meal: String
    let memo: String
    let recipe: String
    let location: CreateCardResponseLocation
    let isShared: Bool
    let recipeUrl: String?
    let cardImageUrl: String?
    let hashtag: [CreateCardResponseHashtag]
    
    enum CodingKeys: String, CodingKey {
        case meal, memo, recipe, location, hashtag
        case isShared = "is_shared"
        case recipeUrl = "recipe_url"
        case cardImageUrl = "card_image_url"
    }
}

struct CreateCardResponseLocation: Codable {
    let name: String
    let latitude: Double
    let longitude: Double
}

struct CreateCardResponseHashtag: Codable {
    let hashtagId: String
    let hashtagName: String
    
    enum CodingKeys: String, CodingKey {
        case hashtagId
        case hashtagName = "hashtag_name"
    }
}

xxxxx

 

 

// TargetType

//
//  CardTargetType.swift
//  EatPic-iOS
//
//  Created by 이은정 on 8/9/25.
//
import Foundation
import Moya

enum CardTargetType {
    case createCard(request: CreateCardRequest)
    case fetchCardDetail(cardId: Int)
}

extension CardTargetType: APITargetType {
    var path: String {
        switch self {
        case .createCard:
            return "/newcard"
        case .fetchCardDetail(let cardId):
            return "/api/cards/{cardId}"
        }
    }
    
    var method: Moya.Method {
        switch self {
        case .createCard:
            return .post
        case .fetchCardDetail:
            return .get
        }
    }
    
    var task: Moya.Task {
        switch self {
        case .createCard(let request):
            return .requestJSONEncodable(request)
        case .fetchCardDetail:
            return .requestPlain
            
        }
    }
    
    var sampleData: Data {
        switch self {
        case .createCard:
            return Data("""
                {
                  "isSuccess": true,
                  "code": "COMMON_201",
                  "message": "Pic 카드가 기록되었습니다.",
                  "result": {
                    "meal": "dinner",
                    "memo": "어쩌구저쩌구",
                    "recipe": "레시피 내용",
                    "location": {
                      "name": "서울 강남구",
                      "latitude": 37.4979,
                      "longitude": 127.0276
                    },
                    "is_shared": true,
                    "recipe_url": "http://...",
                    "card_image_url": "image.png",
                    "hashtag": [
                      { "hashtagId": "1", "hashtag_name": "한식" },
                      { "hashtagId": "2", "hashtag_name": "양식" },
                      { "hashtagId": "3", "hashtag_name": "중식" }
                    ]
                  }
            }

            """.utf8)
            
        case .fetchCardDetail:
            return Data("""
                {
                  "isSuccess": true,
                  "code": "string",
                  "message": "string",
                  "result": {
                    "cardId": 1,
                    "imageUrl": "https://cdn.eatpic.com/cards/123.jpg",
                    "date": "2025-07-01",
                    "time": "10:10",
                    "mealType": "BREAKFAST",
                    "recipeUrl": "https://recipe.example.com/salad-abc123",
                    "latitude": 37.4979,
                    "longitude": 127.0276,
                    "locationText": "장소 이름",
                    "memo": "오늘은 샐러드를 먹었습니다~ 아보카도를 많이 넣어 먹었어요~~",
                    "recipe": "이 레시피는요 일단 야채들이 필요하고요...",
                    "nextMeal": {
                      "cardId": 2
                    }
                  }
                }
            """.utf8)
        }
    }
}

 

 

// APIProviderStore

//
//  APIProviderStore.swift
//  EatPic-iOSTests
//
//  Created by jaewon Lee on 7/19/25.
//

import Foundation
import Moya

/// APIProviderStore는 각 도메인별로 필요한 MoyaProvider를 생성하는 책임을 갖습니다.
/// 네트워크 서비스에 의존하여 필요한 프로바이더를 외부에 제공합니다.
final class APIProviderStore {
    let networkService: NetworkService
    
    init(networkService: NetworkService) {
        self.networkService = networkService
    }
}

extension APIProviderStore {
    /// 사용자 API 요청을 위한 MoyaProvider를 반환합니다.
    /// 기본적으로 테스트용 Provider를 반환하도록 구성되어 있습니다.
    /// 추후, 실제 프로바이더로 변경해야 합니다.
    func user() -> MoyaProvider<UserTargetType> {
        return networkService.testProvider(for: UserTargetType.self)
    }
    
    func card() -> MoyaProvider<CardTargetType> {
        return networkService.testProvider(for: CardTargetType.self)
    }
    
    func cardDetail() -> MoyaProvider<CardTargetType> {
        return networkService.testProvider(for: CardTargetType.self)
    }
}

 

 

 

 

// ViewModel

//
//  CardViewModel.swift
//  EatPic-iOS
//
//  Created by 이은정 on 8/10/25.
//

import Foundation
import Moya

struct CardModel {
    let profileImageUrl: String
    let id: Int
    let time: String
    let imageURL: String
    let recipeUrl: String
    let latitude: Double
    let longitude: Double
    let locationText: String
    let memo: String
    let recipe: String
}

@Observable
final class CardViewModel {
    private let cardProvider: MoyaProvider<CardTargetType>
    
    init(container: DIContainer) {
        self.cardProvider = container.apiProviderStore.cardDetail()
    }
    
    @MainActor
    func fetchCardDetail(cardId: Int) async {
        do {
            let response = try await cardProvider.requestAsync(.fetchCardDetail(cardId: cardId))
            
            let dto = try JSONDecoder().decode(CardDetailResponse.self, from: response.data)
            
            print(dto)
        } catch {
            print("요청 또는 디코딩 실패:", error.localizedDescription)
        }
    }
}

 

 

 

 

// View

//
//  CardTestView.swift
//  EatPic-iOS
//
//  Created by 이은정 on 8/10/25.
//
import SwiftUI

struct CardTestView: View {
    @State private var viewModel = CardViewModel(container: .init())
    
    var body: some View {
        VStack(spacing: 16) {
            Button("카드 상세 조회") {
                Task {
                    await viewModel.fetchCardDetail(cardId: 1)

                }
            }
        }
    }
}

#Preview {
    CardTestView()
}

'👥Club > 🍀UMC🍀' 카테고리의 다른 글

Git  (0) 2025.07.07
Github-flow  (1) 2025.07.07
🍎 iOS 1주차 과제  (0) 2025.07.03
🍎 Alamofire  (0) 2025.05.29
🍎 Combine  (0) 2025.05.28