diff --git a/iOS/.swiftlint.yml b/iOS/.swiftlint.yml index 57a62cf..3a9fefa 100644 --- a/iOS/.swiftlint.yml +++ b/iOS/.swiftlint.yml @@ -26,3 +26,9 @@ type_name: max_length: warning: 40 error: 1000 + +# 중첩 타입 +nesting: + type_level: + warning: 3 + error: 4 diff --git a/iOS/Features/RewindJourney/Sources/RewindJourneyView/MSMusicView.swift b/iOS/Features/RewindJourney/Sources/RewindJourneyView/MSMusicView.swift new file mode 100644 index 0000000..d202e20 --- /dev/null +++ b/iOS/Features/RewindJourney/Sources/RewindJourneyView/MSMusicView.swift @@ -0,0 +1,183 @@ +// +// MSMusicView.swift +// RewindJourney +// +// Created by 전민건 on 11/22/23. +// + +import UIKit + +import MSDesignSystem +import MSLogger +import MSUIKit + +final class MSMusicView: UIProgressView { + + // MARK: - Constants + + private enum Metric { + + static let verticalInset: CGFloat = 8.0 + static let horizonalInset: CGFloat = 12.0 + static let cornerRadius: CGFloat = 8.0 + + // albumart view + enum AlbumArtView { + static let height: CGFloat = 52.0 + static let width: CGFloat = 52.0 + } + // title view + enum TitleView { + static let height: CGFloat = 4.0 + static let inset: CGFloat = 4.0 + static let titleHight: CGFloat = 24.0 + static let subTitleHight: CGFloat = 20.0 + } + + // playtime view + enum PlayTimeView { + static let width: CGFloat = 67.0 + static let verticalInset: CGFloat = 22.0 + static let horizonalInset: CGFloat = 4.0 + static let conponentsHeight: CGFloat = 24.0 + } + + } + + private enum Default { + + // titleView + enum TitleView { + static let title: String = "Attention" + static let subTitle: String = "NewJeans" + static let defaultIndex: Int = 0 + } + + // stackView + enum PlayTime { + static let time: String = "00 : 00" + } + + } + + // MARK: - UI Components + + private let albumArtView = UIImageView() + public var albumArtImage: UIImage? { + didSet { + self.albumArtView.image = albumArtImage + } + } + private let titleStackView = UIStackView() + private let titleLabel = UILabel() + private let subTitleLabel = UILabel() + private let playTimeStackView = UIStackView() + private let playTimeLabel = UILabel() + private let playTimeIconView = UIImageView(image: .msIcon(.voice)) + + // MARK: - UI Configuration + + public func configure() { + self.configureLayout() + self.configureStyle() + } + + // MARK: - UI Configuration: layout + + private func configureLayout() { + self.configureAlbumArtViewLayout() + self.configurePlayTimeViewLayout() + self.configureTitleViewLayout() + } + + private func configureAlbumArtViewLayout() { + self.addSubview(self.albumArtView) + self.albumArtView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + self.albumArtView.topAnchor.constraint(equalTo: self.topAnchor, constant: Metric.verticalInset), + self.albumArtView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -Metric.verticalInset), + self.albumArtView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: Metric.horizonalInset), + self.albumArtView.widthAnchor.constraint(equalToConstant: Metric.AlbumArtView.width) + ]) + } + + private func configureTitleViewLayout() { + self.addSubview(self.titleStackView) + self.titleStackView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + self.titleStackView.topAnchor.constraint(equalTo: self.topAnchor, + constant: Metric.verticalInset), + self.titleStackView.bottomAnchor.constraint(equalTo: self.bottomAnchor, + constant: -Metric.verticalInset), + self.titleStackView.leadingAnchor.constraint(equalTo: self.albumArtView.trailingAnchor, + constant: Metric.horizonalInset), + self.titleStackView.trailingAnchor.constraint(equalTo: self.playTimeStackView.leadingAnchor, + constant: -Metric.horizonalInset) + ]) + + self.titleStackView.addArrangedSubview(self.titleLabel) + self.titleStackView.addArrangedSubview(self.subTitleLabel) + + self.titleStackView.axis = .vertical + self.titleStackView.spacing = Metric.TitleView.inset + } + + private func configurePlayTimeViewLayout() { + self.addSubview(self.playTimeStackView) + self.playTimeStackView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + self.playTimeStackView.topAnchor.constraint(equalTo: self.topAnchor, + constant: Metric.PlayTimeView.verticalInset), + self.playTimeStackView.bottomAnchor.constraint(equalTo: self.bottomAnchor, + constant: -Metric.PlayTimeView.verticalInset), + self.playTimeStackView.widthAnchor.constraint(equalToConstant: Metric.PlayTimeView.width), + self.playTimeStackView.trailingAnchor.constraint(equalTo: self.trailingAnchor, + constant: -Metric.horizonalInset) + ]) + + self.playTimeStackView.addArrangedSubview(self.playTimeIconView) + self.playTimeStackView.addArrangedSubview(self.playTimeLabel) + + self.playTimeStackView.axis = .horizontal + self.playTimeStackView.spacing = Metric.PlayTimeView.horizonalInset + self.titleStackView.distribution = .fill + } + + // MARK: - UI Configuration: style + + private func configureStyle() { + self.clipsToBounds = true + self.layer.cornerRadius = Metric.cornerRadius + self.trackTintColor = .msColor(.secondaryBackground) + self.progressTintColor = .msColor(.musicSpot) + self.progress = 0.4 + + self.configureTitleViewStyle() + self.configurePlayTimeViewStyle() + } + + private func configureTitleViewStyle() { + self.titleLabel.text = Default.TitleView.title + self.titleLabel.font = .msFont(.buttonTitle) + self.subTitleLabel.text = Default.TitleView.subTitle + self.subTitleLabel.font = .msFont(.paragraph) + } + + private func configurePlayTimeViewStyle() { + self.playTimeLabel.text = Default.PlayTime.time + self.playTimeLabel.font = .msFont(.caption) + self.playTimeLabel.textColor = .msColor(.secondaryTypo) + self.playTimeIconView.tintColor = .msColor(.secondaryTypo) + } + +} + +// MARK: - Preview + +@available(iOS 17, *) +#Preview { + MSFont.registerFonts() + let musicView = MSMusicView() + musicView.albumArtImage = UIImage(systemName: "pencil") + return musicView +} diff --git a/iOS/Features/RewindJourney/Sources/RewindJourneyView/MSProgressView.swift b/iOS/Features/RewindJourney/Sources/RewindJourneyView/MSProgressView.swift new file mode 100644 index 0000000..a56216e --- /dev/null +++ b/iOS/Features/RewindJourney/Sources/RewindJourneyView/MSProgressView.swift @@ -0,0 +1,49 @@ +// +// MSProgressView.swift +// RewindJourney +// +// Created by 전민건 on 11/22/23. +// + +import UIKit + +public final class MSProgressView: UIProgressView { + + // MARK: - Properties + + internal var percentage: Float = 0.0 { + didSet { + syncProgress(percentage: percentage) + } + } + internal var isHighlighted: Bool = false { + didSet { + self.percentage = self.isHighlighted ? 1.0 : 0.0 + } + } + + // MARK: - Configure + + private func configureColor() { + self.trackTintColor = .systemGray6 + self.tintColor = .white + } + + // MARK: - Initializer + + public override init(frame: CGRect) { + super.init(frame: frame) + self.configureColor() + } + + required init?(coder: NSCoder) { + fatalError("MusicSpot은 code-based 로 개발되었습니다.") + } + + // MARK: - Functions: change progress + + private func syncProgress(percentage: Float) { + self.progress = percentage + } + +} diff --git a/iOS/Features/RewindJourney/Sources/RewindJourneyView/RewindJourneyViewController.swift b/iOS/Features/RewindJourney/Sources/RewindJourneyView/RewindJourneyViewController.swift new file mode 100644 index 0000000..ac653d0 --- /dev/null +++ b/iOS/Features/RewindJourney/Sources/RewindJourneyView/RewindJourneyViewController.swift @@ -0,0 +1,244 @@ +// +// RewindJourneyViewController.swift +// RewindJourney +// +// Created by 전민건 on 11/22/23. +// + +import UIKit + +import MSDesignSystem + +public final class RewindJourneyViewController: UIViewController { + + // MARK: - Constants + + private enum Metric { + + // progress bar + enum Progressbar { + + static let height: CGFloat = 4.0 + static let inset: CGFloat = 4.0 + static let defaultIndex: Int = 0 + + } + + // stackView + enum StackView { + + static let inset: CGFloat = 12.0 + + } + + // musicView + enum MusicView { + + static let height: CGFloat = 69.0 + static let inset: CGFloat = 12.0 + static let bottomInset: CGFloat = 34.0 + + } + + } + + // MARK: - Properties + + private let stackView = UIStackView() + private let presentImageView = UIImageView() + private let musicView = MSMusicView() + private var progressViews: [MSProgressView]? + private var preHighlightenProgressView: MSProgressView? + private let leftTouchView = UIButton() + private let rightTouchView = UIButton() + + public var images: [UIImage]? + private var presentImageIndex: Int? { + didSet { + self.changeProgressViews() + } + } + + // MARK: - UI Configuration + + func configure() { + self.configureLayout() + self.configureStyle() + self.configureAction() + + self.musicView.configure() + } + + // MARK: - UI Configuration: Layout + + private func configureLayout() { + self.configurePresentImageViewLayout() + self.configureStackViewLayout() + self.configureProgressbarsLayout() + self.configureMusicViewLayout() + self.configureTouchViewLayout() + } + + private func configurePresentImageViewLayout() { + self.view.addSubview(self.presentImageView) + + self.presentImageView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + self.presentImageView.topAnchor.constraint(equalTo: self.view.topAnchor), + self.presentImageView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), + self.presentImageView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), + self.presentImageView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor) + ]) + } + + private func configureStackViewLayout() { + self.view.addSubview(self.stackView) + + self.stackView.axis = .horizontal + self.stackView.spacing = Metric.Progressbar.inset + self.stackView.distribution = .fillEqually + + self.stackView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + self.stackView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor), + self.stackView.heightAnchor.constraint(equalToConstant: Metric.Progressbar.height), + self.stackView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, + constant: Metric.StackView.inset), + self.stackView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, + constant: -Metric.StackView.inset)]) + } + + private func configureProgressbarsLayout() { + guard let images else { return } + var views = [MSProgressView]() + images.forEach {_ in + let progressView = MSProgressView() + views.append(progressView) + stackView.addArrangedSubview(progressView) + } + + self.progressViews = views + } + + private func configureMusicViewLayout() { + self.view.addSubview(self.musicView) + + self.musicView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + self.musicView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, + constant: -Metric.MusicView.bottomInset), + self.musicView.heightAnchor.constraint(equalToConstant: Metric.MusicView.height), + self.musicView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, + constant: Metric.MusicView.inset), + self.musicView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, + constant: -Metric.MusicView.inset) + ]) + } + + private func configureTouchViewLayout() { + self.view.addSubview(self.leftTouchView) + self.view.addSubview(self.rightTouchView) + + self.leftTouchView.translatesAutoresizingMaskIntoConstraints = false + self.rightTouchView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + self.leftTouchView.topAnchor.constraint(equalTo: self.view.topAnchor), + self.rightTouchView.topAnchor.constraint(equalTo: self.view.topAnchor), + self.leftTouchView.bottomAnchor.constraint(equalTo: self.musicView.topAnchor), + self.rightTouchView.bottomAnchor.constraint(equalTo: self.musicView.topAnchor), + self.leftTouchView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), + self.rightTouchView.leadingAnchor.constraint(equalTo: self.view.centerXAnchor), + self.leftTouchView.trailingAnchor.constraint(equalTo: self.view.centerXAnchor), + self.rightTouchView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor) + ]) + } + + // MARK: - UI Configuration: Style + + private func configureStyle() { + self.configurePresentImageViewStyle() + self.configureProgressbarsStyle() + } + + private func configurePresentImageViewStyle() { + self.presentImageView.contentMode = .scaleAspectFit + } + + private func configureProgressbarsStyle() { + self.presentImageIndex = Metric.Progressbar.defaultIndex + } + + // MARK: - Configuration: Action + + private func configureAction() { + self.configureLeftTouchViewAction() + self.configureRightTouchViewAction() + } + + private func configureLeftTouchViewAction() { + self.leftTouchView.addTarget(self, action: #selector(leftTouchViewTapped), for: .touchUpInside) + } + + private func configureRightTouchViewAction() { + self.rightTouchView.addTarget(self, action: #selector(rightTouchViewTapped), for: .touchUpInside) + } + + private func changeProgressViews() { + guard let presentIndex = self.presentImageIndex, + let images else { return } + + self.presentImageView.image = images[presentIndex] + self.preHighlightenProgressView = self.progressViews?[presentIndex] + self.preHighlightenProgressView?.isHighlighted = false + + let minIndex = 0 + let maxIndex = images.count - 1 + + for index in minIndex...maxIndex { + self.progressViews?[index].isHighlighted = index <= presentIndex ? true : false + } + } + + // MARK: - Life Cycle + + public override func viewDidLoad() { + super.viewDidLoad() + + self.configure() + } + + // MARK: - Actions + + @objc private func leftTouchViewTapped() { + guard let presentImageIndex else { + return + } + if presentImageIndex > 0 { + let index = presentImageIndex - 1 + self.presentImageIndex = index + } + } + + @objc private func rightTouchViewTapped() { + guard let images, let presentImageIndex else { + return + } + if presentImageIndex < images.count - 1 { + let index = presentImageIndex + 1 + self.presentImageIndex = index + } + } + +} + +// MARK: - Preview + +@available(iOS 17, *) +#Preview { + MSFont.registerFonts() + let viewController = RewindJourneyViewController() + viewController.images = [UIImage(systemName: "pencil")!, + UIImage(systemName: "pencil")!, + UIImage(systemName: "pencil")!] + return viewController +} diff --git a/iOS/Features/Spot/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/iOS/Features/Spot/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/iOS/Features/Spot/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/iOS/MSCoreKit/Sources/MSNetworking/APIURL.swift b/iOS/MSCoreKit/Sources/MSNetworking/APIURL.swift index 66f3035..5fa985a 100644 --- a/iOS/MSCoreKit/Sources/MSNetworking/APIURL.swift +++ b/iOS/MSCoreKit/Sources/MSNetworking/APIURL.swift @@ -8,13 +8,11 @@ import Foundation enum APIbaseURL: String { - - //base URL 추가 + // base URL 추가 case none = "" } enum APIpathURL: String { - - //path URL 추가 + // path URL 추가 case none = "" } diff --git a/iOS/MSCoreKit/Sources/MSNetworking/MSRouter.swift b/iOS/MSCoreKit/Sources/MSNetworking/MSRouter.swift new file mode 100644 index 0000000..787d47b --- /dev/null +++ b/iOS/MSCoreKit/Sources/MSNetworking/MSRouter.swift @@ -0,0 +1,42 @@ +// +// MSRouter.swift +// MSCoreKit +// +// Created by 전민건 on 11/16/23. +// + +import Foundation + +public struct RouterType { + + private var encodable: Encodable? + + // 기능별 MSRouter + public var getJourney: MSRouter { + MSRouter(baseURL: .none, pathURL: .none, method: .get, body: HTTPBody(content: encodable)) + } + public var getPerson: MSRouter { + MSRouter(baseURL: .none, pathURL: .none, method: .get, body: HTTPBody(content: encodable)) + } + +} + +public struct MSRouter: Router { + + let baseURL: APIbaseURL + let pathURL: APIpathURL + let method: HTTPMethod + var body: HTTPBody + + func asURLRequest() -> URLRequest? { + guard let url = URL(string: baseURL.rawValue + pathURL.rawValue) else { + return nil + } + var request = URLRequest(url: url) + request.httpMethod = method.rawValue + request.httpBody = body.contentToData() + + return request + } + +} diff --git a/iOS/MSFoundation/Tests/MSLoggerTests/MSLoggerTests.swift b/iOS/MSFoundation/Tests/MSLoggerTests/MSLoggerTests.swift index b01caba..48d5b00 100644 --- a/iOS/MSFoundation/Tests/MSLoggerTests/MSLoggerTests.swift +++ b/iOS/MSFoundation/Tests/MSLoggerTests/MSLoggerTests.swift @@ -11,10 +11,10 @@ import MSLogger final class MSLoggerTests: XCTestCase { func test_Logger객체_잘_생성되는지_성공() { - //arrange, act + // arrange, act let logger = MSLogger.make(category: .login) - //assert + // assert XCTAssertNotNil(logger) } } diff --git a/iOS/MSUIKit/Sources/MSUIKit/MSUIComponents.swift b/iOS/MSUIKit/Sources/MSUIKit/MSUIComponents.swift new file mode 100644 index 0000000..dc701bb --- /dev/null +++ b/iOS/MSUIKit/Sources/MSUIKit/MSUIComponents.swift @@ -0,0 +1,8 @@ +// +// MSUIComponents.swift +// MSUIKit +// +// Created by 이창준 on 11/14/23. +// + +import UIKit diff --git a/iOS/commit b/iOS/commit index 2355a98..8eac4c3 100755 --- a/iOS/commit +++ b/iOS/commit @@ -1,13 +1,16 @@ #!/bin/sh - +​ LINTPATH='.swiftlint.yml' declare -a PATHS=("MSCoreKit" "MSFoundation" "MSUIKit" "MSData" "MusicSpot" "Features") failures="" - +​ for path in "${PATHS[@]}"; do if [ -d "$path" ]; then echo "👀 Running SwiftLint for $path" result=$(swiftlint lint --progress --config $LINTPATH $path) + if [[ "$result" == *warning* ]]; then + echo "🚧 Lint Warning: \n$result" + fi if [[ ! "$result" == *error* ]]; then echo "✅ Lint succeed for $path\n" else @@ -19,11 +22,11 @@ for path in "${PATHS[@]}"; do exit 1 fi done - +​ if [ ! -z "$failures" ]; then echo "$failures" exit 1 else echo "✨ All linting checks passed. Ready to commit." gitmoji -c -fi +fi \ No newline at end of file