diff --git a/.DS_Store b/.DS_Store
deleted file mode 100644
index cc92e93..0000000
Binary files a/.DS_Store and /dev/null differ
diff --git a/.github/workflows/Xcode_build_test.yml b/.github/workflows/Xcode_build_test.yml
new file mode 100644
index 0000000..d61884a
--- /dev/null
+++ b/.github/workflows/Xcode_build_test.yml
@@ -0,0 +1,128 @@
+name: Xcode_build_test
+
+env:
+ PACKAGES_JSON: '["MSCoreKit", "MSFoundation", "MSUIKit"]'
+
+on:
+ pull_request:
+ branches:
+ - 'iOS/release'
+ - 'iOS/epic/**'
+ types: [assigned, labeled, opened, synchronize, reopened]
+
+jobs:
+ prepare-matrix:
+ runs-on: macos-13
+ outputs:
+ matrix: ${{ steps.set-matrix.outputs.matrix }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Xcode
+ if: ${{ !env.ACT }}
+ uses: maxim-lobanov/setup-xcode@v1
+ with:
+ xcode-version: '15.0.1'
+
+ - name: Install jq
+ run: brew install jq
+
+ - name: Generate matrix
+ id: set-matrix
+ run: |
+ cd iOS
+ matrix="{\"include\":["
+ packages=$(echo $PACKAGES_JSON | jq -r '.[]')
+ first_entry=true
+ for package in $packages; do
+ cd $package
+ for scheme in $(xcodebuild -list | grep -E '^[[:space:]]*Schemes:' -A 10 | tail -n +2 | grep -v '^$'); do
+ if [[ $scheme != *"-Package" ]] && [[ $scheme != *"Tests" ]]; then
+ if [ "$first_entry" = true ]; then
+ first_entry=false
+ else
+ matrix+=","
+ fi
+ matrix+="{\"package\":\"$package\", \"scheme\":\"$scheme\"}"
+ fi
+ done
+ cd ..
+ done
+ # cd Features
+ # for package in JourneyList SaveJourney; do
+ # cd $package
+ # for scheme in $(xcodebuild -list | grep -E '^[[:space:]]*Schemes:' -A 10 | tail -n +2 | grep -v '^$'); do
+ # if [[ $scheme != *"-Package" ]]; then
+ # if [ "$first_entry" = true ]; then
+ # first_entry=false
+ # else
+ # matrix+=","
+ # fi
+ # matrix+="{\"package\":\"$package\", \"scheme\":\"$scheme\"}"
+ # fi
+ # done
+ # cd ..
+ # done
+ matrix+="]}"
+ echo "matrix=$matrix" >> $GITHUB_OUTPUT
+
+ xcode-build:
+ needs: prepare-matrix
+ runs-on: macos-13
+ strategy:
+ fail-fast: false
+ matrix: ${{fromJson(needs.prepare-matrix.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Xcode
+ if: ${{ !env.ACT }}
+ uses: maxim-lobanov/setup-xcode@v1
+ with:
+ xcode-version: '15.0.1'
+
+ - name: ๐ ๏ธ Build ${{ matrix.scheme }}
+ if: ${{ !contains(matrix.scheme, 'Tests') }}
+ run: |
+ echo "๐ ๏ธ Building ${{ matrix.package }} - Scheme: ${{ matrix.scheme }}"
+ cd iOS/${{ matrix.package }}
+ xcodebuild \
+ -scheme ${{ matrix.scheme }} \
+ -sdk 'iphonesimulator' \
+ -destination 'platform=iOS Simulator,OS=17.0.1,name=iPhone 15 Pro' \
+ clean build
+
+ prepare-test-matrix:
+ runs-on: macos-13
+ outputs:
+ matrix: ${{ steps.set-matrix.outputs.matrix }}
+ steps:
+ - id: set-matrix
+ run: |
+ matrix="{\"package\": $PACKAGES_JSON}"
+ echo "matrix=$matrix" >> $GITHUB_OUTPUT
+
+ xcode-test:
+ needs: prepare-test-matrix
+ runs-on: macos-13
+ strategy:
+ fail-fast: false
+ matrix: ${{fromJson(needs.prepare-test-matrix.outputs.matrix)}}
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Xcode
+ if: ${{ !env.ACT }}
+ uses: maxim-lobanov/setup-xcode@v1
+ with:
+ xcode-version: '15.0.1'
+
+ - name: ๐งช Test ${{ matrix.package }}
+ run: |
+ echo "๐งช Testing ${{ matrix.package }}"
+ cd iOS/${{ matrix.package }}
+ xcodebuild \
+ -scheme ${{ matrix.package }}-Package \
+ -sdk 'iphonesimulator' \
+ -destination 'platform=iOS Simulator,OS=17.0.1,name=iPhone 15 Pro' \
+ clean test
diff --git a/iOS/Features/JourneyList/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/iOS/Features/JourneyList/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/iOS/Features/JourneyList/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/iOS/Features/SaveJourney/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/iOS/Features/SaveJourney/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/iOS/Features/SaveJourney/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSCacheStorage.xcscheme b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSCacheStorage.xcscheme
new file mode 100644
index 0000000..1113d29
--- /dev/null
+++ b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSCacheStorage.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSCacheStorageTests.xcscheme b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSCacheStorageTests.xcscheme
new file mode 100644
index 0000000..326b091
--- /dev/null
+++ b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSCacheStorageTests.xcscheme
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOS/MSFoundation/.swiftpm/xcode/xcshareddata/xcschemes/MSFoundation.xcscheme b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSFetcher.xcscheme
similarity index 88%
rename from iOS/MSFoundation/.swiftpm/xcode/xcshareddata/xcschemes/MSFoundation.xcscheme
rename to iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSFetcher.xcscheme
index b683dc0..1e30232 100644
--- a/iOS/MSFoundation/.swiftpm/xcode/xcshareddata/xcschemes/MSFoundation.xcscheme
+++ b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSFetcher.xcscheme
@@ -14,9 +14,9 @@
buildForAnalyzing = "YES">
@@ -49,9 +49,9 @@
diff --git a/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSFetcherTests.xcscheme b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSFetcherTests.xcscheme
new file mode 100644
index 0000000..f299dc2
--- /dev/null
+++ b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSFetcherTests.xcscheme
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSNetworking.xcscheme b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSNetworking.xcscheme
new file mode 100644
index 0000000..f6fba4e
--- /dev/null
+++ b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSNetworking.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSNetworkingTests.xcscheme b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSNetworkingTests.xcscheme
new file mode 100644
index 0000000..5e9ef94
--- /dev/null
+++ b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSNetworkingTests.xcscheme
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSPersistentStorage.xcscheme b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSPersistentStorage.xcscheme
new file mode 100644
index 0000000..273d1d1
--- /dev/null
+++ b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSPersistentStorage.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSPersistentStorageTests.xcscheme b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSPersistentStorageTests.xcscheme
new file mode 100644
index 0000000..e9cfe80
--- /dev/null
+++ b/iOS/MSCoreKit/.swiftpm/xcode/xcshareddata/xcschemes/MSPersistentStorageTests.xcscheme
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOS/MSCoreKit/Package.swift b/iOS/MSCoreKit/Package.swift
index 2317ccd..e044c96 100644
--- a/iOS/MSCoreKit/Package.swift
+++ b/iOS/MSCoreKit/Package.swift
@@ -53,13 +53,13 @@ let package = Package(
// Tests
.testTarget(name: .persistentStorage.testTarget,
- dependencies: [.target(name: .persistentStorage)]),
+ dependencies: ["MSPersistentStorage"]),
.testTarget(name: .networking.testTarget,
- dependencies: [.target(name: .networking)]),
+ dependencies: ["MSNetworking"]),
.testTarget(name: .fetcher.testTarget,
- dependencies: [.target(name: .fetcher)]),
+ dependencies: ["MSFetcher"]),
.testTarget(name: .cache.testTarget,
- dependencies: [.target(name: .cache)])
+ dependencies: ["MSCacheStorage"])
],
swiftLanguageVersions: [.v5]
)
diff --git a/iOS/MSCoreKit/Tests/MSNetworkingTests/MSNetworkingTests.swift b/iOS/MSCoreKit/Tests/MSNetworkingTests/MSNetworkingTests.swift
index 7016b29..0f8e587 100644
--- a/iOS/MSCoreKit/Tests/MSNetworkingTests/MSNetworkingTests.swift
+++ b/iOS/MSCoreKit/Tests/MSNetworkingTests/MSNetworkingTests.swift
@@ -14,10 +14,10 @@ final class MSNetworkingTests: XCTestCase {
private var subscriber: Set = []
func test_get_JourneyDTO_์ฑ๊ณต() {
- //Arrange
+ // Arrange
let getJourneyRouter = MockRouterType().getJourney
- //Act
+ // Act
mockNetworking.request(mockRouter: getJourneyRouter, type: JourneyDTO.self)
.sink { response in
switch response {
@@ -26,10 +26,10 @@ final class MSNetworkingTests: XCTestCase {
default:
return
}
- } receiveValue: { JourneyDTO in
+ } receiveValue: { journeyDTO in
- //Assert
- XCTAssertNotNil(JourneyDTO)
+ // Assert
+ XCTAssertNotNil(journeyDTO)
}
.store(in: &subscriber)
}
diff --git a/iOS/MSCoreKit/Tests/MSNetworkingTests/Mock/MockMSNetworking.swift b/iOS/MSCoreKit/Tests/MSNetworkingTests/Mock/MockMSNetworking.swift
index cf6bd34..fd3dc61 100644
--- a/iOS/MSCoreKit/Tests/MSNetworkingTests/Mock/MockMSNetworking.swift
+++ b/iOS/MSCoreKit/Tests/MSNetworkingTests/Mock/MockMSNetworking.swift
@@ -13,9 +13,9 @@ import Combine
public class MockMSNetworking {
var expectedSuccessJourneyGetPublisher: Result.Publisher {
- let journey = JourneyDTO(JourneyIdentifier: UUID(),
+ let journey = JourneyDTO(journeyIdentifier: UUID(),
title: "",
- metaData: JourneyMetadataDTO(date: Date()),
+ metaData: JourneyMetadataDTO(date: .now),
spots: [],
coordinates: [],
song: nil,
@@ -24,10 +24,10 @@ public class MockMSNetworking {
guard let jsonData = try? JSONEncoder().encode(journey) else {
return Just(Data()).setFailureType(to: Error.self)
}
-
+
let request = Just(jsonData)
.setFailureType(to: Error.self)
-
+
return request
}
@@ -41,11 +41,10 @@ public class MockMSNetworking {
}
return publisher.tryMap { result -> T in
- let value = try JSONDecoder().decode(T.self, from: result)
- return value
- }
- .eraseToAnyPublisher()
+ let value = try JSONDecoder().decode(T.self, from: result)
+ return value
+ }
+ .eraseToAnyPublisher()
}
}
-
diff --git a/iOS/MSFoundation/.swiftpm/xcode/xcshareddata/xcschemes/MSFoundation-Package.xcscheme b/iOS/MSFoundation/.swiftpm/xcode/xcshareddata/xcschemes/MSFoundation-Package.xcscheme
deleted file mode 100644
index 38b856f..0000000
--- a/iOS/MSFoundation/.swiftpm/xcode/xcshareddata/xcschemes/MSFoundation-Package.xcscheme
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/iOS/MSUIKit/Package.swift b/iOS/MSUIKit/Package.swift
index a29e434..0e5acca 100644
--- a/iOS/MSUIKit/Package.swift
+++ b/iOS/MSUIKit/Package.swift
@@ -36,9 +36,11 @@ let package = Package(
.process("../MSDesignSystem/Resources")
]),
.target(name: .uiKit,
- dependencies: [.target(name: .designSystem)])
+ dependencies: ["MSDesignSystem"]),
// Tests
+ .testTarget(name: .designSystem.testTarget,
+ dependencies: ["MSDesignSystem"])
],
swiftLanguageVersions: [.v5]
)
diff --git a/iOS/MSUIKit/Sources/MSUIKit/BaseViewController.swift b/iOS/MSUIKit/Sources/MSUIKit/BaseViewController.swift
new file mode 100644
index 0000000..aabfba3
--- /dev/null
+++ b/iOS/MSUIKit/Sources/MSUIKit/BaseViewController.swift
@@ -0,0 +1,37 @@
+//
+// BaseViewController.swift
+// MSUIKit
+//
+// Created by ์ด์ฐฝ์ค on 11/23/23.
+//
+
+import UIKit
+
+import MSDesignSystem
+
+open class BaseViewController: UIViewController {
+
+ open override func viewDidLoad() {
+ super.viewDidLoad()
+ self.configureSafeArea()
+ self.configureStyle()
+ self.configureLayout()
+ }
+
+ /// CornerRadius, ์์ ๋ฑ์ ๋ณ๊ฒฝ์ ํฌํจํฉ๋๋ค.
+ open func configureStyle() {
+ self.view.backgroundColor = .msColor(.primaryBackground)
+ }
+
+ /// addSubView, Auto Layout ๋ฑ์ ๋ ์ด์์ ์ค์ ์ ํฌํจํฉ๋๋ค.
+ open func configureLayout() { }
+
+ private func configureSafeArea() {
+ let safeAreaInsets = UIEdgeInsets(top: 24.0,
+ left: 16.0,
+ bottom: 24.0,
+ right: 16.0)
+ self.additionalSafeAreaInsets = safeAreaInsets
+ }
+
+}
diff --git a/iOS/MSUIKit/Sources/MSUIKit/MSBottomSheetViewController.swift b/iOS/MSUIKit/Sources/MSUIKit/MSBottomSheetViewController.swift
new file mode 100644
index 0000000..5662940
--- /dev/null
+++ b/iOS/MSUIKit/Sources/MSUIKit/MSBottomSheetViewController.swift
@@ -0,0 +1,277 @@
+//
+// MSUIComponents.swift
+// MSUIKit
+//
+// Created by ์ด์ฐฝ์ค on 11/14/23.
+//
+
+import UIKit
+
+import MSDesignSystem
+
+open class MSBottomSheetViewController
+: UIViewController, UIGestureRecognizerDelegate {
+
+ // MARK: - Configuration
+
+ public struct BottomSheetConfiguration {
+ let fullHeight: CGFloat
+ let detentHeight: CGFloat
+ let minimizedHeight: CGFloat
+
+ var fullDetentDiff: CGFloat {
+ return self.fullHeight - self.detentHeight
+ }
+
+ var detentMinimizedDiff: CGFloat {
+ return self.detentHeight - self.minimizedHeight
+ }
+
+ public init(fullHeight: CGFloat,
+ detentHeight: CGFloat,
+ minimizedHeight: CGFloat) {
+ self.fullHeight = fullHeight
+ self.detentHeight = detentHeight
+ self.minimizedHeight = minimizedHeight
+ }
+ }
+
+ // MARK: - State
+
+ public enum State {
+ case full
+ case detented
+ case minimized
+ }
+
+ // MARK: - UI Components
+
+ let contentViewController: Content
+ let bottomSheetViewController: BottomSheet
+
+ private var topConstraints: NSLayoutConstraint?
+
+ private lazy var panGesture: UIPanGestureRecognizer = {
+ let panGesture = UIPanGestureRecognizer()
+ panGesture.delegate = self
+ panGesture.addTarget(self, action: #selector(self.handlePanGesture(_:)))
+ return panGesture
+ }()
+
+ // MARK: - Properties
+
+ private let configuration: BottomSheetConfiguration
+ var state: State = .minimized
+ private let gestureVelocity: CGFloat = 750.0
+
+ // MARK: - Initializer
+
+ public init(contentViewController: Content,
+ bottomSheetViewController: BottomSheet,
+ configuration: BottomSheetConfiguration) {
+ self.contentViewController = contentViewController
+ self.bottomSheetViewController = bottomSheetViewController
+ self.configuration = configuration
+ super.init(nibName: nil, bundle: nil)
+ self.configureLayout()
+ self.configureStyle()
+ self.configureGesture()
+ }
+
+ public required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ // MARK: - Functions
+
+ public func presentFullBottomSheet(animated: Bool = true) {
+ self.topConstraints?.constant = -self.configuration.fullHeight
+
+ if animated {
+ UIView.animate(withDuration: 0.3) {
+ self.view.layoutIfNeeded()
+ } completion: { _ in
+ self.state = .full
+ }
+ } else {
+ self.view.layoutIfNeeded()
+ self.state = .full
+ }
+ }
+
+ public func presentDetentedBottomSheet(animated: Bool = true) {
+ self.topConstraints?.constant = -self.configuration.detentHeight
+
+ if animated {
+ UIView.animate(withDuration: 0.5,
+ delay: .zero,
+ usingSpringWithDamping: 0.8,
+ initialSpringVelocity: 0.5,
+ options: [.curveEaseInOut]) {
+ self.view.layoutIfNeeded()
+ } completion: { _ in
+ self.state = .detented
+ }
+ } else {
+ self.view.layoutIfNeeded()
+ self.state = .detented
+ }
+ }
+
+ public func dismissBottomSheet(animated: Bool = true) {
+ self.topConstraints?.constant = -self.configuration.minimizedHeight
+
+ if animated {
+ UIView.animate(withDuration: 0.5,
+ delay: .zero,
+ usingSpringWithDamping: 0.8,
+ initialSpringVelocity: 0.5,
+ options: [.curveEaseInOut]) {
+ self.view.layoutIfNeeded()
+ } completion: { _ in
+ self.state = .minimized
+ }
+ } else {
+ self.view.layoutIfNeeded()
+ self.state = .minimized
+ }
+ }
+
+ // MARK: - Pan Gesture
+
+ @objc
+ private func handlePanGesture(_ sender: UIPanGestureRecognizer) {
+ let translation = sender.translation(in: self.bottomSheetViewController.view)
+ let velocity = sender.velocity(in: self.bottomSheetViewController.view)
+
+ switch sender.state {
+ case .began, .changed:
+ self.handlePanChanged(translation: translation)
+ case .ended:
+ self.handlePanEnded(translation: translation, velocity: velocity)
+ case .failed:
+ self.handlePanFailed()
+ default: break
+ }
+ }
+
+ private func handlePanChanged(translation: CGPoint) {
+ switch self.state {
+ case .full:
+ guard translation.y > 0 else { return }
+
+ self.topConstraints?.constant = -(self.configuration.fullHeight - translation.y.magnitude)
+ self.view.layoutIfNeeded()
+ case .detented:
+ if translation.y >= 0 { // ์๋๋ก Pan
+ self.topConstraints?.constant = -(self.configuration.detentHeight - translation.y.magnitude)
+ } else if translation.y < 0 { // ์๋ก Pan
+ self.topConstraints?.constant = -(self.configuration.detentHeight + translation.y.magnitude)
+ }
+ self.view.layoutIfNeeded()
+ case .minimized:
+ guard translation.y < 0 else { return }
+
+ let newConstant = -(self.configuration.minimizedHeight + translation.y.magnitude)
+ self.topConstraints?.constant = newConstant
+ self.view.layoutIfNeeded()
+ }
+ }
+
+ private func handlePanEnded(translation: CGPoint, velocity: CGPoint) {
+ let yTransMagnitude = translation.y.magnitude
+ switch self.state {
+ case .full:
+ if velocity.y < 0 {
+ self.presentFullBottomSheet()
+ } else if yTransMagnitude >= self.configuration.fullDetentDiff / 2 || velocity.y > self.gestureVelocity {
+ self.presentDetentedBottomSheet()
+ } else {
+ self.presentFullBottomSheet()
+ }
+ case .detented:
+ if yTransMagnitude >= self.configuration.fullDetentDiff / 2 || velocity.y < -self.gestureVelocity {
+ self.presentFullBottomSheet()
+ } else if translation.y >= self.configuration.detentMinimizedDiff / 2 || velocity.y > self.gestureVelocity {
+ self.dismissBottomSheet()
+ } else {
+ self.presentDetentedBottomSheet()
+ }
+ case .minimized:
+ if yTransMagnitude >= self.configuration.detentHeight / 2 || velocity.y < -self.gestureVelocity {
+ self.presentDetentedBottomSheet()
+ } else {
+ self.dismissBottomSheet()
+ }
+ }
+ }
+
+ private func handlePanFailed() {
+ switch self.state {
+ case .full:
+ self.presentFullBottomSheet()
+ case .detented:
+ self.presentDetentedBottomSheet()
+ case .minimized:
+ self.dismissBottomSheet()
+ }
+ }
+
+ public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
+ shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
+ ) -> Bool {
+ return true
+ }
+
+}
+
+// MARK: - UI Configuration
+
+private extension MSBottomSheetViewController {
+
+ func configureStyle() {
+ self.bottomSheetViewController.view.layer.cornerRadius = 12.0
+ self.bottomSheetViewController.view.clipsToBounds = true
+ }
+
+ func configureLayout() {
+ self.addChild(self.contentViewController)
+ self.addChild(self.bottomSheetViewController)
+
+ self.view.addSubview(self.contentViewController.view)
+ self.view.addSubview(self.bottomSheetViewController.view)
+
+ self.contentViewController.view.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ self.contentViewController.view.leftAnchor
+ .constraint(equalTo: self.view.leftAnchor),
+ self.contentViewController.view.rightAnchor
+ .constraint(equalTo: self.view.rightAnchor),
+ self.contentViewController.view.topAnchor
+ .constraint(equalTo: self.view.topAnchor),
+ self.contentViewController.view.bottomAnchor
+ .constraint(equalTo: self.view.bottomAnchor)
+ ])
+ self.contentViewController.didMove(toParent: self)
+
+ self.bottomSheetViewController.view.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ self.bottomSheetViewController.view.heightAnchor
+ .constraint(equalToConstant: self.configuration.fullHeight),
+ self.bottomSheetViewController.view.leftAnchor
+ .constraint(equalTo: self.view.leftAnchor),
+ self.bottomSheetViewController.view.rightAnchor
+ .constraint(equalTo: self.view.rightAnchor)
+ ])
+ self.topConstraints = self.bottomSheetViewController.view.topAnchor
+ .constraint(equalTo: self.view.bottomAnchor,
+ constant: -self.configuration.minimizedHeight)
+ self.topConstraints?.isActive = true
+ self.bottomSheetViewController.didMove(toParent: self)
+ }
+
+ func configureGesture() {
+ self.bottomSheetViewController.view.addGestureRecognizer(self.panGesture)
+ }
+
+}
diff --git a/iOS/MSUIKit/Tests/MSDesignSystemTests/MSDesignSystemTests.swift b/iOS/MSUIKit/Tests/MSDesignSystemTests/MSDesignSystemTests.swift
new file mode 100644
index 0000000..7eebb79
--- /dev/null
+++ b/iOS/MSUIKit/Tests/MSDesignSystemTests/MSDesignSystemTests.swift
@@ -0,0 +1,10 @@
+//
+// MSDesignSystemTests.swift
+// MSUIKit
+//
+// Created by ์ด์ฐฝ์ค on 11/26/23.
+//
+
+import XCTest
+
+final class MSDesignSystemTests: XCTestCase { }
diff --git a/iOS/MusicSpot.xcworkspace/contents.xcworkspacedata b/iOS/MusicSpot.xcworkspace/contents.xcworkspacedata
index e854475..8cad135 100644
--- a/iOS/MusicSpot.xcworkspace/contents.xcworkspacedata
+++ b/iOS/MusicSpot.xcworkspace/contents.xcworkspacedata
@@ -2,15 +2,21 @@
+ location = "group:Features/JourneyList">
+ location = "group:Features/SaveJourney">
+ location = "group:MusicSpot/MusicSpot.xcodeproj">
+
+
+
+
diff --git a/iOS/MusicSpot.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist~HEAD b/iOS/MusicSpot.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist~HEAD
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/iOS/MusicSpot.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist~HEAD
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/iOS/MusicSpot.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist~release b/iOS/MusicSpot.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist~release
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/iOS/MusicSpot.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist~release
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/iOS/MusicSpot/MusicSpot/SceneDelegate.swift b/iOS/MusicSpot/MusicSpot/SceneDelegate.swift
index 61bf718..b01638c 100644
--- a/iOS/MusicSpot/MusicSpot/SceneDelegate.swift
+++ b/iOS/MusicSpot/MusicSpot/SceneDelegate.swift
@@ -8,11 +8,18 @@
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
+
var window: UIWindow?
func scene(_ scene: UIScene,
willConnectTo _: UISceneSession,
options _: UIScene.ConnectionOptions) {
- guard (scene as? UIWindowScene) != nil else { return }
+ guard let windowScene = (scene as? UIWindowScene) else { return }
+ let window = UIWindow(windowScene: windowScene)
+ defer { self.window = window }
+
+ let testViewController = UIViewController()
+ window.rootViewController = testViewController
+ window.makeKeyAndVisible()
}
}