Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4f85137
feat/#332: MLSDictionaryFeature 모ㅗ듈 생성
pinocchio22 May 19, 2026
31cb9f6
Revert "feat/#332: MLSDictionaryFeature 모ㅗ듈 생성"
pinocchio22 May 19, 2026
3662508
feat/#332: MLSDictionaryFeature 모듈 생성
pinocchio22 May 19, 2026
852ae71
feat/#332: DictionaryFeature 모듈 분리
pinocchio22 May 26, 2026
d01d670
test/332: 테스트 코드 작성
pinocchio22 May 27, 2026
35e2d22
style/#332: Apply SwiftLint autocorrect
github-actions[bot] May 27, 2026
8e418e3
refactor/#332: DictionaryDetailBaseViewController 콘텐츠 뷰 관리 구조 개선
pinocchio22 Jun 3, 2026
167a4f5
refactor/#332: 최근 검색어 저장 로직 지연 실행 구조로 개선
pinocchio22 Jun 3, 2026
92fd5d7
refactor/#332: 토큰 조회 시점을 구독 시점으로 변경
pinocchio22 Jun 3, 2026
a06021a
refactor/#332: 제미나이 코드리뷰 수정
pinocchio22 Jun 3, 2026
5bb766b
Merge branch 'feat/#332-MLSDictionaryFeature' of github.com:Team-Mapl…
pinocchio22 Jun 3, 2026
d9664e3
style/#332: Apply SwiftLint autocorrect
github-actions[bot] Jun 3, 2026
156afcb
fix/#332: navigateTo 오타 수정
pinocchio22 Jun 3, 2026
056d958
fix/#332: DictionaryDetailFactoryImpl 자기 자신 순환 참조 수정
pinocchio22 Jun 3, 2026
668f315
fix/#332: MonsterDictionaryDetailReactor itemTapped 인덱스 체크 추가
pinocchio22 Jun 3, 2026
616752a
fix/#332: LaunchScreen.storyboard 제거
pinocchio22 Jun 3, 2026
fc4c8d6
Merge branch 'feat/#332-MLSDictionaryFeature' of github.com:Team-Mapl…
pinocchio22 Jun 3, 2026
5d9044e
style/#332: Apply SwiftLint autocorrect
github-actions[bot] Jun 3, 2026
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
21 changes: 12 additions & 9 deletions MLS/Data/Data/Repository/UserDefaultsRepositoryImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,20 @@ public final class UserDefaultsRepositoryImpl: UserDefaultsRepository {
}

public func addRecentSearch(keyword: String) -> Completable {
return Completable.create { completable in
var current = UserDefaults.standard.stringArray(forKey: self.recentSearchkey) ?? []

// 중복 제거
current.removeAll(where: { $0 == keyword })
current.insert(keyword, at: 0)
let keyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines)

UserDefaults.standard.set(current, forKey: self.recentSearchkey)
completable(.completed)
return Disposables.create()
guard !keyword.isEmpty else {
return .empty()
}

var current = UserDefaults.standard.stringArray(forKey: recentSearchkey) ?? []

current.removeAll(where: { $0 == keyword })
current.insert(keyword, at: 0)

UserDefaults.standard.set(current, forKey: recentSearchkey)

return .empty()
}
Comment thread
pinocchio22 marked this conversation as resolved.

