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

[CAT-207] 온보딩 - 고양이 선택하기 뷰 UI및 기능 #29

Merged
merged 14 commits into from
Aug 16, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ public enum Domain: String, Modulable {
case AppService
case AuthService
case PushService
case UserService
}
9 changes: 8 additions & 1 deletion Projects/Core/APIClient/Interface/APIClientInterface.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@

public func apiRequest<T: Decodable>(
request: APIBaseRequest,
as: T.Type,
as: T.Type = T.self,
isWithInterceptor: Bool = true
) async throws -> T {
let (data, _) = try await self.apiRequest(request, isWithInterceptor)

if T.self == EmptyResponse.self {
return EmptyResponse() as! T

Check warning on line 31 in Projects/Core/APIClient/Interface/APIClientInterface.swift

View workflow job for this annotation

GitHub Actions / Run Swiftlint

Force casts should be avoided (force_cast)
}

do {
let decodedData = try JSONDecoder().decode(T.self, from: data)
return decodedData
Expand All @@ -40,3 +44,6 @@
public static let previewValue = Self()
public static let testValue = Self()
}

// MARK: Empty Response 대응 논의 필요
public struct EmptyResponse: Decodable {}
15 changes: 0 additions & 15 deletions Projects/Core/KeychainClient/Interface/Keys.swift

This file was deleted.

10 changes: 7 additions & 3 deletions Projects/Domain/AuthService/Sources/API/AuthAPIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,24 @@ extension AuthService: DependencyKey {
private static func live() -> Self {
return AuthService(
login: { deviceID, apiClient, keychainClient in
guard isTokenValid(keychainClient) else { return }
guard !isTokenValid(keychainClient) else { return }

let service = AuthAPIRequest.login(deviceID)
let response = try await apiClient.apiRequest(
request: service,
as: AuthDTO.Response.TokenResponseDTO.self,
isWithInterceptor: false
)
_ = keychainClient.create(key: KeychainKeys.accessToken.rawValue, data: response.accessToken)

_ = keychainClient.create(key: "mohanyang_keychain_access_token", data: response.accessToken)
_ = keychainClient.create(key: "mohanyang_keychain_refresh_token", data: response.refreshToken)
Jihyun247 marked this conversation as resolved.
Show resolved Hide resolved
return
}
)
}

private static func isTokenValid(_ keychainClient: KeychainClient) -> Bool {
return keychainClient.read(key: KeychainKeys.accessToken.rawValue) != nil
let isTokenExist = keychainClient.read(key: "mohanyang_keychain_access_token")
return isTokenExist != nil
}
}
2 changes: 2 additions & 0 deletions Projects/Domain/Domain/Sources/Exports.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@
@_exported import PushService
@_exported import AuthService
@_exported import AuthServiceInterface
@_exported import UserService
@_exported import UserServiceInterface
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// UserAPIClientInterface.swift
// UserService
//
// Created by 김지현 on 8/16/24.
// Copyright © 2024 PomoNyang. All rights reserved.
//

import Foundation

import APIClientInterface

import Dependencies
import DependenciesMacros


@DependencyClient
public struct UserService {
public var fetchCatLists: @Sendable (
_ apiClient: APIClient
) async throws -> CatList
public var selectCat: @Sendable (
_ no: Int,
_ apiClient: APIClient
) async throws -> Void
}

extension UserService: TestDependencyKey {
public static let previewValue = Self()
public static let testValue = Self()
}
48 changes: 48 additions & 0 deletions Projects/Domain/UserService/Interface/API/UserAPIRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// UserAPIRequest.swift
// UserService
//
// Created by 김지현 on 8/16/24.
// Copyright © 2024 PomoNyang. All rights reserved.
//

import Foundation
import APIClientInterface

public enum UserAPIrequest {
case fetchCatList, selectCat(Int)
}

extension UserAPIrequest: APIBaseRequest {
public var baseURL: String {
return API.apiBaseURL
}

public var path: String {
switch self {
case .fetchCatList:
return "/api/v1/cats"
case .selectCat:
return "/api/v1/users/cats"
}
}

public var method: HTTPMethod {
switch self {
case .fetchCatList:
return .get
case .selectCat:
return .put
}
}

public var parameters: RequestParams {
switch self {
case .fetchCatList:
return .requestPlain
case .selectCat(let no):
let dto = UserDTO.Request.SelectCatRequestDTO(catNo: no)
return .body(dto)
}
}
}
32 changes: 32 additions & 0 deletions Projects/Domain/UserService/Interface/DTO/UserDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// UserDTO.swift
// UserService
//
// Created by 김지현 on 8/16/24.
// Copyright © 2024 PomoNyang. All rights reserved.
//

import Foundation

public typealias CatList = [UserDTO.Response.GetCatListResponseDTO]

public enum UserDTO {
public enum Request { }
public enum Response { }
}

public extension UserDTO.Request {
struct SelectCatRequestDTO: Encodable {
public var catNo: Int
}
}

public extension UserDTO.Response {
struct GetCatListResponseDTO: Equatable, Decodable {
public var no: Int
public var name: String
public var type: String
}

struct SelectCatResponseDTO: Decodable { }
}
20 changes: 20 additions & 0 deletions Projects/Domain/UserService/Project.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import ProjectDescription
import ProjectDescriptionHelpers

@_spi(Domain)
@_spi(Core)
import DependencyPlugin

let project: Project = .makeTMABasedProject(
module: Domain.UserService,
scripts: [],
targets: [
.sources,
.interface
],
dependencies: [
.interface: [
.dependency(rootModule: Core.self)
]
]
)
37 changes: 37 additions & 0 deletions Projects/Domain/UserService/Sources/API/UserAPIClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// UserAPIClient.swift
// UserService
//
// Created by 김지현 on 8/16/24.
// Copyright © 2024 PomoNyang. All rights reserved.
//

import Foundation

import APIClientInterface
import UserServiceInterface

import Dependencies

extension UserService: DependencyKey {
public static let liveValue: UserService = .live()
private static func live() -> Self {
return UserService(
fetchCatLists: { apiClient in
let request = UserAPIrequest.fetchCatList
return try await apiClient.apiRequest(
request: request,
as: CatList.self
)
},
selectCat: { no, apiClient in
let request = UserAPIrequest.selectCat(no)
_ = try await apiClient.apiRequest(
request: request,
as: EmptyResponse.self
)
return
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,21 @@ public struct OnboardingCore {
var width: CGFloat = .zero
var currentIdx: Int = 0
var currentItemID: String = ""
@Presents var selectCat: SelectCatCore.State?
}

public enum Action: BindableAction {
case onApear
case calculateOffset(CGFloat, OnboardingItem)
case dragStart
case tapStartButton
case _timerStart
case _timerEnd
case _timerTicked
case _nextPage(Int)
case _resetTofront
case binding(BindingAction<State>)
case selectCat(PresentationAction<SelectCatCore.Action>)
}

public init() { }
Expand All @@ -42,6 +45,9 @@ public struct OnboardingCore {
public var body: some ReducerOf<Self> {
BindingReducer()
Reduce(self.core)
.ifLet(\.$selectCat, action: \.selectCat) {
SelectCatCore()
}
}

private func core(_ state: inout State, _ action: Action) -> EffectOf<Self> {
Expand Down Expand Up @@ -90,6 +96,10 @@ public struct OnboardingCore {
timerStartAction
)

case .tapStartButton:
state.selectCat = SelectCatCore.State()
return .run { send in await send(._timerEnd) }

case ._timerStart:
return .run { send in
for await _ in self.clock.timer(interval: .seconds(3)) {
Expand Down Expand Up @@ -122,7 +132,10 @@ public struct OnboardingCore {
state.currentItemID = state.fakedData[1].id.uuidString
return .none

default:
case .binding:
return .none

case .selectCat:
return .none
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,27 @@ let OnboardingItemsData: [OnboardingItem] = [
subTitle: "일정 시간 집중과 휴식을 반복해 번아웃을 방지하고\n짧은 시간의 몰입을 경험해보세요.")
]

public struct RandomAccessEquatableItems<Element>: RandomAccessCollection, Equatable where Element: Equatable {
Jihyun247 marked this conversation as resolved.
Show resolved Hide resolved
var elements: [Element]
public init(elements: [Element]) {
self.elements = elements
}

public var startIndex: Int { elements.startIndex }
public var endIndex: Int { elements.endIndex }

public subscript(position: Int) -> Element {
return elements[position]
}

public static func == (lhs: RandomAccessEquatableItems, rhs: RandomAccessEquatableItems) -> Bool {
guard lhs.count == rhs.count else { return false }
for (index, element) in lhs.enumerated() {
if element != rhs[index] {
return false
}
}
return true
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ public struct OnboardingView: View {
}

public var body: some View {
ZStack {
Alias.Color.Background.primary
.ignoresSafeArea()
NavigationStack {
VStack {
Spacer()
VStack(spacing: 0) {
Expand Down Expand Up @@ -54,15 +52,24 @@ public struct OnboardingView: View {
.padding(.vertical, 32)

Button(title: "시작하기") {
// 다음 뷰 이동
store.send(.tapStartButton)
}
.buttonStyle(.box(level: .primary, size: .large, width: .medium))
.padding(.top, 16)
}
Spacer()
}
.background {
Alias.Color.Background.primary
.ignoresSafeArea()
}
.onAppear { store.send(.onApear) }
.navigationDestination(
item: $store.scope(state: \.selectCat, action: \.selectCat)
) { store in
SelectCatView(store: store)
}
}
.onAppear { store.send(.onApear) }
.background {
GeometryReader { geometry in
Color.clear
Expand Down
Loading
Loading