-
Notifications
You must be signed in to change notification settings - Fork 0
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
[WEAV-74] 프로필 입력 - 회사 선택 #29
Changes from all commits
53f12f1
16c9038
054a52f
8a48ab8
d1ef483
204cec1
fac9ee6
f53583c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// | ||
// CompanySearchResponse.swift | ||
// CommonKit | ||
// | ||
// Created by 김지수 on 10/9/24. | ||
// Copyright © 2024 com.weave. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
public struct CompanySearchResponse { | ||
public let id: String | ||
public let name: String | ||
|
||
public init(id: String, name: String) { | ||
self.id = id | ||
self.name = name | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,45 @@ | ||||||
// | ||||||
// CompanyService.swift | ||||||
// CommonKit | ||||||
// | ||||||
// Created by 김지수 on 10/9/24. | ||||||
// Copyright © 2024 com.weave. All rights reserved. | ||||||
// | ||||||
|
||||||
import Foundation | ||||||
import OpenapiGenerated | ||||||
import Model | ||||||
|
||||||
//MARK: - Service Protocol | ||||||
public protocol CompanyServiceProtocol { | ||||||
func requestSearchCompany( | ||||||
keyword: String, | ||||||
next: String? | ||||||
) async throws -> ([CompanySearchResponse], String?) | ||||||
} | ||||||
|
||||||
//MARK: - Service | ||||||
public final class CompanyService { | ||||||
public static var shared = CompanyService() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 싱글톤 인스턴스를 'let'으로 선언하는 것이 좋습니다. 싱글톤 패턴에서는 인스턴스가 변경되지 않으므로 'var' 대신 'let'을 사용하여 불변성을 보장하는 것이 좋습니다. 다음과 같이 수정할 수 있습니다: -public static var shared = CompanyService()
+public static let shared = CompanyService() 📝 Committable suggestion
Suggested change
|
||||||
private init() {} | ||||||
} | ||||||
|
||||||
extension CompanyService: CompanyServiceProtocol { | ||||||
public func requestSearchCompany( | ||||||
keyword: String, | ||||||
next: String? = nil | ||||||
) async throws -> ([CompanySearchResponse], String?) { | ||||||
let response = try await client.searchCompanies( | ||||||
query: .init(name: keyword, next: next) | ||||||
).ok.body.json | ||||||
Comment on lines
+32
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'client' 인스턴스가 정의되지 않았습니다. 'client'를 사용하여 |
||||||
|
||||||
let result = response.companies.map { | ||||||
CompanySearchResponse( | ||||||
id: $0.id, | ||||||
name: $0.name | ||||||
) | ||||||
} | ||||||
let next = response.next | ||||||
return (result, next) | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// | ||
// CompanyServiceMock.swift | ||
// CommonKit | ||
// | ||
// Created by 김지수 on 10/10/24. | ||
// Copyright © 2024 com.weave. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import Model | ||
|
||
//MARK: - Service | ||
public final class CompanyServiceMock: CompanyServiceProtocol { | ||
|
||
public init() {} | ||
Comment on lines
+13
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 초기화 메서드를 개선하여 더 유연한 테스트를 가능하게 할 수 있습니다. 현재 초기화 메서드는 매개변수가 없어 고정된 동작만 가능합니다. 다음과 같이 개선하여 다양한 테스트 시나리오를 지원할 수 있습니다: public init(companies: [Model.CompanySearchResponse] = [], delay: TimeInterval = 0, shouldSimulateError: Bool = false) {
self.companies = companies
self.delay = delay
self.shouldSimulateError = shouldSimulateError
} 이렇게 하면 테스트 시 다양한 상황을 시뮬레이션할 수 있습니다. |
||
|
||
public func requestSearchCompany(keyword: String, next: String?) async throws -> ([Model.CompanySearchResponse], String?) { | ||
return ([ | ||
.init(id: "0", name: "현대글로비스"), | ||
.init(id: "1", name: "현대자동차"), | ||
.init(id: "2", name: "기아자동차"), | ||
.init(id: "3", name: "채널톡"), | ||
.init(id: "4", name: "닥터다이어리"), | ||
.init(id: "5", name: "컬쳐커넥션"), | ||
.init(id: "6", name: "엄청좋은회사"), | ||
.init(id: "7", name: "로지텍"), | ||
.init(id: "8", name: "스탠리"), | ||
.init(id: "9", name: "스타벅스"), | ||
.init(id: "10", name: "호날두주식회사"), | ||
.init(id: "11", name: "애플"), | ||
.init(id: "12", name: "엔비디아"), | ||
], nil) | ||
} | ||
Comment on lines
+17
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. requestSearchCompany 메서드의 구현을 개선해야 합니다. 현재 구현은 실제 검색 동작을 정확히 시뮬레이션하지 않습니다. 다음과 같은 개선사항을 제안합니다:
예시 구현: public func requestSearchCompany(keyword: String, next: String?) async throws -> ([Model.CompanySearchResponse], String?) {
if shouldSimulateError {
throw NSError(domain: "CompanyServiceMock", code: 0, userInfo: [NSLocalizedDescriptionKey: "Simulated network error"])
}
if delay > 0 {
try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
}
let filteredCompanies = companies.filter { $0.name.lowercased().contains(keyword.lowercased()) }
let pageSize = 10
let startIndex = Int(next ?? "0") ?? 0
let endIndex = min(startIndex + pageSize, filteredCompanies.count)
let nextPage = endIndex < filteredCompanies.count ? String(endIndex) : nil
return (Array(filteredCompanies[startIndex..<endIndex]), nextPage)
} 이 구현은 더 현실적인 검색 동작을 제공하며, 다양한 테스트 시나리오를 지원합니다. |
||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,123 @@ | ||||||||||
// | ||||||||||
// DropDownView.swift | ||||||||||
// DesignCore | ||||||||||
// | ||||||||||
// Created by 김지수 on 10/9/24. | ||||||||||
// Copyright © 2024 com.weave. All rights reserved. | ||||||||||
// | ||||||||||
|
||||||||||
import SwiftUI | ||||||||||
|
||||||||||
public protocol DropDownFetchable: Hashable, Equatable { | ||||||||||
var id: String { get } | ||||||||||
var name: String { get } | ||||||||||
} | ||||||||||
|
||||||||||
public struct DropDownPicker<Content: View>: View { | ||||||||||
|
||||||||||
@FocusState var showDropDown: Bool | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'FocusState' 대신 'Binding' 사용 필요 'FocusState'는 주로 키보드 포커스 관리에 사용되며, 현재 상황에서는 'Binding'을 사용하는 것이 적절합니다. 외부에서 전달된 'FocusState'를 내부에서 프로퍼티 래퍼 변수에 할당하는 것은 컴파일 오류를 유발할 수 있습니다. 다음과 같이 수정하는 것을 제안합니다: - @FocusState var showDropDown: Bool
+ @Binding var showDropDown: Bool
...
- showDropDown: FocusState<Bool>,
+ showDropDown: Binding<Bool>,
...
- self._showDropDown = showDropDown
+ self._showDropDown = showDropDown
... Also applies to: 52-52, 59-59 |
||||||||||
|
||||||||||
var tapHandler: ((Int) -> Void)? | ||||||||||
var content: () -> Content | ||||||||||
|
||||||||||
var dataSources: [any DropDownFetchable] | ||||||||||
let itemSize: CGFloat = 56 | ||||||||||
|
||||||||||
var nextPageHandler: (() -> Void)? | ||||||||||
var needCallNextPage = false | ||||||||||
|
||||||||||
var frameHeight: CGFloat { | ||||||||||
if !showDropDown { | ||||||||||
return 0 | ||||||||||
} | ||||||||||
|
||||||||||
if dataSources.count > 3 { | ||||||||||
return itemSize * 3 + itemSize * 0.5 | ||||||||||
} | ||||||||||
|
||||||||||
return itemSize * CGFloat(dataSources.count) | ||||||||||
} | ||||||||||
|
||||||||||
var frameOffset: CGFloat { | ||||||||||
if !showDropDown { | ||||||||||
return 0 | ||||||||||
} | ||||||||||
|
||||||||||
if dataSources.count > 3 { | ||||||||||
return 140 | ||||||||||
} | ||||||||||
|
||||||||||
return 125 - 28 * CGFloat(3 - dataSources.count) | ||||||||||
} | ||||||||||
|
||||||||||
public init( | ||||||||||
dataSources: [any DropDownFetchable], | ||||||||||
showDropDown: FocusState<Bool>, | ||||||||||
needCallNextPage: Bool = false, | ||||||||||
@ViewBuilder content: @escaping () -> Content, | ||||||||||
tapHandler: ((Int) -> Void)? = nil, | ||||||||||
nextPageHandler: (() -> Void)? = nil | ||||||||||
) { | ||||||||||
self.dataSources = dataSources | ||||||||||
self.content = content | ||||||||||
self.tapHandler = tapHandler | ||||||||||
self._showDropDown = showDropDown | ||||||||||
self.nextPageHandler = nextPageHandler | ||||||||||
self.needCallNextPage = needCallNextPage | ||||||||||
} | ||||||||||
|
||||||||||
public var body: some View { | ||||||||||
VStack { | ||||||||||
ZStack { | ||||||||||
ZStack { | ||||||||||
RoundedRectangle(cornerRadius: 24) | ||||||||||
.foregroundStyle(.white) // | ||||||||||
Comment on lines
+73
to
+74
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'foregroundStyle(.white)' 대신 'fill(Color.white)' 사용 권장 'RoundedRectangle'에 색상을 적용할 때는 'foregroundStyle'보다 'fill'을 사용하는 것이 적절합니다. 다음과 같이 수정하는 것을 제안합니다: RoundedRectangle(cornerRadius: 24)
- .foregroundStyle(.white)
+ .fill(Color.white) 📝 Committable suggestion
Suggested change
|
||||||||||
ScrollView { | ||||||||||
LazyVStack(alignment: .leading, spacing: 0) { | ||||||||||
ForEach(0 ..< dataSources.count, id: \.self) { index in | ||||||||||
let item = dataSources[index] | ||||||||||
Button(action: { | ||||||||||
tapHandler?(index) | ||||||||||
withAnimation { | ||||||||||
showDropDown.toggle() | ||||||||||
} | ||||||||||
}, label: { | ||||||||||
HStack(spacing: 16) { | ||||||||||
Text(item.name) | ||||||||||
.typography(.regular_14) | ||||||||||
.multilineTextAlignment(.leading) | ||||||||||
Spacer() | ||||||||||
} | ||||||||||
.foregroundStyle(DesignCore.Colors.grey500) | ||||||||||
.frame(height: itemSize) | ||||||||||
.padding(.horizontal, 16) | ||||||||||
.background(.white) | ||||||||||
}) | ||||||||||
} | ||||||||||
} | ||||||||||
.frame(maxWidth: .infinity, alignment: .leading) | ||||||||||
if needCallNextPage { | ||||||||||
ProgressView() | ||||||||||
.padding(.bottom, 10) | ||||||||||
.onAppear { | ||||||||||
nextPageHandler?() | ||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
.clipShape( | ||||||||||
RoundedRectangle(cornerRadius: 24) | ||||||||||
|
||||||||||
) | ||||||||||
.shadow(.default) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'shadow(.default)' 사용 오류 수정 필요 'shadow' 모디파이어에 '.default'를 사용하는 것은 컴파일 오류를 발생시킬 수 있습니다. 대신 적절한 형식의 'shadow'를 사용하세요. 다음과 같이 수정하는 것을 제안합니다: - .shadow(.default)
+ .shadow(radius: 4) 📝 Committable suggestion
Suggested change
|
||||||||||
.animation(.easeInOut(duration: 0.2), value: frameOffset) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. '.animation()' 모디파이어의 사용을 지양하세요 SwiftUI에서 다음과 같이 수정하는 것을 제안합니다: - .animation(.easeInOut(duration: 0.2), value: frameOffset) 필요한 곳에서 적절한 애니메이션을 적용해 주세요.
|
||||||||||
.frame(height: frameHeight) | ||||||||||
.offset(y: frameOffset) | ||||||||||
|
||||||||||
content() | ||||||||||
} | ||||||||||
.frame(height: 50) | ||||||||||
} | ||||||||||
.zIndex(999) | ||||||||||
} | ||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// MARK: 주석 형식을 수정해야 합니다.
SwiftLint에 따르면, 주석('//') 뒤에 최소한 하나의 공백이 필요하며, 'MARK' 주석은 '// MARK: ...' 또는 '// MARK: - ...' 형식을 따라야 합니다.
다음과 같이 수정할 수 있습니다:
Also applies to: 21-21
🧰 Tools
🪛 SwiftLint