public func removeRecentSearch(keyword: String) -> Completable {
Expand Down
192 changes: 191 additions & 1 deletion MLS/MLS.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions MLS/MLS.xcworkspace/contents.xcworkspacedata

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

4 changes: 2 additions & 2 deletions MLS/MLSCore/Sources/MLSCore/ImageLoader/ImageLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public final class ImageLoader: @unchecked Sendable {

private init() {}

public func loadImage(stringURL: String?, defaultImage: UIImage? = nil, completion: @escaping @Sendable (UIImage?) -> Void) {
@MainActor public func loadImage(stringURL: String?, defaultImage: UIImage? = nil, completion: @MainActor @escaping @Sendable (UIImage?) -> Void) {
guard let stringURL,
let url = URL(string: stringURL),
["http", "https"].contains(url.scheme?.lowercased() ?? "")
Expand All @@ -52,7 +52,7 @@ public final class ImageLoader: @unchecked Sendable {
/// - stringURL: 이미지 URL 문자열
/// - defaultImage: 로드 실패 시 반환할 기본 이미지
/// - completion: 로드 완료 후 호출되는 클로저
public func loadImage(url: URL?, defaultImage: UIImage? = nil, completion: @escaping @Sendable (UIImage?) -> Void) {
@MainActor public func loadImage(url: URL?, defaultImage: UIImage? = nil, completion: @MainActor @escaping @Sendable (UIImage?) -> Void) {
loadImage(url: url) { result in
DispatchQueue.main.async {
switch result {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol DictionaryTabControllable: AnyObject {
func changeTab(index: Int)
}
11 changes: 11 additions & 0 deletions MLS/MLSCore/Sources/MLSCore/Navigation/DictionaryTabRegistry.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
public enum DictionaryTabRegistry {
private static weak var controller: DictionaryTabControllable?

public static func register(controller: DictionaryTabControllable) {
self.controller = controller
}

public static func changeTab(index: Int) {
controller?.changeTab(index: index)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ public extension CardList {
iconButton.isHidden = false
dropInfoStack.isHidden = true
badge.isHidden = true
rankContainer.isHidden = true
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import UIKit

import RxSwift
import SnapKit

public class CheckBoxButtonListSmallCell: UICollectionViewCell {

private let checkBoxButton: CheckBoxButton = {
let button = CheckBoxButton(style: .listSmall, mainTitle: nil, subTitle: nil)
button.isUserInteractionEnabled = false
return button
}()

private var disposeBag = DisposeBag()

override init(frame: CGRect) {
super.init(frame: frame)
addViews()
setupConstraints()
configureUI()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

public override var isSelected: Bool {
didSet {
checkBoxButton.isSelected = isSelected
}
}
}

// MARK: - SetUp
private extension CheckBoxButtonListSmallCell {
func addViews() {
contentView.addSubview(checkBoxButton)
}

func setupConstraints() {
checkBoxButton.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}

func configureUI() {
}
}

public extension CheckBoxButtonListSmallCell {
func inject(title: String?) {
checkBoxButton.mainTitle = title
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import UIKit

import SnapKit

public class PageTabbarCell: UICollectionViewCell {

private let titleLabel: UILabel = {
let label = UILabel()
label.font = .b_m_r
label.textColor = .neutral600
label.numberOfLines = 1
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.8
return label
}()

override init(frame: CGRect) {
super.init(frame: frame)
addViews()
setupConstraints()
configureUI()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

public override var isSelected: Bool {
didSet {
let font: UIFont? = isSelected ? .sub_m_b : .b_m_r
let textColor: UIColor? = isSelected ? .textColor : .neutral600
titleLabel.font = font
titleLabel.textColor = textColor
}
}
}

// MARK: - SetUp
private extension PageTabbarCell {
func addViews() {
contentView.addSubview(titleLabel)
}

func setupConstraints() {
titleLabel.snp.makeConstraints { make in
make.horizontalEdges.equalToSuperview()
make.centerY.equalToSuperview()
}
}

func configureUI() { }
}

public extension PageTabbarCell {
func inject(title: String?) {
titleLabel.text = title
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import UIKit

import SnapKit

public class TapButtonCell: UICollectionViewCell {

public let button: TapButton = {
let button = TapButton()
return button
}()

override init(frame: CGRect) {
super.init(frame: frame)
addViews()
setupConstraints()
configureUI()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

public override var isSelected: Bool {
didSet {
button.isSelected = isSelected
}
}
}

// MARK: - SetUp
private extension TapButtonCell {
func addViews() {
contentView.addSubview(button)
}

func setupConstraints() {
button.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}

func configureUI() { }
}

public extension TapButtonCell {
func inject(title: String?) {
button.text = title
button.isUserInteractionEnabled = false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ public enum DesignSystemAsset {

public static func image(named name: String) -> UIImage {
guard let image = UIImage(named: name, in: .module, compatibleWith: nil) else {
fatalError("❌ Image not found: \(name)")
}
return image
print("❌ Image not found: \(name)")
return UIImage()
}
return image
}
}

Expand Down
8 changes: 8 additions & 0 deletions MLS/MLSDictionaryFeature/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
86 changes: 86 additions & 0 deletions MLS/MLSDictionaryFeature/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// swift-tools-version: 6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "MLSDictionaryFeature",
platforms: [.iOS(.v15)],
products: [
.library(
name: "MLSDictionaryFeatureInterface",
targets: ["MLSDictionaryFeatureInterface"]
),
.library(
name: "MLSDictionaryFeature",
targets: ["MLSDictionaryFeature"]
),
.library(
name: "MLSDictionaryFeatureTesting",
targets: ["MLSDictionaryFeatureTesting"]
)
],
dependencies: [
.package(path: "../MLSAuthFeature"),
.package(path: "../MLSMyPageFeature"),
.package(path: "../MLSCore"),
.package(path: "../MLSDesignSystem"),
.package(url: "https://github.com/ReactorKit/ReactorKit.git", from: "3.2.0"),
.package(url: "https://github.com/ReactiveX/RxSwift.git", from: "6.7.0"),
.package(url: "https://github.com/RxSwiftCommunity/RxKeyboard.git", from: "2.0.0"),
.package(url: "https://github.com/SnapKit/SnapKit.git", from: "5.7.1")
],
targets: [
// Interface
.target(
name: "MLSDictionaryFeatureInterface",
dependencies: [
.product(name: "MLSCore", package: "MLSCore"),
.product(name: "MLSDesignSystem", package: "MLSDesignSystem"),
.product(name: "RxSwift", package: "RxSwift")
],
swiftSettings: [.swiftLanguageMode(.v5)]
),
// Feature
.target(
name: "MLSDictionaryFeature",
dependencies: [
"MLSDictionaryFeatureInterface",
.product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"),
.product(name: "MLSCore", package: "MLSCore"),
.product(name: "MLSDesignSystem", package: "MLSDesignSystem"),
.product(name: "MLSMyPageFeatureInterface", package: "MLSMyPageFeature"),
.product(name: "ReactorKit", package: "ReactorKit"),
.product(name: "RxSwift", package: "RxSwift"),
.product(name: "RxCocoa", package: "RxSwift"),
.product(name: "RxRelay", package: "RxSwift"),
.product(name: "RxKeyboard", package: "RxKeyboard"),
.product(name: "SnapKit", package: "SnapKit")
],
swiftSettings: [.swiftLanguageMode(.v5)]
),
// Mock
.target(
name: "MLSDictionaryFeatureTesting",
dependencies: [
"MLSDictionaryFeatureInterface",
.product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"),
.product(name: "MLSMyPageFeatureInterface", package: "MLSMyPageFeature"),
.product(name: "RxSwift", package: "RxSwift")
],
swiftSettings: [.swiftLanguageMode(.v5)]
),
// Tests
.testTarget(
name: "MLSDictionaryFeatureTests",
dependencies: [
"MLSDictionaryFeature",
"MLSDictionaryFeatureInterface",
"MLSDictionaryFeatureTesting",
.product(name: "MLSAuthFeatureInterface", package: "MLSAuthFeature"),
.product(name: "MLSAuthFeatureTesting", package: "MLSAuthFeature"),
.product(name: "RxBlocking", package: "RxSwift")
],
)
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
public struct DictionaryAllDTO: DictionaryDTOProtocol {
public let originalId: Int
public let name: String
public let imageUrl: String?
public let level: Int?
public let type: String
public let bookmarkId: Int?
public var id: Int { originalId }

public init(originalId: Int, name: String, imageUrl: String?, level: Int?, type: String, bookmarkId: Int?) {
self.originalId = originalId
self.name = name
self.imageUrl = imageUrl
self.level = level
self.type = type
self.bookmarkId = bookmarkId
}
}
Loading