├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ └── 아차-이슈.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── Acha
├── .swiftlint.yml
├── Acha.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Acha.xcscheme
├── Acha
│ ├── Acha.entitlements
│ ├── App
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── AppIcon.png
│ │ │ │ └── Contents.json
│ │ │ ├── Color
│ │ │ │ ├── CommentBoxColor.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Contents.json
│ │ │ │ ├── GameRoomColor.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── PointDarkColor.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── PointLightColor.colorset
│ │ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ └── Image
│ │ │ │ ├── Contents.json
│ │ │ │ ├── commentImage.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── bubble 1.png
│ │ │ │ ├── crazy.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── crazy.png
│ │ │ │ ├── email.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── email.png
│ │ │ │ ├── firstAnnotation.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── penguin.png
│ │ │ │ ├── fourthAnnotation.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── bunny.png
│ │ │ │ ├── invalidate.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── exclamation-mark.png
│ │ │ │ ├── map_0.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── map_0.png
│ │ │ │ ├── map_1.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── map_1.png
│ │ │ │ ├── map_2.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── map_2.png
│ │ │ │ ├── map_3.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── map_3.png
│ │ │ │ ├── nickname.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── name.png
│ │ │ │ ├── noBadge.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── noBadge.png
│ │ │ │ ├── password.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── padlock.png
│ │ │ │ ├── rank0.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── medal.png
│ │ │ │ ├── rank1.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── medal (1).png
│ │ │ │ ├── rank2.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── medal (2).png
│ │ │ │ ├── secondAnnotation.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── pet.png
│ │ │ │ ├── thirdAnnotation.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── cat.png
│ │ │ │ └── x.circle.fill.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── x.circle.fill.png
│ │ ├── Base.lproj
│ │ │ └── LaunchScreen.storyboard
│ │ ├── Coordinator
│ │ │ ├── AppCoordinator.swift
│ │ │ └── Coordinator.swift
│ │ ├── GoogleService-Info.plist
│ │ ├── Info.plist
│ │ └── SceneDelegate.swift
│ ├── Data
│ │ ├── DTO
│ │ │ ├── BadgeDTO.swift
│ │ │ ├── ChatDTO.swift
│ │ │ ├── CommentDTO.swift
│ │ │ ├── CommunityDTO.swift
│ │ │ ├── CoordinateDTO.swift
│ │ │ ├── InGameUserDataDTO.swift
│ │ │ ├── MapDTO.swift
│ │ │ ├── MultiGamePlayerDTO.swift
│ │ │ ├── PostDTO.swift
│ │ │ ├── RecordDTO.swift
│ │ │ ├── RoomDTO.swift
│ │ │ └── UserDTO.swift
│ │ └── Repository
│ │ │ ├── DefaultBadgeRepository.swift
│ │ │ ├── DefaultCommunityRepository.swift
│ │ │ ├── DefaultGameRoomRepository.swift
│ │ │ ├── DefaultLocationRepository.swift
│ │ │ ├── DefaultMapRepository.swift
│ │ │ ├── DefaultRecordRepository.swift
│ │ │ ├── DefaultTempRepository.swift
│ │ │ ├── DefaultTimeRepository.swift
│ │ │ ├── DefaultUserRepository.swift
│ │ │ └── Protocol
│ │ │ ├── BadgeRepository.swift
│ │ │ ├── CommunityRepository.swift
│ │ │ ├── GameRoomRepository.swift
│ │ │ ├── LocationRepository.swift
│ │ │ ├── MapRepository.swift
│ │ │ ├── RecordRepository.swift
│ │ │ ├── TempRepository.swift
│ │ │ ├── TimeRepository.swift
│ │ │ └── UserRepository.swift
│ ├── Domain
│ │ ├── Entity
│ │ │ ├── Badge.swift
│ │ │ ├── Chat.swift
│ │ │ ├── Comment.swift
│ │ │ ├── Coordinate.swift
│ │ │ ├── HealthKitReadData.swift
│ │ │ ├── HealthKitWriteData.swift
│ │ │ ├── Image.swift
│ │ │ ├── InGameRanking.swift
│ │ │ ├── InGameRecord.swift
│ │ │ ├── LogInData.swift
│ │ │ ├── Map.swift
│ │ │ ├── MapRegion.swift
│ │ │ ├── MultiGamePlayerData.swift
│ │ │ ├── Post.swift
│ │ │ ├── Record.swift
│ │ │ ├── RecordViewChartData.swift
│ │ │ ├── RecordViewDayTotalRecord.swift
│ │ │ ├── RecordViewHeaderRecord.swift
│ │ │ ├── RoomUser.swift
│ │ │ ├── SignUpData.swift
│ │ │ └── User.swift
│ │ └── UseCase
│ │ │ ├── Auth
│ │ │ ├── DefatulSignUpUsecase.swift
│ │ │ ├── DefaultLoginUsecase.swift
│ │ │ └── Protocol
│ │ │ │ ├── AuthUseCaseProtocol.swift
│ │ │ │ ├── LoginUseCase.swift
│ │ │ │ └── SignUpUseCase.swift
│ │ │ ├── Community
│ │ │ ├── DefaultCommunityDetailUseCase.swift
│ │ │ ├── DefaultCommunityMainUseCase.swift
│ │ │ ├── DefaultCommunityPostWriteUseCase.swift
│ │ │ └── Protocol
│ │ │ │ ├── CommunityDetailUseCase.swift
│ │ │ │ ├── CommunityMainUseCase.swift
│ │ │ │ └── CommunityPostWriteUseCase.swift
│ │ │ ├── Game
│ │ │ ├── DefaultInGameUseCase.swift
│ │ │ ├── DefaultMapBaseUseCase.swift
│ │ │ ├── DefaultMultiGameChatUseCase.swift
│ │ │ ├── DefaultMultiGameRoomUseCase.swift
│ │ │ ├── DefaultMultiGameUseCase.swift
│ │ │ ├── DefaultSelectMapUseCase.swift
│ │ │ ├── DefaultSingleGameUseCase.swift
│ │ │ └── Protocol
│ │ │ │ ├── InGameUseCase.swift
│ │ │ │ ├── MapBaseUseCase.swift
│ │ │ │ ├── MultiGameChatUseCase.swift
│ │ │ │ ├── MultiGameRoomUseCase.swift
│ │ │ │ ├── MultiGameUseCase.swift
│ │ │ │ ├── SelectMapUseCase.swift
│ │ │ │ └── SingleGameUseCase.swift
│ │ │ ├── Home
│ │ │ └── DefaultHomeUseCase.swift
│ │ │ ├── MyPage
│ │ │ ├── DefaultMyInfoEditUseCase.swift
│ │ │ ├── DefaultMyPageUseCase.swift
│ │ │ └── Protocol
│ │ │ │ ├── MyInfoEditUseCase.swift
│ │ │ │ └── MyPageUseCase.swift
│ │ │ └── Record
│ │ │ ├── DefaultRecordMainViewUseCase.swift
│ │ │ ├── DefaultRecordMapViewUseCase.swift
│ │ │ └── Protocol
│ │ │ ├── RecordMainViewUseCase.swift
│ │ │ └── RecordMapViewUseCase.swift
│ ├── Presentation
│ │ ├── Auth
│ │ │ ├── Common
│ │ │ │ ├── AuthButton.swift
│ │ │ │ ├── AuthInputTextField.swift
│ │ │ │ ├── AuthTitleView.swift
│ │ │ │ └── ScrollAbleViewController.swift
│ │ │ ├── Coordinator
│ │ │ │ ├── AuthCoordinator.swift
│ │ │ │ ├── LoginCoordinator.swift
│ │ │ │ └── SignupCoordinator .swift
│ │ │ ├── ViewController
│ │ │ │ ├── LoginViewController.swift
│ │ │ │ └── SignupViewController.swift
│ │ │ └── ViewModel
│ │ │ │ ├── LoginViewModel.swift
│ │ │ │ └── SignUpViewModel.swift
│ │ ├── Base
│ │ │ ├── BaseViewModel.swift
│ │ │ └── MapBase
│ │ │ │ ├── MapBaseViewController.swift
│ │ │ │ └── MapBaseViewModel.swift
│ │ └── TabBar
│ │ │ ├── Community
│ │ │ ├── Cell
│ │ │ │ ├── CommunityDetailCommentCell.swift
│ │ │ │ ├── CommunityDetailCommentHeaderView.swift
│ │ │ │ ├── CommunityDetailPostCell.swift
│ │ │ │ ├── CommunityIndicatorCell.swift
│ │ │ │ └── CommunityMainCell.swift
│ │ │ ├── Common
│ │ │ │ └── CommentView.swift
│ │ │ ├── ViewController
│ │ │ │ ├── CommunityDetailViewController.swift
│ │ │ │ ├── CommunityMainViewController.swift
│ │ │ │ └── CommunityPostWriteViewController.swift
│ │ │ └── ViewModel
│ │ │ │ ├── CommunityDetailViewModel.swift
│ │ │ │ ├── CommunityMainViewModel.swift
│ │ │ │ └── CommunityPostWriteViewModel.swift
│ │ │ ├── Coordinator
│ │ │ ├── CommunityCoordinator.swift
│ │ │ ├── HomeCoordinator.swift
│ │ │ ├── MultiGameCoordinator.swift
│ │ │ ├── MyPageCoordinator.swift
│ │ │ ├── RecordCoordinator.swift
│ │ │ ├── SingleGameCoordinator.swift
│ │ │ └── TabBarCoordinator.swift
│ │ │ ├── Home
│ │ │ ├── Common
│ │ │ │ ├── DistanceAndTimeBar.swift
│ │ │ │ └── PaddingLabel.swift
│ │ │ ├── HomeView
│ │ │ │ ├── ViewController
│ │ │ │ │ ├── HomeViewController.swift
│ │ │ │ │ ├── MultiGameEnterViewController.swift
│ │ │ │ │ └── QRReaderViewController.swift
│ │ │ │ └── ViewModel
│ │ │ │ │ └── HomeViewModel.swift
│ │ │ ├── InGameMenus
│ │ │ │ ├── View
│ │ │ │ │ └── InGameMenuCell.swift
│ │ │ │ ├── ViewController
│ │ │ │ │ ├── InGamePlayMenuViewController.swift
│ │ │ │ │ ├── InGameRankingViewController.swift
│ │ │ │ │ └── InGameRecordViewController.swift
│ │ │ │ └── ViewModel
│ │ │ │ │ ├── InGameRankingViewModel.swift
│ │ │ │ │ └── InGameRecordViewModel.swift
│ │ │ ├── MultiGame
│ │ │ │ ├── View
│ │ │ │ │ ├── ChatCell.swift
│ │ │ │ │ ├── GameRankCell.swift
│ │ │ │ │ ├── GameRoomCell.swift
│ │ │ │ │ ├── GameRoomHeader.swift
│ │ │ │ │ ├── PlayerAnnotation.swift
│ │ │ │ │ ├── PlayerAnnotationView.swift
│ │ │ │ │ └── PlayerCircle.swift
│ │ │ │ ├── ViewController
│ │ │ │ │ ├── MultiGameChatViewController.swift
│ │ │ │ │ ├── MultiGameRoomViewController.swift
│ │ │ │ │ └── MultiGameViewController.swift
│ │ │ │ └── ViewModel
│ │ │ │ │ ├── MultiGameChatViewModel.swift
│ │ │ │ │ ├── MultiGameRoomViewModel.swift
│ │ │ │ │ └── MultiGameViewModel.swift
│ │ │ ├── SelectMap
│ │ │ │ ├── View
│ │ │ │ │ ├── SelectMapRankingHeaderView.swift
│ │ │ │ │ └── SelectMapRecordCell.swift
│ │ │ │ ├── ViewController
│ │ │ │ │ └── SelectMapViewController.swift
│ │ │ │ └── ViewModel
│ │ │ │ │ └── SelectMapViewModel.swift
│ │ │ └── SingleGame
│ │ │ │ ├── View
│ │ │ │ └── GameOverView.swift
│ │ │ │ ├── ViewController
│ │ │ │ └── SingleGameViewController.swift
│ │ │ │ └── ViewModel
│ │ │ │ └── SingleGameViewModel.swift
│ │ │ ├── MyPage
│ │ │ ├── View
│ │ │ │ ├── BadgeCell.swift
│ │ │ │ ├── InfoTextFieldView.swift
│ │ │ │ ├── MyPageHeaderView.swift
│ │ │ │ └── SettingCell.swift
│ │ │ ├── ViewController
│ │ │ │ ├── BadgeViewController.swift
│ │ │ │ ├── CharacterSelectViewController.swift
│ │ │ │ ├── MyInfoEditViewController.swift
│ │ │ │ └── MyPageViewController.swift
│ │ │ └── ViewModel
│ │ │ │ ├── BadgeViewModel.swift
│ │ │ │ ├── CharacterSelectViewModel.swift
│ │ │ │ ├── MyInfoEditViewModel.swift
│ │ │ │ └── MyPageViewModel.swift
│ │ │ └── Record
│ │ │ ├── View
│ │ │ ├── LineGraphView.swift
│ │ │ ├── RecordMainCell.swift
│ │ │ ├── RecordMainChartCell.swift
│ │ │ ├── RecordMainHeaderView.swift
│ │ │ ├── RecordMapCategoryCell.swift
│ │ │ ├── RecordMapHeaderView.swift
│ │ │ ├── RecordMapImageCell.swift
│ │ │ └── RecordMapRankingCell.swift
│ │ │ ├── ViewController
│ │ │ ├── RecordMainViewController.swift
│ │ │ ├── RecordMapViewController.swift
│ │ │ └── RecordPageViewController.swift
│ │ │ └── ViewModel
│ │ │ ├── RecordMainViewModel.swift
│ │ │ └── RecordMapViewModel.swift
│ └── Util
│ │ ├── Dependency
│ │ ├── DIContainer.swift
│ │ ├── DependenciesContainer.swift
│ │ ├── DependenciesDefinition.swift
│ │ └── DependencyKey.swift
│ │ ├── Enums
│ │ ├── AnimationKeyPath.swift
│ │ ├── Errors.swift
│ │ ├── FirebaseRealtimeType.swift
│ │ ├── FirebaseStorageType.swift
│ │ ├── Locations.swift
│ │ ├── PinCharacter.swift
│ │ ├── SystemImageNameSpace.swift
│ │ └── TabBarType.swift
│ │ ├── Error
│ │ └── FirebaseServiceError.swift
│ │ ├── Extension
│ │ ├── CALayer+.swift
│ │ ├── CLLocationCoordinate2D+.swift
│ │ ├── Collection+.swift
│ │ ├── Date+.swift
│ │ ├── Double+.swift
│ │ ├── Encodable+.swift
│ │ ├── Int+.swift
│ │ ├── MKCircle+.swift
│ │ ├── Reactive+
│ │ │ ├── MKMapView+Rx.swift
│ │ │ ├── UIApplication+Rx.swift
│ │ │ ├── UIView+Rx.swift
│ │ │ └── UIViewCotnroller+Rx.swift
│ │ ├── String+.swift
│ │ ├── UIButton+.swift
│ │ ├── UIColor+.swift
│ │ ├── UIFont+.swift
│ │ ├── UIImage+.swift
│ │ ├── UITextField+.swift
│ │ └── UIViewController+.swift
│ │ ├── Manager
│ │ ├── AchaKeyboardManager.swift
│ │ └── KeyChainManager.swift
│ │ ├── Map
│ │ └── MapAnnotation.swift
│ │ └── Service
│ │ ├── DefaultAuthService.swift
│ │ ├── DefaultFirebaseStorageNetworkService.swift
│ │ ├── DefaultHealthKitService.swift
│ │ ├── DefaultImageCacheService.swift
│ │ ├── DefaultKeychainService.swift
│ │ ├── DefaultLocationService.swift
│ │ ├── DefaultRandomService.swift
│ │ ├── DefaultRealtimeDatabaseNetworkService.swift
│ │ ├── DefaultTimerService.swift
│ │ └── Protocol
│ │ ├── AuthService.swift
│ │ ├── FirebaseStorageNetworkService.swift
│ │ ├── HealthKitService.swift
│ │ ├── ImageCacheService.swift
│ │ ├── KeychainService.swift
│ │ ├── LocationService.swift
│ │ ├── RandomService.swift
│ │ ├── RealtimeDatabaseNetworkService.swift
│ │ └── TimerService.swift
├── AchaTests
│ └── AchaTests.swift
├── AchaUITests
│ ├── AchaUITests.swift
│ └── AchaUITestsLaunchTests.swift
└── GPX
│ ├── WorkSpace by 25km:h.gpx
│ ├── WorkSpace by 6km:h.gpx
│ ├── 국민대 12KMH.gpx
│ ├── 국민대 30KMH.gpx
│ ├── 부원여중 12KMH.gpx
│ ├── 부원여중 5MPH.gpx
│ ├── 부평동초등학교 20MPH.gpx
│ ├── 부평동초등학교 4.2MPH.gpx
│ ├── 부평동초등학교 4MPH.gpx
│ └── 부평동초등학교 5MPH.gpx
├── AchaLibrary
├── .gitignore
├── .swiftpm
│ └── xcode
│ │ ├── package.xcworkspace
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata
│ │ └── xcschemes
│ │ └── AchaLibrary.xcscheme
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
│ └── AchaLibrary
│ │ └── AchaLibrary.swift
└── Tests
│ └── AchaLibraryTests
│ └── AchaLibraryTests.swift
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj binary merge=union
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/아차-이슈.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 아차 이슈
3 | about: 템플릿
4 | title: ''
5 | labels: ''
6 | assignees: seungki-cho
7 |
8 | ---
9 |
10 | ## 📌 할 일
11 | - [ ] (1)
12 | - [ ] (2)
13 | ## 😫 관련 문제 및 해결
14 |
15 | ## 📖 참고 자료
16 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | ### 작업 내용:
6 |
7 |
10 | - ex. 주문상품 리스트 구현 or 이미지 뷰어 구현
11 |
12 | ### 이번에 공들였던 부분:
13 |
14 |
18 |
19 | ### 질문:
20 |
21 |
25 |
26 | ### 제출 전 필수 확인 사항:
27 |
28 | - [ ] 빌드가 되는 코드인가요?
29 | - [ ] 버그가 발생하지 않는지 충분히 테스트 해봤나요?
30 |
--------------------------------------------------------------------------------
/Acha/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules:
2 | - trailing_whitespace
3 | - large_tuple
4 | - function_body_length
5 | - type_body_length
6 | excluded:
7 | - Acha/AppDelegate.swift
8 | - Acha/SceneDelegate.swift
9 | - AchaUITests/AchaUITests.swift
10 | - AchaTests/AchaTests.swift
11 |
--------------------------------------------------------------------------------
/Acha/Acha.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Acha/Acha.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Acha/Acha/Acha.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | aps-environment
6 | development
7 | com.apple.developer.healthkit
8 |
9 | com.apple.developer.healthkit.access
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Acha/Acha/App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/11.
6 | //
7 |
8 | import UIKit
9 | import FirebaseCore
10 | import RxSwift
11 |
12 | @main
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | func application(_ application: UIApplication,
16 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | FirebaseApp.configure()
18 | DependenciesDefinition().inject()
19 |
20 | let appearance = UITabBarAppearance()
21 | appearance.configureWithOpaqueBackground()
22 | appearance.backgroundColor = .pointLight
23 | UITabBar.appearance().tintColor = .white
24 | UITabBar.appearance().standardAppearance = appearance
25 |
26 | if #available(iOS 15, *) {
27 | UITabBar.appearance().scrollEdgeAppearance = appearance
28 | }
29 |
30 | return true
31 | }
32 |
33 | // MARK: UISceneSession Lifecycle
34 |
35 | func application(_ application: UIApplication,
36 | configurationForConnecting connectingSceneSession: UISceneSession,
37 | options: UIScene.ConnectionOptions) -> UISceneConfiguration {
38 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
39 | }
40 |
41 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
42 |
43 | }
44 |
45 | func applicationWillTerminate(_ application: UIApplication) {
46 | guard let roomID = UserDefaults.standard.value(forKey: "roomID") as? String else {return}
47 | UserDefaults.standard.removeObject(forKey: "roomID")
48 | print(roomID)
49 | let service = DefaultRealtimeDatabaseNetworkService()
50 | service.terminate(type: .room(id: roomID))
51 | sleep(5)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/AppIcon.appiconset/AppIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/AppIcon.appiconset/AppIcon.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "AppIcon.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | }
9 | ],
10 | "info" : {
11 | "author" : "xcode",
12 | "version" : 1
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Color/CommentBoxColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.851",
9 | "green" : "0.851",
10 | "red" : "0.851"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "1.000",
27 | "green" : "1.000",
28 | "red" : "1.000"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Color/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Color/GameRoomColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.753",
9 | "green" : "0.753",
10 | "red" : "0.753"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "1.000",
27 | "green" : "1.000",
28 | "red" : "1.000"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Color/PointDarkColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.345",
9 | "green" : "0.259",
10 | "red" : "0.251"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.557",
27 | "green" : "0.443",
28 | "red" : "0.420"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Color/PointLightColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.557",
9 | "green" : "0.443",
10 | "red" : "0.420"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.345",
27 | "green" : "0.259",
28 | "red" : "0.251"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/commentImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "bubble 1.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/commentImage.imageset/bubble 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/commentImage.imageset/bubble 1.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/crazy.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "crazy.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/crazy.imageset/crazy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/crazy.imageset/crazy.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/email.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "email.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/email.imageset/email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/email.imageset/email.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/firstAnnotation.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "penguin.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/firstAnnotation.imageset/penguin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/firstAnnotation.imageset/penguin.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/fourthAnnotation.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "bunny.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/fourthAnnotation.imageset/bunny.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/fourthAnnotation.imageset/bunny.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/invalidate.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "exclamation-mark.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/invalidate.imageset/exclamation-mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/invalidate.imageset/exclamation-mark.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/map_0.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "map_0.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/map_0.imageset/map_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/map_0.imageset/map_0.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/map_1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "map_1.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/map_1.imageset/map_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/map_1.imageset/map_1.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/map_2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "map_2.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/map_2.imageset/map_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/map_2.imageset/map_2.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/map_3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "map_3.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/map_3.imageset/map_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/map_3.imageset/map_3.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/nickname.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "name.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/nickname.imageset/name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/nickname.imageset/name.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/noBadge.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "noBadge.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/noBadge.imageset/noBadge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/noBadge.imageset/noBadge.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/password.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "padlock.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/password.imageset/padlock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/password.imageset/padlock.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/rank0.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "medal.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/rank0.imageset/medal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/rank0.imageset/medal.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/rank1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "medal (1).png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/rank1.imageset/medal (1).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/rank1.imageset/medal (1).png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/rank2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "medal (2).png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/rank2.imageset/medal (2).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/rank2.imageset/medal (2).png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/secondAnnotation.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "pet.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/secondAnnotation.imageset/pet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/secondAnnotation.imageset/pet.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/thirdAnnotation.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "cat.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/thirdAnnotation.imageset/cat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/thirdAnnotation.imageset/cat.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/x.circle.fill.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "x.circle.fill.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Assets.xcassets/Image/x.circle.fill.imageset/x.circle.fill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boostcampwm-2022/iOS08_Acha/7fb18961ab68c0e9e76d939ef61f2da6e29a0a7a/Acha/Acha/App/Assets.xcassets/Image/x.circle.fill.imageset/x.circle.fill.png
--------------------------------------------------------------------------------
/Acha/Acha/App/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Coordinator/AppCoordinator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppCoordinator.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/14.
6 | //
7 |
8 | import UIKit
9 |
10 | protocol AppCoordinatorProtocol: Coordinator {
11 | func connectAuth()
12 | func connectTabBar()
13 | }
14 |
15 | final class AppCoordinator: AppCoordinatorProtocol {
16 |
17 | var navigationController: UINavigationController
18 | var childCoordinators = [Coordinator]()
19 | weak var delegate: CoordinatorDelegate?
20 |
21 | required init(navigationController: UINavigationController) {
22 | self.navigationController = navigationController
23 | }
24 |
25 | func start() {
26 | if (try? KeyChainManager.get()) == nil {
27 | connectAuth()
28 | } else {
29 | connectTabBar()
30 | }
31 | }
32 |
33 | func connectAuth() {
34 | let coordinator = AuthCoordinator(navigationController: navigationController)
35 | coordinator.start()
36 | coordinator.delegate = self
37 | appendChildCoordinator(coordinator: coordinator)
38 | }
39 |
40 | func connectTabBar() {
41 | let coordinator = TabBarCoordinator(navigationController: navigationController)
42 | coordinator.start()
43 | coordinator.delegate = self
44 | appendChildCoordinator(coordinator: coordinator)
45 | }
46 | }
47 |
48 | extension AppCoordinator: CoordinatorDelegate {
49 | func didFinished(childCoordinator: Coordinator) {
50 | removeAllChildCoordinator()
51 | switch childCoordinator {
52 | case is AuthCoordinator:
53 | connectTabBar()
54 | case is TabBarCoordinator:
55 | connectAuth()
56 | default:
57 | break
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Coordinator/Coordinator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Coordinator.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/14.
6 | //
7 |
8 | import UIKit
9 |
10 | protocol Coordinator: AnyObject {
11 | var navigationController: UINavigationController {get set}
12 | var childCoordinators: [Coordinator] {get set}
13 | var delegate: CoordinatorDelegate? {get set}
14 |
15 | init(navigationController: UINavigationController)
16 |
17 | func start()
18 | /// 특정 자식 코디네이터 삭제
19 | func removeChildCoordinator(coordinator: Coordinator)
20 | /// 자식 코디네이터 추가
21 | func appendChildCoordinator(coordinator: Coordinator)
22 | /// 자식 코디네이터 전부 제거
23 | func removeAllChildCoordinator()
24 | /// 자기 자신 컨트롤러를 네비게이션 컨트롤러에서 제거
25 | func popSelfFromNavigatonController()
26 | }
27 |
28 | extension Coordinator {
29 | func removeChildCoordinator(coordinator: Coordinator) {
30 | childCoordinators = childCoordinators.filter { $0 !== coordinator }
31 | }
32 |
33 | func appendChildCoordinator(coordinator: Coordinator) {
34 | childCoordinators.append(coordinator)
35 | }
36 |
37 | func removeAllChildCoordinator() {
38 | childCoordinators = []
39 | }
40 |
41 | func popSelfFromNavigatonController() {
42 | navigationController.viewControllers.removeLast()
43 | }
44 | }
45 |
46 | protocol CoordinatorDelegate: AnyObject {
47 | func didFinished(childCoordinator: Coordinator)
48 | }
49 |
--------------------------------------------------------------------------------
/Acha/Acha/App/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CLIENT_ID
6 | 357663288042-3bj767jdrqsie6vr6hhlq0ud0suifcvp.apps.googleusercontent.com
7 | REVERSED_CLIENT_ID
8 | com.googleusercontent.apps.357663288042-3bj767jdrqsie6vr6hhlq0ud0suifcvp
9 | API_KEY
10 | AIzaSyDrpL6fKqQ1Wpep35USMRwzCaRodBJEHZM
11 | GCM_SENDER_ID
12 | 357663288042
13 | PLIST_VERSION
14 | 1
15 | BUNDLE_ID
16 | com.boostcamp.Acha
17 | PROJECT_ID
18 | acha-75e27
19 | STORAGE_BUCKET
20 | acha-75e27.appspot.com
21 | IS_ADS_ENABLED
22 |
23 | IS_ANALYTICS_ENABLED
24 |
25 | IS_APPINVITE_ENABLED
26 |
27 | IS_GCM_ENABLED
28 |
29 | IS_SIGNIN_ENABLED
30 |
31 | GOOGLE_APP_ID
32 | 1:357663288042:ios:7d1e8ada78ce7368d80593
33 |
34 |
--------------------------------------------------------------------------------
/Acha/Acha/App/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSLocationAlwaysAndWhenInUseUsageDescription
6 | 앱을 사용하는 동안 사용자의 실시간 위치를 포함하기 위해 위치 권한을 요청합니다
7 | NSLocationWhenInUseUsageDescription
8 | 앱을 사용하는 동안 사용자의 실시간 위치를 포함하기 위해 위치 권한을 요청합니다
9 | NSCameraUsageDescription
10 | QR 코드 인식을 위해 카메라 사용 권한을 요청합니다
11 | UIApplicationSceneManifest
12 |
13 | UIApplicationSupportsMultipleScenes
14 |
15 | UISceneConfigurations
16 |
17 | UIWindowSceneSessionRoleApplication
18 |
19 |
20 | UISceneConfigurationName
21 | Default Configuration
22 | UISceneDelegateClassName
23 | $(PRODUCT_MODULE_NAME).SceneDelegate
24 |
25 |
26 |
27 |
28 | UIBackgroundModes
29 |
30 | location
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Acha/Acha/App/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/11.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 | var coordinator: AppCoordinator?
14 |
15 | func scene(_ scene: UIScene,
16 | willConnectTo session: UISceneSession,
17 | options connectionOptions: UIScene.ConnectionOptions) {
18 | guard let scene = (scene as? UIWindowScene) else { return }
19 | self.window = UIWindow(windowScene: scene)
20 |
21 | let navigationController = UINavigationController()
22 | coordinator = AppCoordinator(navigationController: navigationController)
23 | self.window?.rootViewController = navigationController
24 | self.window?.makeKeyAndVisible()
25 | coordinator?.start()
26 | }
27 |
28 | func sceneDidDisconnect(_ scene: UIScene) {
29 |
30 | }
31 |
32 | func sceneDidBecomeActive(_ scene: UIScene) {
33 | // Called when the scene has moved from an inactive state to an active state.
34 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
35 | }
36 |
37 | func sceneWillResignActive(_ scene: UIScene) {
38 | // Called when the scene will move from an active state to an inactive state.
39 | // This may occur due to temporary interruptions (ex. an incoming phone call).
40 | }
41 |
42 | func sceneWillEnterForeground(_ scene: UIScene) {
43 | // Called as the scene transitions from the background to the foreground.
44 | // Use this method to undo the changes made on entering the background.
45 | }
46 |
47 | func sceneDidEnterBackground(_ scene: UIScene) {
48 | // Called as the scene transitions from the foreground to the background.
49 | // Use this method to save data, release shared resources, and store enough scene-specific state information
50 | // to restore the scene back to its current state.
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/BadgeDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BadgeDTO.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct BadgeDTO: Codable {
11 | let id: Int
12 | let name: String
13 | let imageURL: String
14 | let isHidden: Bool
15 |
16 | enum CodingKeys: String, CodingKey {
17 | case id
18 | case name
19 | case imageURL = "image"
20 | case isHidden
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/ChatDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChatDTO.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/08.
6 | //
7 |
8 | import Foundation
9 |
10 | struct ChatDTO: Codable {
11 | let id: String
12 | let nickName: String
13 | let created: Date
14 | let text: String
15 | var read: [String]
16 |
17 | func toChat() -> Chat {
18 | return Chat(id: id, nickName: nickName, created: created, text: text)
19 | }
20 |
21 | func toRead() -> [String] {
22 | return read
23 | }
24 |
25 | init(data: Chat) {
26 | self.id = data.id
27 | self.nickName = data.nickName
28 | self.created = data.created
29 | self.text = data.text
30 | self.read = [data.id]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/CommentDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommentDTO.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/28.
6 | //
7 |
8 | import Foundation
9 |
10 | struct CommentDTO: Codable {
11 | let id: Int
12 | let postId: Int
13 | let nickName: String
14 | let userId: String
15 | let text: String
16 | let createdAt: Date
17 |
18 | func toDomain() -> Comment {
19 | return Comment(
20 | id: id,
21 | postId: postId,
22 | userId: userId,
23 | nickName: nickName,
24 | text: text,
25 | createdAt: createdAt
26 | )
27 | }
28 |
29 | init(data: Comment) {
30 | self.id = data.id
31 | self.postId = data.postId
32 | self.nickName = data.nickName
33 | self.userId = data.userId
34 | self.text = data.text
35 | self.createdAt = data.createdAt
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/CommunityDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommunityDTO.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/03.
6 | //
7 |
8 | import Foundation
9 |
10 | struct CommunityDTO: Codable {
11 | var postList: [PostDTO]?
12 |
13 | mutating func addPost(post: PostDTO) {
14 | postList?.append(post)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/CoordinateDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CoordinateDTO.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/27.
6 | //
7 |
8 | import Foundation
9 |
10 | struct CoordinateDTO: Codable {
11 | let latitude, longitude: Double
12 |
13 | func distance(from: Coordinate) -> Double {
14 | let theta = self.longitude - from.longitude
15 | let dist = sin(self.latitude.degreeToRadian()) *
16 | sin(from.latitude.degreeToRadian()) +
17 | cos(self.latitude.degreeToRadian()) *
18 | cos(from.latitude.degreeToRadian()) *
19 | cos(theta.degreeToRadian())
20 |
21 | return acos(dist).radianToDegree() * 60 * 1.853159616 * 1000
22 | }
23 |
24 | func toDomain() -> Coordinate {
25 | return Coordinate(latitude: latitude, longitude: longitude)
26 | }
27 |
28 | init(data: Coordinate) {
29 | self.latitude = data.latitude
30 | self.longitude = data.longitude
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/InGameUserDataDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InGameUserData.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/27.
6 | //
7 |
8 | import Foundation
9 |
10 | struct InGameUserDataDTO: Codable, Comparable {
11 | static func < (lhs: InGameUserDataDTO, rhs: InGameUserDataDTO) -> Bool {
12 | return lhs.eatenMapID.count < rhs.eatenMapID.count
13 | }
14 |
15 | static func == (lhs: InGameUserDataDTO, rhs: InGameUserDataDTO) -> Bool {
16 | return lhs.eatenMapID.count == rhs.eatenMapID.count
17 | }
18 |
19 | let userID: String
20 | let eatenMapID: [Int]
21 | var routes: [CoordinateDTO]
22 | }
23 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/MapDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapDTO.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/27.
6 | //
7 |
8 | import Foundation
9 |
10 | struct MapDTO: Codable {
11 | let mapID: Int
12 | let name: String
13 | let centerCoordinate: CoordinateDTO
14 | let coordinates: [CoordinateDTO]
15 | let location: String
16 | let image: String
17 |
18 | enum CodingKeys: String, CodingKey {
19 | case mapID = "mapId"
20 | case name, centerCoordinate, coordinates, location, image
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/MultiGamePlayerDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiGamePlayerDTO.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 |
10 | struct MultiGamePlayerDTO: Codable {
11 | let id: String
12 | let nickName: String
13 | let currentLocation: CoordinateDTO?
14 | let point: Int
15 | let locationHistory: [CoordinateDTO]?
16 |
17 | func toDamin() -> MultiGamePlayerData {
18 | return MultiGamePlayerData(
19 | id: id,
20 | nickName: nickName,
21 | currentLocation: currentLocation == nil ? nil : currentLocation?.toDomain(),
22 | point: point
23 | )
24 | }
25 |
26 | init(data: MultiGamePlayerData, history: [Coordinate]) {
27 | self.id = data.id
28 | self.nickName = data.nickName
29 | self.currentLocation = data.currentLocation == nil ? nil : CoordinateDTO(data: data.currentLocation!)
30 | self.point = data.point
31 | self.locationHistory = history.map { CoordinateDTO(data: $0) }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/PostDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PostDTO.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/29.
6 | //
7 | import Foundation
8 |
9 | struct PostDTO: Codable {
10 | let id: Int
11 | let userId: String
12 | let nickName: String
13 | let text: String
14 | let image: String?
15 | let createdAt: Date
16 | let comments: [CommentDTO]?
17 |
18 | func toDomain() -> Post {
19 | let comments = comments == nil ? [] : comments?.map { $0.toDomain() }
20 |
21 | return Post(
22 | id: id,
23 | userId: userId,
24 | nickName: nickName,
25 | text: text,
26 | createdAt: createdAt,
27 | comments: comments
28 | )
29 | }
30 |
31 | init(data: Post, image: String? = nil) {
32 | self.id = data.id
33 | self.userId = data.userId
34 | self.nickName = data.nickName
35 | self.text = data.text
36 | self.image = image
37 | self.createdAt = data.createdAt
38 | self.comments = data.comments?.compactMap { CommentDTO(data: $0) }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/RecordDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordDTO.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct RecordDTO: Codable {
11 | let id: Int
12 | let mapID: Int
13 | let userID: String
14 | let calorie: Int
15 | let distance: Int
16 | let time: Int
17 | let isSingleMode: Bool
18 | let isWin: Bool?
19 | let isCompleted: Bool
20 | let createdAt: String
21 |
22 | enum CodingKeys: String, CodingKey {
23 | case id
24 | case mapID = "map_id"
25 | case userID = "user_id"
26 | case calorie
27 | case distance
28 | case time
29 | case isSingleMode
30 | case isWin
31 | case isCompleted
32 | case createdAt = "created_at"
33 | }
34 |
35 | func toDomain() -> Record {
36 | return Record(id: id,
37 | mapID: mapID,
38 | userID: userID,
39 | calorie: calorie,
40 | distance: distance,
41 | time: time,
42 | isSingleMode: isSingleMode,
43 | isWin: isWin,
44 | isCompleted: isCompleted,
45 | createdAt: createdAt)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/RoomDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomDTO.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/23.
6 | //
7 |
8 | import Foundation
9 |
10 | struct RoomDTO: Codable {
11 | let id: String
12 | let createdAt: Date
13 | var user: [UserDTO]
14 | let mapID: [Int]?
15 | var inGameUserDatas: [InGameUserDataDTO]?
16 | var gameInformation: [MultiGamePlayerDTO]?
17 | var chats: [ChatDTO]?
18 |
19 | func toRoomUsers() -> [RoomUser] {
20 | return user.map { $0.toRoomUser() }
21 | }
22 |
23 | mutating func leaveFromRoom(userID: String) {
24 | self.user = user.filter { $0.id != userID }
25 | }
26 |
27 | mutating func enterRoom(user: UserDTO) {
28 | self.user.append(user)
29 | }
30 |
31 | mutating func appendChat(chat: ChatDTO) {
32 | if self.chats == nil {
33 | self.chats = [chat]
34 | } else {
35 | self.chats?.append(chat)
36 | }
37 | }
38 |
39 | init(id: String, user: [UserDTO]) {
40 | self.id = id
41 | self.user = user
42 | self.createdAt = Date()
43 | self.mapID = nil
44 | self.inGameUserDatas = nil
45 | self.gameInformation = nil
46 | self.chats = nil
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/DTO/UserDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserDTO.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct UserDTO: Codable {
11 | let id: String
12 | let nickname: String
13 | let badges: [Int]?
14 | let records: [Int]?
15 | let pinCharacter: String?
16 | let friends: [Int]?
17 |
18 | func toRoomUser() -> RoomUser {
19 | return RoomUser(id: id, nickName: nickname)
20 | }
21 |
22 | func toDomain() -> User {
23 | return User(id: id,
24 | nickName: nickname,
25 | badges: badges ?? [],
26 | records: records ?? [],
27 | pinCharacter: pinCharacter ?? "firstAnnotation",
28 | friends: friends ?? [])
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/DefaultLocationRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultLocationRepository.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import CoreLocation
11 |
12 | struct DefaultLocationRepository: LocationRepository {
13 |
14 | private let locationService: LocationService
15 |
16 | init(locationService: LocationService) {
17 | self.locationService = locationService
18 | }
19 |
20 | func getCurrentLocation() -> Observable {
21 | locationService.start()
22 | return locationService.userLocation
23 | .skip(1)
24 | .map { return Coordinate(
25 | latitude: $0.coordinate.latitude,
26 | longitude: $0.coordinate.longitude
27 | ) }
28 | }
29 |
30 | func stopObservingLocation() {
31 | locationService.stop()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/DefaultTempRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultRecordMapViewRepository.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/23.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | final class DefaultTempRepository: TempRepository {
12 | var tempDBNetwork: TempDBNetwork
13 |
14 | init(tempDBNetwork: TempDBNetwork) {
15 | self.tempDBNetwork = tempDBNetwork
16 | }
17 |
18 | func fetchMapData() -> Observable<[MapDTO]> {
19 | return tempDBNetwork.fetchData(path: "mapList").map { data in
20 | guard let mapDTO = try? JSONDecoder().decode([MapDTO].self, from: data) else { return [] }
21 | return mapDTO
22 | }
23 | }
24 |
25 | func fetchRecordData() -> Observable<[Record]> {
26 | return tempDBNetwork.fetchData(path: "record").map { data in
27 | guard let records = try? JSONDecoder().decode([Record].self, from: data) else { return [] }
28 | return records
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/DefaultTimeRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultTimeRepository.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | struct DefaultTimeRepository: TimeRepository {
12 |
13 | private let timerService: TimerService
14 |
15 | init(timeService: TimerService) {
16 | self.timerService = timeService
17 | }
18 |
19 | func startTimer() -> Observable {
20 | return timerService.start()
21 | }
22 |
23 | func setTimer(time: Int) -> Observable {
24 | return startTimer()
25 | .map {
26 | let remainTime = time - $0
27 | if remainTime <= 0 { stopTimer() }
28 | return remainTime
29 | }
30 | }
31 |
32 | func stopTimer() {
33 | return timerService.stop()
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/Protocol/BadgeRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BadgeRepository.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/12/01.
6 | //
7 |
8 | import RxSwift
9 |
10 | protocol BadgeRepository {
11 | func fetchAllBadges() -> Observable<[Badge]>
12 | func fetchSomeBadges(ids: [Int]) -> Observable<[Badge]>
13 | }
14 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/Protocol/CommunityRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommunityRepository.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/05.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol CommunityRepository {
12 | var uploadCommentSuccess: PublishSubject { get set }
13 |
14 | func loadPost(count: Int) -> Observable<[Post]>
15 | func fetchPost(postID: Int) -> Observable
16 | func uploadPost(post: Post, image: Image?) -> Single
17 | func updatePost(post: Post, image: Image?) -> Single
18 | func deletePost(id: Int) -> Single
19 | func uploadComment(comment: Comment) -> Single
20 | }
21 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/Protocol/GameRoomRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GameRoomRepository.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/05.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol GameRoomRepository {
12 | /// RoomDTO 전부를 끌고 오는 메서드 입니다
13 | func fetchRoomData(id: String) -> Single
14 |
15 | /// RoomDTO 를 RoomUser 로 변환 하고 리턴하는 메서드 입니다
16 | func fetchRoomUserData(id: String) -> Single<[RoomUser]>
17 |
18 | /// 방에 들어가는 메서드 입니다
19 | func enterRoom(id: String) -> Single<[RoomUser]>
20 |
21 | /// 방을 만드는 메서드입니다. ( 입장 포함 ) ... 방 번호 리턴
22 | func makeRoom() -> Observable
23 |
24 | /// 방을 떠나는 메서드입니다.
25 | func leaveRoom(id: String)
26 |
27 | /// 방을 삭제하는 메서드입니다.
28 | func deleteRoom(id: String)
29 |
30 | /// 원하는 방의 상황을 옵저빙 할 수 있는 메서드입니다.
31 | func observingRoom(id: String) -> Observable
32 |
33 | /// [RoomUser] 형태로 변경 해주는 메서드입니다.
34 | func observingRoomUser(id: String) -> Observable<[RoomUser]>
35 |
36 | /// observer 해제 ( observing 하고 안 할때 풀어줘야 합니다 )
37 | func removeObserverRoom(id: String)
38 |
39 | /// 게임 유저 데이터 불러 오기 ... observing 사용 ... 해제 필요
40 | func observingMultiGamePlayers(id: String) -> Observable<[MultiGamePlayerData]>
41 |
42 | /// 게임 룸 업데이트 메서드
43 | func updateMultiGamePlayer(
44 | roomId: String,
45 | data: MultiGamePlayerData,
46 | histroy: [Coordinate]
47 | )
48 |
49 | /// 게임 시작시 인 게임 데이터 만들기
50 | func startGame(roomId: String)
51 |
52 | /// 룸 챗 옵저빙
53 | func observingChats(id: String) -> Observable<[ChatDTO]>
54 |
55 | /// 읽은 정보 업데이트
56 | func updateReads(roomID: String, reads: [[String]])
57 |
58 | /// 쳇 업데이트
59 | func updateChats(roomID: String, chat: Chat)
60 |
61 | /// 메시지들 읽은 사람들 리턴해주는 메서드
62 | func observingReads(id: String) -> Observable<[[String]]>
63 | }
64 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/Protocol/LocationRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LocationRepository.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import CoreLocation
11 |
12 | protocol LocationRepository {
13 |
14 | /// location 얻는 레포지토리
15 | func getCurrentLocation() -> Observable
16 | /// CLLocation 스탑
17 | func stopObservingLocation()
18 | }
19 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/Protocol/MapRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapRepository.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/27.
6 | //
7 |
8 | import RxSwift
9 |
10 | protocol MapRepository {
11 | func fetchAllMaps() -> Observable<[Map]>
12 | func fetchMapsAtLocation(location: String) -> Observable<[Map]>
13 | }
14 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/Protocol/RecordRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordRepository.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/27.
6 | //
7 |
8 | import RxSwift
9 |
10 | protocol RecordRepository {
11 | func fetchAllRecords() -> Single<[Record]>
12 | func fetchRecordDataAtMapID(mapID: Int) -> Single<[Record]>
13 | func uploadNewRecord(record: Record)
14 | func recordCount() -> Single
15 | func healthKitAuthorization() -> Observable
16 | func healthKitWrite(_ data: HealthKitWriteData) -> Observable
17 | func healthKitRead() -> Observable
18 | }
19 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/Protocol/TempRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordMainViewRepository.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/23.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol TempRepository {
12 | var tempDBNetwork: TempDBNetwork { get set }
13 | func fetchMapData() -> Observable<[MapDTO]>
14 | func fetchRecordData() -> Observable<[Record]>
15 | }
16 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/Protocol/TimeRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TimeRepository.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol TimeRepository {
12 |
13 | /// 타이머 시작 ( 1 -> 2 -> 3 ... )
14 | func startTimer() -> Observable
15 |
16 | /// 특정 시간 타이머 맞춤 ( 0초 되면 종료 )
17 | func setTimer(time: Int) -> Observable
18 |
19 | /// 타이머 종료
20 | func stopTimer()
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/Data/Repository/Protocol/UserRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserRepository.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/02.
6 | //
7 | import Foundation
8 | import RxSwift
9 |
10 | protocol UserRepository {
11 | func getUUID() -> String?
12 | func fetchUserData() -> Single
13 | func signUp(data: SignUpData) -> Single
14 | func logIn(data: LoginData) -> Single
15 | func signOut() -> Observable
16 | func delete() -> Single
17 | func updateUserData(user: User) -> Single
18 | func updateUserEmail(email: String, password: String) -> Single
19 | }
20 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/Badge.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Badge.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/30.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Badge: Hashable {
11 | let id: Int
12 | let name: String
13 | let image: Data
14 | let isHidden: Bool
15 | var isOwn: Bool = false
16 | }
17 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/Chat.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Chat.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/08.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Chat: Hashable {
11 | let id: String
12 | let nickName: String
13 | let created: Date
14 | let text: String
15 | }
16 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/Comment.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Comment.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/29.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Comment: Hashable {
11 | var id: Int
12 | var postId: Int
13 | var userId: String
14 | var nickName: String
15 | let text: String
16 | let createdAt: Date
17 |
18 | init(
19 | id: Int = -1,
20 | postId: Int = -1,
21 | userId: String,
22 | nickName: String,
23 | text: String,
24 | createdAt: Date = Date()
25 | ) {
26 | self.id = id
27 | self.postId = postId
28 | self.userId = userId
29 | self.nickName = nickName
30 | self.text = text
31 | self.createdAt = createdAt
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/Coordinate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Coordinate.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/21.
6 | //
7 |
8 | import Foundation
9 | import MapKit
10 |
11 | // MARK: - Coordinate
12 | struct Coordinate: Decodable, Hashable {
13 | let latitude, longitude: Double
14 |
15 | func distance(from: Coordinate) -> Double {
16 | let theta = self.longitude - from.longitude
17 | let dist = sin(self.latitude.degreeToRadian()) *
18 | sin(from.latitude.degreeToRadian()) +
19 | cos(self.latitude.degreeToRadian()) *
20 | cos(from.latitude.degreeToRadian()) *
21 | cos(theta.degreeToRadian())
22 |
23 | return acos(dist).radianToDegree() * 60 * 1.853159616 * 1000
24 | }
25 |
26 | func toCLLocationCoordinate2D() -> CLLocationCoordinate2D {
27 | return CLLocationCoordinate2D(
28 | latitude: latitude,
29 | longitude: longitude
30 | )
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/HealthKitReadData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HealthKitReadData.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/01.
6 | //
7 |
8 | import Foundation
9 |
10 | struct HealthKitReadData {
11 | let calorie: Double?
12 | let distance: Double?
13 | }
14 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/HealthKitWriteData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HealthKitWriteData.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/01.
6 | //
7 |
8 | import Foundation
9 |
10 | struct HealthKitWriteData {
11 | let distance: Int
12 | let diatanceTime: Int64
13 |
14 | let calorie: Int
15 | let calorieTime: Int64
16 |
17 | init(distance: Double, diatanceTime: Int, calorie: Int, calorieTime: Int) {
18 | self.distance = Int(distance)
19 | self.diatanceTime = Int64(diatanceTime)
20 | self.calorie = calorie
21 | self.calorieTime = Int64(calorieTime)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/Image.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Image.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/07.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Image: Hashable {
11 | let name: String
12 | let data: Data
13 | }
14 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/InGameRanking.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InGameRanking.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/15.
6 | //
7 |
8 | import Foundation
9 |
10 | struct InGameRanking: Hashable {
11 | var id: Int
12 | var time: Int
13 | var userName: String
14 | var date: Date
15 | }
16 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/InGameRecord.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InGameRecord.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/15.
6 | //
7 |
8 | import Foundation
9 |
10 | struct InGameRecord: Hashable {
11 | var id: Int
12 | var time: Int
13 | var userName: String
14 | var date: Date
15 | }
16 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/LogInData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LogInData.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/27.
6 | //
7 |
8 | import Foundation
9 |
10 | struct LoginData: Equatable {
11 | let email: String
12 | let password: String
13 | }
14 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/Map.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Map.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/14.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - Map
11 | struct Map: Hashable {
12 | let mapID: Int
13 | let name: String
14 | let centerCoordinate: Coordinate
15 | let coordinates: [Coordinate]
16 | let location: String
17 | let image: Data
18 | }
19 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/MapRegion.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapRegion.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/21.
6 | //
7 |
8 | import Foundation
9 |
10 | struct MapRegion {
11 | let center: Coordinate
12 | let span: CoordinateSpan
13 | }
14 |
15 | struct CoordinateSpan {
16 | let latitudeDelta: Double
17 | let longitudeDelta: Double
18 | }
19 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/MultiGamePlayerData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiGamePlayerData.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 |
10 | struct MultiGamePlayerData: Hashable {
11 | let id: String
12 | let nickName: String
13 | let currentLocation: Coordinate?
14 | let point: Int
15 | }
16 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/Post.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Post.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/28.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Post: Hashable {
11 | var id: Int
12 | var userId: String
13 | var nickName: String
14 | var text: String
15 | var image: Data?
16 | let createdAt: Date
17 | var comments: [Comment]?
18 |
19 | init() {
20 | self.id = -1
21 | self.userId = ""
22 | self.nickName = ""
23 | self.text = ""
24 | self.image = nil
25 | self.createdAt = Date()
26 | self.comments = nil
27 | }
28 |
29 | init(
30 | id: Int = -1,
31 | userId: String,
32 | nickName: String,
33 | text: String,
34 | image: Data? = nil,
35 | createdAt: Date = Date(),
36 | comments: [Comment]? = nil
37 | ) {
38 | self.id = id
39 | self.userId = userId
40 | self.nickName = nickName
41 | self.text = text
42 | self.image = image
43 | self.createdAt = createdAt
44 | self.comments = comments
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/Record.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AchaRecord.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/17.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Record: Hashable, Codable {
11 | var id: Int
12 | var mapID: Int
13 | var userID: String
14 | var calorie: Int
15 | var distance: Int
16 | var time: Int
17 | var isSingleMode: Bool
18 | var isWin: Bool?
19 | var isCompleted: Bool
20 | var createdAt: String
21 |
22 | enum CodingKeys: String, CodingKey {
23 | case id
24 | case mapID = "map_id"
25 | case userID = "user_id"
26 | case calorie
27 | case distance
28 | case time
29 | case isSingleMode
30 | case isWin
31 | case isCompleted
32 | case createdAt = "created_at"
33 | }
34 |
35 | init(id: Int = -1,
36 | mapID: Int = -1,
37 | userID: String = "",
38 | calorie: Int = -1,
39 | distance: Int = -1,
40 | time: Int = Int.max,
41 | isSingleMode: Bool = true,
42 | isWin: Bool? = nil,
43 | isCompleted: Bool = false,
44 | createdAt: String = "") {
45 | self.id = id
46 | self.mapID = mapID
47 | self.userID = userID
48 | self.calorie = calorie
49 | self.distance = distance
50 | self.time = time
51 | self.isSingleMode = isSingleMode
52 | self.isWin = isWin
53 | self.isCompleted = isCompleted
54 | self.createdAt = createdAt
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/RecordViewChartData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordViewChartData.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/17.
6 | //
7 |
8 | import Foundation
9 |
10 | struct RecordViewChartData: Hashable {
11 | var number: Int
12 | var distance: Int
13 | }
14 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/RecordViewDayTotalRecord.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordViewDayTotalRecord.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/18.
6 | //
7 |
8 | import Foundation
9 |
10 | struct DayTotalRecord: Hashable {
11 | var distance: Int
12 | var calorie: Int
13 | }
14 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/RecordViewHeaderRecord.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordViewHeaderRecord.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/17.
6 | //
7 |
8 | import Foundation
9 |
10 | struct RecordViewHeaderRecord: Hashable {
11 | var date: String
12 | var distance: Int
13 | var kcal: Int
14 | }
15 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/RoomUser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomUser.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/24.
6 | //
7 |
8 | import Foundation
9 |
10 | struct RoomUser: Hashable {
11 | let id: String
12 | let nickName: String
13 | }
14 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/SignUpData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SignUpData.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/22.
6 | //
7 |
8 | import Foundation
9 |
10 | struct SignUpData: Equatable {
11 | let email: String
12 | let password: String
13 | let nickName: String
14 |
15 | func toUserDTO(id: String) -> UserDTO {
16 | return UserDTO(id: id,
17 | nickname: nickName,
18 | badges: [],
19 | records: [],
20 | pinCharacter: nil,
21 | friends: []
22 | )
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/Entity/User.swift:
--------------------------------------------------------------------------------
1 | //
2 | // User.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/05.
6 | //
7 |
8 | import Foundation
9 |
10 | struct User {
11 | let id: String
12 | var nickName: String
13 | let badges: [Int]
14 | let records: [Int]
15 | var pinCharacter: String
16 | let friends: [Int]
17 |
18 | init(id: String = "",
19 | nickName: String = "",
20 | badges: [Int] = [],
21 | records: [Int] = [],
22 | pinCharacter: String = "firstAnnotation",
23 | friends: [Int] = []) {
24 | self.id = id
25 | self.nickName = nickName
26 | self.badges = badges
27 | self.records = records
28 | self.pinCharacter = pinCharacter
29 | self.friends = friends
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Auth/DefatulSignUpUsecase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SignUpUsecase.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/27.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxRelay
11 |
12 | final class DefaultSignUpUsecase: SignUpUsecase {
13 |
14 | var emailValidation: Bool = false
15 | var passwordValidation: Bool = false
16 | var nickNameValidation: Bool = false
17 |
18 | var emailRelay: RxRelay.BehaviorRelay = .init(value: "")
19 | var passwordRelay: RxRelay.BehaviorRelay = .init(value: "")
20 | var nickNameRelay: RxRelay.BehaviorRelay = .init(value: "")
21 |
22 | private let repository: UserRepository
23 |
24 | init(repository: UserRepository) {
25 | self.repository = repository
26 | }
27 |
28 | @discardableResult
29 | public func emailValidate(text: String) -> Bool {
30 | let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
31 | emailValidation = text.stringCheck(pattern: pattern)
32 | if emailValidation { emailRelay.accept(text) }
33 | return emailValidation
34 | }
35 |
36 | @discardableResult
37 | public func passwordValidate(text: String) -> Bool {
38 | passwordValidation = text.count >= 6 ? true : false
39 | if passwordValidation { passwordRelay.accept(text) }
40 | return passwordValidation
41 | }
42 |
43 | @discardableResult
44 | public func nickNameValidate(text: String) -> Bool {
45 | nickNameValidation = text.count >= 3 ? true : false
46 | if nickNameValidation { nickNameRelay.accept(text) }
47 | return nickNameValidation
48 | }
49 |
50 | func isSignAble() -> Bool {
51 | return emailValidation && passwordValidation && nickNameValidation
52 | }
53 |
54 | func signUp() -> Observable {
55 | let signUpData = SignUpData(
56 | email: emailRelay.value,
57 | password: passwordRelay.value,
58 | nickName: nickNameRelay.value
59 | )
60 | return repository.signUp(data: signUpData)
61 | .map { $0.id }
62 | .asObservable()
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Auth/DefaultLoginUsecase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginUsecase.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/27.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxRelay
11 |
12 | final class DefaultLoginUseCase: LoginUseCase {
13 |
14 | var emailValidation: Bool = false
15 | var passwordValidation: Bool = false
16 |
17 | var emailRelay: RxRelay.BehaviorRelay = .init(value: "")
18 | var passwordRelay: RxRelay.BehaviorRelay = .init(value: "")
19 | private let repository: UserRepository
20 |
21 | init(repository: UserRepository) {
22 | self.repository = repository
23 | }
24 |
25 | @discardableResult
26 | public func emailValidate(text: String) -> Bool {
27 | let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
28 | emailValidation = text.stringCheck(pattern: pattern)
29 | if emailValidation { emailRelay.accept(text) }
30 | return emailValidation
31 | }
32 |
33 | @discardableResult
34 | public func passwordValidate(text: String) -> Bool {
35 | passwordValidation = text.count >= 6 ? true : false
36 | if passwordValidation { passwordRelay.accept(text) }
37 | return passwordValidation
38 | }
39 |
40 | func isLogInAble() -> Bool {
41 | return emailValidation && passwordValidation
42 | }
43 |
44 | func logIn() -> Observable {
45 | let loginData = LoginData(email: emailRelay.value, password: passwordRelay.value)
46 | return repository.logIn(data: loginData).asObservable()
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Auth/Protocol/AuthUseCaseProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthUseCase.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/21.
6 | //
7 |
8 | import Foundation
9 | import FirebaseAuth
10 | import RxSwift
11 | import FirebaseDatabase
12 |
13 | protocol EmailValidatable {
14 | func emailValidate(text: String) -> Bool
15 | }
16 |
17 | protocol PasswordValidatable {
18 | func passwordValidate(text: String) -> Bool
19 | }
20 |
21 | protocol NickNameValidatable {
22 | func nickNameValidate(text: String) -> Bool
23 | }
24 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Auth/Protocol/LoginUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginUseCase.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/12.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxCocoa
11 |
12 | protocol LoginUseCase: EmailValidatable, PasswordValidatable {
13 | var emailRelay: BehaviorRelay {get set}
14 | var passwordRelay: BehaviorRelay {get set}
15 |
16 | var emailValidation: Bool {get set}
17 | var passwordValidation: Bool {get set}
18 |
19 | func isLogInAble() -> Bool
20 | func logIn() -> Observable
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Auth/Protocol/SignUpUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SignUpUseCase.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/12.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxCocoa
11 |
12 | protocol SignUpUsecase: EmailValidatable, PasswordValidatable, NickNameValidatable {
13 | var emailRelay: BehaviorRelay {get set}
14 | var passwordRelay: BehaviorRelay {get set}
15 | var nickNameRelay: BehaviorRelay {get set}
16 |
17 | var emailValidation: Bool {get set}
18 | var passwordValidation: Bool {get set}
19 | var nickNameValidation: Bool {get set}
20 |
21 | func isSignAble() -> Bool
22 | func signUp() -> Observable
23 | }
24 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Community/DefaultCommunityMainUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommunityMainUseCase.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/30.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxRelay
11 |
12 | final class DefaultCommunityMainUseCase: CommunityMainUseCase {
13 | private let communityRepository: DefaultCommunityRepository
14 | private let disposeBag = DisposeBag()
15 |
16 | var posts = PublishSubject<[Post]>()
17 |
18 | init(communityRepository: DefaultCommunityRepository) {
19 | self.communityRepository = communityRepository
20 | }
21 |
22 | func loadPostData(count: Int) {
23 | communityRepository.loadPost(count: count)
24 | .subscribe(onNext: { [weak self] posts in
25 | guard let self else { return }
26 | self.posts.onNext(posts)
27 | }, onError: { [weak self] _ in
28 | guard let self else { return }
29 | self.posts.onNext([])
30 | })
31 | .disposed(by: disposeBag)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Community/DefaultCommunityPostWriteUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultCommunityPostWriteUseCase.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxRelay
11 |
12 | final class DefaultCommunityPostWriteUseCase: CommunityPostWriteUseCase {
13 | private let communityRepository: CommunityRepository
14 | private let userRepository: UserRepository
15 | private let disposeBag = DisposeBag()
16 | let post: Post!
17 |
18 | init(post: Post? = nil,
19 | communityRepository: CommunityRepository,
20 | userRepository: UserRepository
21 | ) {
22 | self.communityRepository = communityRepository
23 | self.post = post
24 | self.userRepository = userRepository
25 | }
26 |
27 | func confirmHavePost() -> Single {
28 | return Single.create { [weak self] single in
29 | guard let self else { return Disposables.create()}
30 | if let post = self.post {
31 | single(.success(post))
32 | }
33 | return Disposables.create()
34 | }
35 | }
36 |
37 | func uploadPost(postContent: String, image: Image?) -> Single {
38 | return Single.create { [weak self] single in
39 | guard let self else { return Disposables.create()}
40 | self.userRepository.fetchUserData()
41 | .subscribe(onSuccess: { user in
42 | if let selfPost = self.post {
43 | var uploadPost = selfPost
44 | uploadPost.text = postContent
45 | uploadPost.userId = user.id
46 | uploadPost.nickName = user.nickName
47 | self.communityRepository.updatePost(post: uploadPost, image: image)
48 | .subscribe(onSuccess: {
49 | single(.success(()))
50 | })
51 | .disposed(by: self.disposeBag)
52 | } else {
53 | let post = Post(userId: user.id, nickName: user.nickName, text: postContent)
54 | self.communityRepository.uploadPost(post: post, image: image)
55 | .subscribe(onSuccess: {
56 | single(.success(()))
57 | })
58 | .disposed(by: self.disposeBag)
59 | }
60 | }).disposed(by: self.disposeBag)
61 | return Disposables.create()
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Community/Protocol/CommunityDetailUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommunityDetailUseCase.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/05.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol CommunityDetailUseCase {
12 | func fetchPost()
13 | func uploadComment(commentMessage: String) -> Single
14 | func deletePost() -> Single
15 |
16 | var post: PublishSubject<(post: Post, isMine: Bool)> { get set }
17 | var user: BehaviorSubject { get set }
18 | var fetchFailure: PublishSubject { get set }
19 | }
20 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Community/Protocol/CommunityMainUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommunityMainUseCase.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/30.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol CommunityMainUseCase {
12 | var posts: PublishSubject<[Post]> { get set }
13 | func loadPostData(count: Int)
14 | }
15 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Community/Protocol/CommunityPostWriteUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommunityPostWriteUseCase.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxRelay
11 |
12 | protocol CommunityPostWriteUseCase {
13 | func confirmHavePost() -> Single
14 | func uploadPost(postContent: String, image: Image?) -> Single
15 | }
16 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Game/DefaultInGameUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultInGameUseCase.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/29.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | class DefaultInGameUseCase: InGameUseCase {
12 | private let recordRepository: RecordRepository
13 | private let userRepository: UserRepository
14 | private let mapID: Int
15 |
16 | private var disposeBag = DisposeBag()
17 | var inGameRecord = PublishSubject<[InGameRecord]>()
18 |
19 | init(mapID: Int,
20 | recordRepository: RecordRepository,
21 | userRepository: UserRepository
22 | ) {
23 | self.mapID = mapID
24 | self.recordRepository = recordRepository
25 | self.userRepository = userRepository
26 | }
27 |
28 | func fetchRecord() {
29 | userRepository
30 | .fetchUserData()
31 | .asObservable()
32 | .subscribe(onNext: { [weak self] (user: User) in
33 | guard let self else { return }
34 | self.recordRepository.fetchAllRecords()
35 | .map { $0.filter { $0.mapID == self.mapID && $0.isCompleted && user.records.contains($0.id) } }
36 | .map { records in
37 | records.map { InGameRecord(id: $0.id,
38 | time: $0.time,
39 | userName: $0.userID,
40 | date: $0.createdAt.convertToDateFormat(format: "yyyy-MM-dd"))
41 | }.sorted { $0.date > $1.date}
42 | }
43 | .asObservable()
44 | .bind(to: self.inGameRecord)
45 | .disposed(by: self.disposeBag)
46 | }).disposed(by: disposeBag)
47 | }
48 |
49 | func fetchRanking() -> Single<[InGameRanking]> {
50 | recordRepository
51 | .fetchAllRecords()
52 | .map { $0.filter { $0.mapID == self.mapID && $0.isCompleted } }
53 | .map { records in
54 | records.map { InGameRanking(id: $0.id,
55 | time: $0.time,
56 | userName: $0.userID,
57 | date: $0.createdAt.convertToDateFormat(format: "yyyy-MM-dd"))
58 | }.sorted { $0.time < $1.time }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Game/DefaultMapBaseUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultMapBaseUseCase.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/28.
6 | //
7 |
8 | import RxSwift
9 |
10 | class DefaultMapBaseUseCase: MapBaseUseCase {
11 |
12 | private let locationService: LocationService
13 | let userRepository: UserRepository
14 |
15 | var user = BehaviorSubject(value: User())
16 | var userLocation = BehaviorSubject(value: Coordinate(latitude: 37.0, longitude: 126.0))
17 | private var disposeBag = DisposeBag()
18 |
19 | init(locationService: LocationService,
20 | userRepository: UserRepository) {
21 | self.locationService = locationService
22 | self.userRepository = userRepository
23 | }
24 |
25 | func start() {
26 | locationService.start()
27 | locationService.userLocation
28 | .map { Coordinate(latitude: $0.coordinate.latitude, longitude: $0.coordinate.longitude) }
29 | .bind(to: self.userLocation)
30 | .disposed(by: disposeBag)
31 |
32 | userRepository.fetchUserData()
33 | .subscribe(onSuccess: { [weak self] user in
34 | guard let self else { return }
35 | self.user.onNext(user)
36 | }).disposed(by: disposeBag)
37 | }
38 |
39 | func stop() {
40 | locationService.stop()
41 | }
42 |
43 | func isAvailableLocationAuthorization() -> Observable<(Bool, Coordinate?)> {
44 | locationService.authorizationStatus
45 | .delay(.microseconds(1), scheduler: MainScheduler.instance)
46 | .map { [weak self] status in
47 | if let self,
48 | status,
49 | let userLocation = try? self.userLocation.value() {
50 | return (status, userLocation)
51 | } else {
52 | return (status, nil)
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Game/DefaultMultiGameRoomUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiGameRoomUseCase.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/23.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | struct DefaultMultiGameRoomUseCase: MultiGameRoomUseCase {
12 |
13 | enum GameRoomError: Error, LocalizedError {
14 | case notAvailiableUserNumber
15 |
16 | var errorDescription: String? {
17 | switch self {
18 | case .notAvailiableUserNumber:
19 | return "2명 이상이여야 게임을 시작할 수 있습니다 ✌️✌️✌️"
20 | }
21 | }
22 | }
23 | private let disposebag = DisposeBag()
24 |
25 | private let repository: GameRoomRepository
26 | init(repository: GameRoomRepository) {
27 | self.repository = repository
28 | }
29 |
30 | func observing(roomID: String) -> Observable<[RoomUser]?> {
31 | return repository.observingRoom(id: roomID)
32 | .map {
33 | return $0.gameInformation != nil ? nil : $0.toRoomUsers()
34 | }
35 | }
36 |
37 | func get(roomID: String) -> Single<[RoomUser]> {
38 | return repository.fetchRoomUserData(id: roomID)
39 | }
40 |
41 | func leave(roomID: String) {
42 | repository.leaveRoom(id: roomID)
43 | }
44 |
45 | func removeObserver(roomID: String) {
46 | repository.removeObserverRoom(id: roomID)
47 | }
48 |
49 | func isGameAvailable(roomID: String) -> Observable {
50 | return Observable.create { observer in
51 | get(roomID: roomID)
52 | .map { roomUsers in
53 | if 2...4 ~= roomUsers.count {
54 | observer.onNext(())
55 | } else {
56 | observer.onError(GameRoomError.notAvailiableUserNumber)
57 | }
58 | }
59 | .subscribe()
60 | .disposed(by: disposebag)
61 | return Disposables.create()
62 | }
63 | }
64 |
65 | func startGame(roomID: String) {
66 | repository.startGame(roomId: roomID)
67 | removeObserver(roomID: roomID)
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Game/Protocol/InGameUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InGameUseCase.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/29.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol InGameUseCase {
12 | var inGameRecord: PublishSubject<[InGameRecord]> { get set }
13 |
14 | func fetchRecord()
15 | func fetchRanking() -> Single<[InGameRanking]>
16 | }
17 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Game/Protocol/MapBaseUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapBaseUseCase.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/28.
6 | //
7 |
8 | import RxSwift
9 |
10 | protocol MapBaseUseCase {
11 | var user: BehaviorSubject { get set }
12 | var userLocation: BehaviorSubject { get set }
13 |
14 | func start()
15 | func stop()
16 | func isAvailableLocationAuthorization() -> Observable<(Bool, Coordinate?)>
17 | }
18 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Game/Protocol/MultiGameChatUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiGameChatUseCase.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/08.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol MultiGameChatUseCase {
12 | func chatWrite(text: String)
13 | func chatUpdate(roomID: String)
14 | func observeChats(roomID: String) -> Observable<[Chat]>
15 | func leave(roomID: String)
16 | }
17 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Game/Protocol/MultiGameRoomUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiGameRoomUseCase.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol MultiGameRoomUseCase {
12 | func observing(roomID: String) -> Observable<[RoomUser]?>
13 | func get(roomID: String) -> Single<[RoomUser]>
14 | func leave(roomID: String)
15 | func removeObserver(roomID: String)
16 | func isGameAvailable(roomID: String) -> Observable
17 | func startGame(roomID: String)
18 | }
19 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Game/Protocol/MultiGameUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiGameUseCase.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol MultiGameUseCase {
12 |
13 | func timerStart() -> Observable
14 | func timerStop()
15 |
16 | func getLocation() -> Observable
17 | func point() -> Observable
18 | var visitedLocation: Set {get set}
19 | var movedDistance: Double {get set}
20 |
21 | func healthKitStore(time: Int)
22 | func healthKitAuthorization() -> Observable
23 | func updateData(roomId: String)
24 |
25 | func initVisitedLocation()
26 |
27 | func stopObserveLocation()
28 |
29 | func observing(roomID: String) -> Observable<[MultiGamePlayerData]>
30 | func watchOthersLocation(roomID: String) -> Single
31 |
32 | func leave(roomID: String)
33 |
34 | func unReadChatting(roomID: String) -> Observable
35 | func stopOberservingRoom(id: String)
36 |
37 | func gameOver(roomID: String) -> Observable
38 | }
39 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Game/Protocol/SelectMapUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SelectMapUseCase.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/27.
6 | //
7 |
8 | import RxSwift
9 |
10 | protocol SelectMapUseCase: MapBaseUseCase {
11 | var selectedMap: Map? { get set }
12 |
13 | func start()
14 | func getMapsInUpdatedRegion(region: MapRegion) -> [Map]
15 | func mapSelected(_ selectedMap: Map) -> Single<(String, [Record])>
16 | func isStartable() -> Bool
17 | }
18 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Game/Protocol/SingleGameUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SingleGameUseCase.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/23.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol SingleGameUseCase: MapBaseUseCase {
12 | var tapTimer: TimerService { get }
13 | var runningTimer: TimerService { get }
14 |
15 | var ishideGameOverButton: BehaviorSubject { get }
16 | var previousCoordinate: Coordinate? { get set }
17 | var runningTime: BehaviorSubject { get set }
18 | var runningDistance: BehaviorSubject { get set }
19 | var wentLocations: PublishSubject<[Coordinate]> { get set }
20 | var visitLocations: PublishSubject<[Coordinate]> { get set }
21 | var tooFarFromLocaiton: BehaviorSubject { get set }
22 | var visitedMapIndex: BehaviorSubject> { get set }
23 | var gameOverInformation: PublishSubject<(Record, String, Badge?)> { get set }
24 |
25 | func start()
26 | func startGameOverTimer()
27 | func startRunningTimer()
28 | func stop()
29 | func gameOverButtonTapped()
30 |
31 | func healthKitWrite() -> Observable
32 | func healthKitAuthorization() -> Observable
33 | }
34 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Home/DefaultHomeUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomeUseCase.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/23.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol HomeUseCase {
12 | func getUUID() -> Observable
13 | func makeRoom() -> Observable
14 | func enterRoom(id: String) -> Single<[RoomUser]>
15 | }
16 |
17 | struct DefaultHomeUseCase: HomeUseCase {
18 |
19 | enum HomUseCaseError: Error {
20 | case uuidNotFound
21 | }
22 |
23 | private let gameRoomRepository: GameRoomRepository
24 | private let userRepository: UserRepository
25 | init(
26 | gameRoomRepository: GameRoomRepository,
27 | userRepository: UserRepository
28 | ) {
29 | self.gameRoomRepository = gameRoomRepository
30 | self.userRepository = userRepository
31 | }
32 |
33 | func getUUID() -> Observable {
34 | return Observable.create { observer in
35 | guard let uuid = userRepository.getUUID() else {
36 | observer.onError(DefaultHomeUseCase.HomUseCaseError.uuidNotFound)
37 | return Disposables.create()
38 | }
39 | observer.onNext(uuid)
40 | observer.onCompleted()
41 | return Disposables.create()
42 | }
43 | }
44 |
45 | func makeRoom() -> Observable {
46 | return gameRoomRepository.makeRoom()
47 | }
48 |
49 | func enterRoom(id: String) -> Single<[RoomUser]> {
50 | return gameRoomRepository.enterRoom(id: id)
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/MyPage/DefaultMyInfoEditUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultMyInfoEditUseCase.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/12/10.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | final class DefaultMyInfoEditUseCase: MyInfoEditUseCase {
12 |
13 | // MARK: - Properties
14 | private let userRepository: UserRepository
15 | private var disposeBag = DisposeBag()
16 |
17 | // MARK: - Lifecycles
18 | init(userRepository: UserRepository) {
19 | self.userRepository = userRepository
20 | }
21 |
22 | // MARK: - Helpers
23 | func emailValidate(text: String) -> Bool {
24 | let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
25 | return text.stringCheck(pattern: pattern)
26 | }
27 |
28 | func updateUserInfo(user: User, email: String, password: String) -> Single {
29 | Single.create { [weak self] single in
30 | guard let self else { return Disposables.create() }
31 | self.userRepository.updateUserEmail(email: email, password: password)
32 | .subscribe(onSuccess: { [weak self] _ in
33 | guard let self else { return }
34 | self.userRepository.updateUserData(user: user)
35 | .subscribe(onSuccess: {
36 | single(.success(()))
37 | }, onFailure: { error in
38 | single(.failure(error))
39 | })
40 | .disposed(by: self.disposeBag)
41 | }, onFailure: {
42 | single(.failure($0))
43 | })
44 | .disposed(by: self.disposeBag)
45 | return Disposables.create()
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/MyPage/Protocol/MyInfoEditUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MyInfoEditUseCase.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/12/10.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol MyInfoEditUseCase: EmailValidatable {
12 | func updateUserInfo(user: User, email: String, password: String) -> Single
13 | }
14 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/MyPage/Protocol/MyPageUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MyPageUseCase.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/12/01.
6 | //
7 |
8 | import RxSwift
9 |
10 | protocol MyPageUseCase {
11 | var userInfo: User? { get set }
12 | var nickName: PublishSubject { get set }
13 | var ownedBadges: BehaviorSubject<[Badge]> { get set }
14 | var recentlyOwnedBadges: BehaviorSubject<[Badge]> { get set }
15 | var allBadges: BehaviorSubject<[Badge]> { get set }
16 | func fetchMyPageData()
17 | func logout() -> Observable
18 | func deleteUser() -> Single
19 | }
20 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Record/Protocol/RecordMainViewUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordViewUseCase.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/21.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | // swiftlint:disable: large_tuple
11 | protocol RecordMainViewUseCase {
12 | var mapAtMapId: BehaviorSubject<[Int: Map]> { get set }
13 | var weekDatas: PublishSubject<[RecordViewChartData]> { get set }
14 | var recordSectionDatas: PublishSubject<(allDates: [String],
15 | totalRecordAtDate: [String: DayTotalRecord],
16 | recordsAtDate: [String: [Record]],
17 | mapAtMapId: [Int: Map])> { get set }
18 |
19 | func loadMapData() -> Single
20 | func loadRecordData()
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/Domain/UseCase/Record/Protocol/RecordMapViewUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordMapViewUseCase.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/24.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol RecordMapViewUseCase {
12 | var dropDownMenus: BehaviorSubject<[Map]> { get set }
13 | var mapDataAtMapName: BehaviorSubject<[String: Map]> { get set }
14 | var mapDataAtCategory: BehaviorSubject<[String: [Map]]> { get set }
15 | var mapNameAndRecordDatas: PublishSubject<(mapImage: Data?,
16 | mapName: String,
17 | recordDatas: [Record])> { get set }
18 |
19 | func loadMapData()
20 | func loadRecordData() -> Observable<[Record]>
21 | func getDropDownMenus(mapName: String)
22 | func getMapNameAndRecordsAtLocation(location: String)
23 | func getMapNameAndRecordDatasAtMapName(mapName: String)
24 | }
25 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/Auth/Common/AuthButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthButton.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/21.
6 | //
7 |
8 | import UIKit
9 |
10 | final class AuthButton: UIButton {
11 |
12 | init(color: UIColor, text: String) {
13 | super.init(frame: .zero)
14 |
15 | configure(color: color, text: text)
16 | }
17 |
18 | required init?(coder: NSCoder) {
19 | fatalError("init(coder:) has not been implemented")
20 | }
21 |
22 | }
23 |
24 | extension AuthButton {
25 | private func configure(color: UIColor, text: String) {
26 | backgroundColor = color
27 | setTitle(text, for: .normal)
28 | layer.cornerRadius = 10
29 | titleLabel?.font = .boldBody
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/Auth/Common/AuthTitleView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthTitleView.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/21.
6 | //
7 |
8 | import UIKit
9 |
10 | final class AuthTitleView: UIStackView {
11 |
12 | lazy var label = UILabel().then {
13 | $0.font = .largeTitle
14 | $0.textAlignment = .center
15 | }
16 |
17 | init(image: UIImage?, text: String) {
18 | super.init(frame: .zero)
19 | label.text = text
20 | configure()
21 | }
22 |
23 | required init(coder: NSCoder) {
24 | fatalError("init(coder:) has not been implemented")
25 | }
26 |
27 | }
28 |
29 | extension AuthTitleView {
30 | private func configure() {
31 | axis = .horizontal
32 | layout()
33 | }
34 |
35 | private func layout() {
36 | addViews()
37 | addConstraints()
38 | }
39 |
40 | private func addViews() {
41 | addArrangedSubview(label)
42 | }
43 |
44 | private func addConstraints() {
45 | label.snp.makeConstraints { make in
46 | make.edges.equalTo(self)
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/Auth/Common/ScrollAbleViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollAbleViewController.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/21.
6 | //
7 |
8 | import UIKit
9 |
10 | class ScrollAbleViewController: UIViewController {
11 |
12 | lazy var scrollView = UIScrollView()
13 | lazy var contentView = UIStackView()
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 | configure()
18 | hideKeyboardWhenTapped()
19 | }
20 |
21 | }
22 |
23 | extension ScrollAbleViewController {
24 | func addView() {
25 | view.addSubview(scrollView)
26 | scrollView.addSubview(contentView)
27 | }
28 |
29 | func addConstraint() {
30 | scrollView.snp.makeConstraints {
31 | $0.top.bottom.equalToSuperview()
32 | $0.leading.trailing.equalToSuperview().inset(80)
33 | }
34 |
35 | contentView.snp.makeConstraints {
36 | $0.width.equalToSuperview()
37 | $0.centerX.top.bottom.equalToSuperview()
38 | }
39 |
40 | }
41 |
42 | func configure() {
43 | contentView.axis = .vertical
44 | contentView.spacing = 50
45 | contentView.backgroundColor = .white
46 | contentView.distribution = .fillProportionally
47 | addView()
48 | addConstraint()
49 | }
50 |
51 | }
52 |
53 | extension ScrollAbleViewController {
54 |
55 | private func hideKeyboardWhenTapped() {
56 | let tapGestureRecognizer = UITapGestureRecognizer(
57 | target: self,
58 | action: #selector(dismissKeyboard)
59 | )
60 | tapGestureRecognizer.cancelsTouchesInView = false
61 | view.addGestureRecognizer(tapGestureRecognizer)
62 | }
63 |
64 | @objc private func dismissKeyboard() {
65 | view.endEditing(true)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/Auth/Coordinator/AuthCoordinator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthCoordinator.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/14.
6 | //
7 |
8 | import UIKit
9 | import FirebaseAuth
10 | import Firebase
11 |
12 | protocol AuthCoordinatorProtocol: Coordinator {
13 | func connectLoginCoordinator()
14 | func connectSignupCoordinator()
15 | }
16 |
17 | final class AuthCoordinator: AuthCoordinatorProtocol {
18 |
19 | var navigationController: UINavigationController
20 | var childCoordinators: [Coordinator] = []
21 | weak var delegate: CoordinatorDelegate?
22 |
23 | required init(navigationController: UINavigationController) {
24 | self.navigationController = navigationController
25 | self.navigationController.isNavigationBarHidden = true
26 | }
27 |
28 | func start() {
29 | connectLoginCoordinator()
30 | }
31 |
32 | func connectLoginCoordinator() {
33 | let coordinator = LoginCoordinator(navigationController: navigationController)
34 | appendChildCoordinator(coordinator: coordinator)
35 | coordinator.delegate = self
36 | coordinator.loginDelegate = self
37 | coordinator.start()
38 | }
39 |
40 | func connectSignupCoordinator() {
41 | let coordinator = SignupCoordinator(navigationController: navigationController)
42 | appendChildCoordinator(coordinator: coordinator)
43 | coordinator.delegate = self
44 | coordinator.signupDelegate = self
45 | coordinator.start()
46 | }
47 | }
48 |
49 | extension AuthCoordinator: CoordinatorDelegate {
50 | func didFinished(childCoordinator: Coordinator) {
51 | removeChildCoordinator(coordinator: childCoordinator)
52 | navigationController.viewControllers = []
53 | delegate?.didFinished(childCoordinator: self)
54 | }
55 | }
56 |
57 | extension AuthCoordinator: LoginCoordinatorDelegate, SginupCoordinatorDelegate {
58 | func switchToSignup() {
59 | resetViews()
60 | connectSignupCoordinator()
61 | }
62 |
63 | func switchToLogin() {
64 | resetViews()
65 | connectLoginCoordinator()
66 | }
67 |
68 | private func resetViews() {
69 | navigationController.viewControllers = []
70 | removeAllChildCoordinator()
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/Auth/Coordinator/LoginCoordinator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginCoordinator.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/14.
6 | //
7 |
8 | import UIKit
9 | import RxSwift
10 |
11 | protocol LoginCoordinatorProtocol: Coordinator {
12 | func showLoginViewController()
13 | func showSignupViewController()
14 | }
15 |
16 | protocol LoginCoordinatorDelegate: AnyObject {
17 | func switchToSignup()
18 | }
19 |
20 | final class LoginCoordinator: LoginCoordinatorProtocol {
21 |
22 | weak var delegate: CoordinatorDelegate?
23 | weak var loginDelegate: LoginCoordinatorDelegate?
24 |
25 | var navigationController: UINavigationController
26 |
27 | var childCoordinators: [Coordinator] = []
28 |
29 | required init(navigationController: UINavigationController) {
30 | self.navigationController = navigationController
31 | }
32 |
33 | func start() {
34 | showLoginViewController()
35 | }
36 | func showLoginViewController() {
37 | @DIContainer.Resolve(LoginUseCase.self)
38 | var useCase: LoginUseCase
39 | let viewModel = LoginViewModel(
40 | coordinator: self,
41 | useCase: useCase
42 | )
43 | let viewController = LoginViewController(viewModel: viewModel)
44 | let transiton = CATransition()
45 | transiton.type = .moveIn
46 | transiton.subtype = .fromLeft
47 | transiton.duration = 0.3
48 | navigationController.view.layer.add(transiton, forKey: "login")
49 | navigationController.pushViewController(viewController, animated: true)
50 | self.navigationController.isNavigationBarHidden = true
51 | }
52 |
53 | func showSignupViewController() {
54 | loginDelegate?.switchToSignup()
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/Auth/Coordinator/SignupCoordinator .swift:
--------------------------------------------------------------------------------
1 | //
2 | // SignupCoordinator .swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/14.
6 | //
7 |
8 | import UIKit
9 |
10 | protocol SignupCoordinatorProtocol: Coordinator {
11 | func showSignupViewController()
12 | func showLoginViewController()
13 | }
14 |
15 | protocol SginupCoordinatorDelegate: AnyObject {
16 | func switchToLogin()
17 | }
18 |
19 | final class SignupCoordinator: SignupCoordinatorProtocol {
20 |
21 | weak var delegate: CoordinatorDelegate?
22 | weak var signupDelegate: SginupCoordinatorDelegate?
23 |
24 | var navigationController: UINavigationController
25 |
26 | var childCoordinators: [Coordinator] = []
27 |
28 | required init(navigationController: UINavigationController) {
29 | self.navigationController = navigationController
30 | }
31 |
32 | func start() {
33 | showSignupViewController()
34 | }
35 |
36 | func showSignupViewController() {
37 | @DIContainer.Resolve(SignUpUsecase.self)
38 | var useCase: SignUpUsecase
39 | let viewModel = SignUpViewModel(
40 | coordinator: self,
41 | useCase: useCase
42 | )
43 | let transiton = CATransition()
44 | transiton.type = .moveIn
45 | transiton.subtype = .fromRight
46 | transiton.duration = 0.3
47 | navigationController.view.layer.add(transiton, forKey: "signup")
48 | let viewController = SignupViewController(viewModel: viewModel)
49 | navigationController.pushViewController(viewController, animated: true)
50 | }
51 |
52 | func showLoginViewController() {
53 | signupDelegate?.switchToLogin()
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/Base/BaseViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseViewModel.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/16.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol BaseViewModel {
12 | associatedtype Input
13 | associatedtype Output
14 |
15 | var disposeBag: DisposeBag { get }
16 |
17 | func transform(input: Input) -> Output
18 | }
19 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/Base/MapBase/MapBaseViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapBaseViewModel.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/28.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | class MapBaseViewModel: BaseViewModel {
12 |
13 | // MARK: - Input
14 | struct Input {
15 | let viewWillDisappearEvent: Observable
16 | let focusButtonTapped: Observable
17 | }
18 | // MARK: - Output
19 | struct Output {
20 | var user = PublishSubject()
21 | var isAvailableLocationAuthorization = PublishSubject<(Bool, Coordinate?)>()
22 | var focusUserEvent = PublishSubject()
23 | }
24 | // MARK: - Properties
25 | var disposeBag = DisposeBag()
26 |
27 | // MARK: - Dependency
28 | let mapBaseUseCase: MapBaseUseCase
29 |
30 | // MARK: - Lifecycles
31 | init(useCase: MapBaseUseCase) {
32 | self.mapBaseUseCase = useCase
33 | }
34 |
35 | // MARK: - Helpers
36 | func transform(input: Input) -> Output {
37 | let output = Output()
38 |
39 | mapBaseUseCase.user
40 | .bind(to: output.user)
41 | .disposed(by: disposeBag)
42 |
43 | input.viewWillDisappearEvent
44 | .subscribe(onNext: { [weak self] in
45 | guard let self else { return }
46 | self.mapBaseUseCase.stop()
47 | })
48 | .disposed(by: disposeBag)
49 |
50 | input.focusButtonTapped
51 | .subscribe(onNext: { [weak self] in
52 | guard let self,
53 | let userLocation = try? self.mapBaseUseCase.userLocation.value() else { return }
54 | output.focusUserEvent.onNext(userLocation)
55 | })
56 | .disposed(by: disposeBag)
57 |
58 | mapBaseUseCase.isAvailableLocationAuthorization()
59 | .bind(to: output.isAvailableLocationAuthorization)
60 | .disposed(by: disposeBag)
61 |
62 | return output
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Community/Cell/CommunityDetailCommentHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommunityDetailCommentHeaderView.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/05.
6 | //
7 |
8 | import UIKit
9 |
10 | final class CommunityDetailCommentHeaderView: UICollectionReusableView {
11 | // MARK: - UI properties
12 | private lazy var titleLabel: UILabel = UILabel().then {
13 | $0.text = "댓글"
14 | $0.font = .largeTitle
15 | $0.textColor = .pointLight
16 | }
17 |
18 | // MARK: - Properties
19 | static let identifier = "CommunityDetailCommentHeaderView"
20 |
21 | // MARK: - Lifecycles
22 | override init(frame: CGRect) {
23 | super.init(frame: frame)
24 | setupViews()
25 | configureUI()
26 | }
27 |
28 | required init?(coder: NSCoder) {
29 | fatalError("init(coder:) has not been implemented")
30 | }
31 |
32 | // MARK: - Helpers
33 | private func setupViews() {
34 | addSubview(titleLabel)
35 | }
36 |
37 | private func configureUI() {
38 | titleLabel.snp.makeConstraints {
39 | $0.leading.trailing.bottom.equalToSuperview()
40 | $0.top.equalToSuperview().offset(20)
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Community/Cell/CommunityIndicatorCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommunityIndicatorCell.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/12.
6 | //
7 |
8 | import UIKit
9 |
10 | final class CommunityIndicatorCell: UICollectionViewCell {
11 | // MARK: - UI properties
12 | var indicatorView: UIActivityIndicatorView = UIActivityIndicatorView().then {
13 | $0.style = .large
14 | }
15 |
16 | // MARK: - Properties
17 | static let identifier = "CommunityIndicatorCell"
18 |
19 | // MARK: - Lifecycles
20 | override init(frame: CGRect) {
21 | super.init(frame: frame)
22 | setupViews()
23 | configureUI()
24 | }
25 |
26 | required init?(coder: NSCoder) {
27 | fatalError("init(coder:) has not been implemented")
28 | }
29 |
30 | // MARK: - Helpers
31 | private func setupViews() {
32 | contentView.addSubview(indicatorView)
33 | }
34 |
35 | private func configureUI() {
36 | indicatorView.snp.makeConstraints {
37 | $0.center.equalToSuperview()
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Community/ViewModel/CommunityPostWriteViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommunityPostWriteViewModel.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxRelay
11 |
12 | final class CommunityPostWriteViewModel: BaseViewModel {
13 | struct Input {
14 | var viewWillAppearEvent: Observable
15 | var rightButtonTapped: Observable<(String, Image?)>
16 | }
17 |
18 | struct Output {
19 | var post = PublishRelay()
20 | }
21 |
22 | // MARK: - Dependency
23 | var disposeBag = DisposeBag()
24 | private let useCase: CommunityPostWriteUseCase
25 | private let coordinator: CommunityCoordinator
26 |
27 | // MARK: - Lifecycles
28 | init(useCase: CommunityPostWriteUseCase, coordinator: CommunityCoordinator) {
29 | self.useCase = useCase
30 | self.coordinator = coordinator
31 | }
32 |
33 | // MARK: - Helpers
34 | func transform(input: Input) -> Output {
35 | let output = Output()
36 |
37 | input.viewWillAppearEvent
38 | .subscribe(onNext: { [weak self] _ in
39 | guard let self else { return }
40 | self.useCase.confirmHavePost()
41 | .subscribe(onSuccess: { post in
42 | if let post {
43 | output.post.accept(post)
44 | }
45 | }).disposed(by: self.disposeBag)
46 | }).disposed(by: disposeBag)
47 |
48 | input.rightButtonTapped
49 | .subscribe(onNext: { [weak self] (postContent, image) in
50 | guard let self else { return }
51 | self.useCase.uploadPost(postContent: postContent, image: image)
52 | .subscribe(onSuccess: { _ in
53 | self.coordinator.popLastViewController()
54 | })
55 | .disposed(by: self.disposeBag)
56 | }).disposed(by: disposeBag)
57 |
58 | return output
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Coordinator/RecordCoordinator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordCoordinator.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/14.
6 | //
7 |
8 | import UIKit
9 |
10 | protocol RecordCoordinatorProtocol: Coordinator {
11 | func showRecordPageViewController()
12 | }
13 |
14 | final class RecordCoordinator: RecordCoordinatorProtocol {
15 | var navigationController: UINavigationController
16 | var childCoordinators: [Coordinator] = []
17 | weak var delegate: CoordinatorDelegate?
18 |
19 | init(navigationController: UINavigationController) {
20 | self.navigationController = navigationController
21 | self.navigationController.isNavigationBarHidden = false
22 | }
23 |
24 | func start() {
25 | showRecordPageViewController()
26 | }
27 |
28 | func showRecordPageViewController() {
29 | @DIContainer.Resolve(RecordMainViewUseCase.self)
30 | var mainUseCase: RecordMainViewUseCase
31 | let mainViewModel = RecordMainViewModel(useCase: mainUseCase)
32 |
33 | @DIContainer.Resolve(RecordMapViewUseCase.self)
34 | var mapUseCase: RecordMapViewUseCase
35 | let mapViewModel = RecordMapViewModel(useCase: mapUseCase)
36 |
37 | let viewController = RecordPageViewController(
38 | recordMainViewController: RecordMainViewController(viewModel: mainViewModel),
39 | recordMapViewController: RecordMapViewController(viewModel: mapViewModel)
40 | )
41 | navigationController.pushViewController(viewController, animated: true)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/Common/PaddingLabel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UILabel+Extension.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/15.
6 | //
7 |
8 | import UIKit
9 |
10 | final class PaddingLabel: UILabel {
11 | private let topInset: CGFloat
12 | private let bottomInset: CGFloat
13 | private let leftInset: CGFloat
14 | private let rightInset: CGFloat
15 |
16 | init(
17 | topInset: CGFloat?,
18 | bottomInset: CGFloat?,
19 | leftInset: CGFloat?,
20 | rightInset: CGFloat?
21 | ) {
22 | self.topInset = topInset ?? 0
23 | self.bottomInset = bottomInset ?? 0
24 | self.leftInset = leftInset ?? 0
25 | self.rightInset = rightInset ?? 0
26 | super.init(frame: .zero)
27 | }
28 |
29 | required init?(coder: NSCoder) {
30 | fatalError("init(coder:) has not been implemented")
31 | }
32 |
33 | override func drawText(in rect: CGRect) {
34 | let insets = UIEdgeInsets(
35 | top: topInset,
36 | left: leftInset,
37 | bottom: bottomInset,
38 | right: rightInset
39 | )
40 | super.drawText(in: rect.inset(by: insets))
41 | }
42 |
43 | override var intrinsicContentSize: CGSize {
44 | let size = super.intrinsicContentSize
45 | let newWidth = size.width + leftInset + rightInset
46 | let newHeight = size.height + topInset + bottomInset
47 | return CGSize(width: newWidth, height: newHeight)
48 | }
49 |
50 | override var bounds: CGRect {
51 | didSet {
52 | preferredMaxLayoutWidth = bounds.width - (leftInset + rightInset)
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/InGameMenus/ViewController/InGamePlayMenuViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SingGamePlayRankingView.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/15.
6 | //
7 |
8 | import UIKit
9 | import SnapKit
10 | import Then
11 |
12 | class InGamePlayMenuViewController: UIViewController {
13 |
14 | // MARK: - UI properties
15 | lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: createBasicListLayout())
16 |
17 | let titleLabel = UILabel().then {
18 | $0.font = .largeTitle
19 | $0.textColor = .white
20 | $0.backgroundColor = .pointLight
21 | $0.textAlignment = .center
22 | $0.translatesAutoresizingMaskIntoConstraints = false
23 | }
24 |
25 | // MARK: - Properties
26 | // MARK: - Lifecycles
27 |
28 | override func viewDidLoad() {
29 | super.viewDidLoad()
30 | layout()
31 | }
32 |
33 | // MARK: - Helpers
34 | private func createBasicListLayout() -> UICollectionViewLayout {
35 | let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
36 | heightDimension: .fractionalHeight(1.0))
37 | let item = NSCollectionLayoutItem(layoutSize: itemSize)
38 |
39 | let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
40 | heightDimension: .absolute(44))
41 | let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
42 | subitems: [item])
43 |
44 | let section = NSCollectionLayoutSection(group: group)
45 |
46 | let layout = UICollectionViewCompositionalLayout(section: section)
47 | return layout
48 | }
49 |
50 | }
51 |
52 | extension InGamePlayMenuViewController {
53 | private func layout() {
54 | addViews()
55 | layoutViews()
56 | }
57 |
58 | private func addViews() {
59 | view.addSubview(titleLabel)
60 | view.addSubview(collectionView)
61 | }
62 |
63 | private func layoutViews() {
64 | titleLabel.snp.makeConstraints {
65 | $0.top.leading.trailing.equalToSuperview()
66 | $0.height.equalTo(100)
67 |
68 | }
69 |
70 | collectionView.snp.makeConstraints {
71 | $0.top.equalTo(titleLabel.snp.bottom)
72 | $0.leading.trailing.bottom.equalToSuperview()
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/InGameMenus/ViewModel/InGameRankingViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InGameRankingViewModel.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/21.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import Firebase
11 |
12 | class InGameRankingViewModel: BaseViewModel {
13 | struct Input {
14 |
15 | }
16 | struct Output {
17 | var rankings: Single<[InGameRanking]>
18 | }
19 | private let inGameUseCase: InGameUseCase
20 | var disposeBag = DisposeBag()
21 |
22 | init(inGameUseCase: InGameUseCase) {
23 | self.inGameUseCase = inGameUseCase
24 | }
25 |
26 | func transform(input: Input) -> Output {
27 | Output(rankings: inGameUseCase.fetchRanking())
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/InGameMenus/ViewModel/InGameRecordViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InGameRecordViewModel.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/21.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxRelay
11 | import Firebase
12 |
13 | final class InGameRecordViewModel: BaseViewModel {
14 |
15 | struct Input {
16 |
17 | }
18 |
19 | struct Output {
20 | var inGameRecord: PublishSubject<[InGameRecord]>
21 | }
22 |
23 | private let inGameUseCase: InGameUseCase
24 | var disposeBag = DisposeBag()
25 |
26 | init(inGameUseCase: InGameUseCase) {
27 | self.inGameUseCase = inGameUseCase
28 | }
29 |
30 | func transform(input: Input) -> Output {
31 | inGameUseCase.fetchRecord()
32 | return Output(inGameRecord: inGameUseCase.inGameRecord)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/MultiGame/View/ChatCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChatCell.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/08.
6 | //
7 |
8 | import UIKit
9 | import SnapKit
10 | import Then
11 |
12 | final class ChatCell: UICollectionViewCell {
13 | static let identifier = String(describing: ChatCell.self)
14 |
15 | private lazy var textLabel = PaddingLabel(topInset: 10, bottomInset: 10, leftInset: 20, rightInset: 20).then {
16 | $0.font = .commentBody
17 | $0.textColor = .black
18 | $0.numberOfLines = 0
19 | }
20 |
21 | override init(frame: CGRect) {
22 | super.init(frame: frame)
23 | configureUI()
24 | }
25 |
26 | required init?(coder: NSCoder) {
27 | fatalError("init(coder:) has not been implemented")
28 | }
29 |
30 | private func configureUI() {
31 | addSubview(textLabel)
32 | textLabel.snp.makeConstraints {
33 | $0.edges.equalToSuperview()
34 | }
35 | }
36 |
37 | func bind(data: Chat) {
38 | textLabel.text = "\(data.nickName) : \(data.text)"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/MultiGame/View/GameRankCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GameRankCollectionViewCell.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/07.
6 | //
7 |
8 | import UIKit
9 | import Then
10 | import SnapKit
11 |
12 | final class GameRankCell: UICollectionViewCell {
13 | static let identifier = String(describing: GameRankCell.self)
14 | private let rankingLabel = PaddingLabel(topInset: 10, bottomInset: 10, leftInset: 10, rightInset: 10).then {
15 | $0.font = .smallTitle
16 | }
17 |
18 | override init(frame: CGRect) {
19 | super.init(frame: frame)
20 | layout()
21 | }
22 |
23 | required init?(coder: NSCoder) {
24 | fatalError("init(coder:) has not been implemented")
25 | }
26 |
27 | private func layout() {
28 | addSubview(rankingLabel)
29 | rankingLabel.snp.makeConstraints {
30 | $0.edges.equalToSuperview()
31 | }
32 | }
33 |
34 | override func prepareForReuse() {
35 | super.prepareForReuse()
36 | rankingLabel.text = ""
37 | }
38 |
39 | func bind(data: MultiGamePlayerData, rank: Int) {
40 | let nickName = data.nickName.stringLimit(6)
41 | rankingLabel.text = "\(rank) : \(nickName) \(data.point) 점"
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/MultiGame/View/GameRoomCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GameRoomCollectionViewCell.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/24.
6 | //
7 |
8 | import UIKit
9 | import Then
10 | import SnapKit
11 |
12 | final class GameRoomCell: UICollectionViewCell {
13 | static let identifier = "gameRoomCollectionViewCell"
14 | private lazy var nameLabel = PaddingLabel(
15 | topInset: 0,
16 | bottomInset: 0,
17 | leftInset: 12,
18 | rightInset: 12
19 | ).then {
20 | $0.numberOfLines = 0
21 | $0.font = .commentBody
22 | $0.textColor = .pointDark
23 | }
24 |
25 | override init(frame: CGRect) {
26 | super.init(frame: .zero)
27 | layout()
28 | }
29 |
30 | required init?(coder: NSCoder) {
31 | fatalError("init(coder:) has not been implemented")
32 | }
33 |
34 | override func prepareForReuse() {
35 | super.prepareForReuse()
36 | nameLabel.text = ""
37 | }
38 | }
39 |
40 | extension GameRoomCell {
41 | func layout() {
42 | addViews()
43 | addConstratins()
44 | }
45 |
46 | func addViews() {
47 | addSubview(nameLabel)
48 | }
49 |
50 | func addConstratins() {
51 | nameLabel.snp.makeConstraints {
52 | $0.edges.equalToSuperview()
53 | }
54 | }
55 | }
56 |
57 | extension GameRoomCell {
58 | func bind(data: RoomUser) {
59 | nameLabel.text = data.nickName
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/MultiGame/View/GameRoomHeader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GameRoomCollectionViewHeader.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/24.
6 | //
7 |
8 | import UIKit
9 | import SnapKit
10 | import Then
11 |
12 | final class GameRoomHeader: UICollectionReusableView {
13 | static let identifier = "GameRoomCollectionViewHeader"
14 | private let headerLabel: PaddingLabel = PaddingLabel(
15 | topInset: 0,
16 | bottomInset: 0,
17 | leftInset: 10,
18 | rightInset: 0
19 | ).then {
20 | $0.font = .title
21 | $0.textColor = .white
22 | $0.textAlignment = .left
23 | }
24 | override init(frame: CGRect) {
25 | super.init(frame: frame)
26 | backgroundColor = .pointDark
27 | layout()
28 | }
29 |
30 | required init?(coder: NSCoder) {
31 | fatalError("init(coder:) has not been implemented")
32 | }
33 |
34 | override func prepareForReuse() {
35 | super.prepareForReuse()
36 | headerLabel.text = ""
37 | }
38 | }
39 |
40 | extension GameRoomHeader {
41 | private func layout() {
42 | addViews()
43 | addConstraints()
44 | }
45 |
46 | private func addViews() {
47 | addSubview(headerLabel)
48 | }
49 |
50 | private func addConstraints() {
51 | headerLabel.snp.makeConstraints {
52 | $0.edges.equalToSuperview()
53 | }
54 | }
55 |
56 | func bind(playerNumber: Int) {
57 | headerLabel.text = "플레이어 \(playerNumber) 명"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/MultiGame/View/PlayerAnnotation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PlayerAnnotation.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/06.
6 | //
7 |
8 | import Foundation
9 | import MapKit
10 |
11 | final class PlayerAnnotation: NSObject, MKAnnotation {
12 |
13 | enum PlayerType: Int {
14 | case first = 0
15 | case second = 1
16 | case third = 2
17 | case fourth = 3
18 | }
19 |
20 | var coordinate: CLLocationCoordinate2D
21 | let nickName: String
22 | let point: Int
23 | private let type: PlayerType
24 | let title: String?
25 | let subtitle: String?
26 |
27 | init(player: MultiGamePlayerData, type: PlayerType) {
28 | self.coordinate = player.currentLocation == nil ? CLLocationCoordinate2D(
29 | latitude: 0,
30 | longitude: 0
31 | ) : player.currentLocation!.toCLLocationCoordinate2D()
32 | self.nickName = player.nickName
33 | self.point = player.point
34 | self.type = type
35 | self.title = player.nickName
36 | self.subtitle = "\(player.point) 점"
37 | super.init()
38 | }
39 |
40 | func image() -> UIImage? {
41 | switch type {
42 | case .first:
43 | return .firstAnnotation
44 | case .second:
45 | return .secondAnnotation
46 | case .third:
47 | return .thirdAnnotation
48 | case .fourth:
49 | return .fourthAnnotation
50 | }
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/MultiGame/View/PlayerAnnotationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PlayerAnnotationView.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/07.
6 | //
7 |
8 | import Foundation
9 | import MapKit
10 |
11 | final class PlayerAnnotationView: MKAnnotationView {
12 |
13 | static let identifier = String(describing: PlayerAnnotationView.self)
14 |
15 | override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
16 | super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
17 | guard let playerAnnotation = self.annotation as? PlayerAnnotation else {return}
18 | image = playerAnnotation.image()?.imageWith(newSize: CGSize(width: 30, height: 30))
19 | }
20 |
21 | required init?(coder aDecoder: NSCoder) {
22 | fatalError("init(coder:) has not been implemented")
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/MultiGame/View/PlayerCircle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PlayerCircle.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/07.
6 | //
7 |
8 | import Foundation
9 | import MapKit
10 |
11 | final class PlayerCircle: MKCircle {
12 |
13 | var type: CircleType = .first
14 |
15 | func overlayColor() -> UIColor? {
16 | switch type {
17 | case .first:
18 | return .red
19 | case .second:
20 | return .purple
21 | case .third:
22 | return .blue
23 | case .fourth:
24 | return .black
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Home/SelectMap/View/SelectMapRankingHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SelectMapRankingHeaderView.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/22.
6 | //
7 |
8 | import UIKit
9 | import Then
10 | import SnapKit
11 |
12 | final class SelectMapRankingHeaderView: UICollectionReusableView {
13 | // MARK: - UI properties
14 | private let titleLabel: UILabel = UILabel().then {
15 | $0.font = .boldBody
16 | $0.textColor = .white
17 | $0.text = "땅 이름 랭킹"
18 | $0.clipsToBounds = true
19 | }
20 |
21 | private let closeButton: UIButton = UIButton().then {
22 | $0.setImage(SystemImageNameSpace.xmark.uiImage, for: .normal)
23 | $0.tintColor = .white
24 | let imageConfig = UIImage.SymbolConfiguration(pointSize: 24)
25 | $0.setPreferredSymbolConfiguration(imageConfig, forImageIn: .normal)
26 | }
27 |
28 | // MARK: - Properties
29 | static let identifier = "SelectMapRankingHeaderView"
30 | private var closeButtonHandler: (() -> Void)?
31 |
32 | // MARK: - Lifecycles
33 | override init(frame: CGRect) {
34 | super.init(frame: frame)
35 | configureUI()
36 | closeButton.addTarget(self,
37 | action: #selector(closeButtonDidClick),
38 | for: .touchUpInside)
39 | }
40 |
41 | required init?(coder: NSCoder) {
42 | fatalError("init(coder:) has not been implemented")
43 | }
44 |
45 | // MARK: - Helpers
46 | @objc func closeButtonDidClick() {
47 | closeButtonHandler?()
48 | }
49 |
50 | private func configureUI() {
51 | backgroundColor = .pointLight
52 |
53 | addSubview(closeButton)
54 | closeButton.snp.makeConstraints {
55 | $0.top.trailing.bottom.equalToSuperview().inset(15)
56 | $0.width.height.equalTo(30)
57 | }
58 |
59 | addSubview(titleLabel)
60 | titleLabel.snp.makeConstraints {
61 | $0.top.leading.bottom.equalToSuperview().inset(15)
62 | $0.trailing.equalTo(closeButton.snp.leading).inset(15)
63 | }
64 | }
65 |
66 | func setData(mapName: String, closeButtonHandler: (() -> Void)?) {
67 | titleLabel.text = mapName + " 랭킹"
68 | self.closeButtonHandler = closeButtonHandler
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/MyPage/View/BadgeCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BadgeCell.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/30.
6 | //
7 |
8 | import UIKit
9 | import Then
10 | import SnapKit
11 | import RxSwift
12 |
13 | final class BadgeCell: UICollectionViewCell {
14 | // MARK: - UI properties
15 | private lazy var badgeLabel: UILabel = UILabel().then {
16 | $0.font = .subBody
17 | $0.textAlignment = .center
18 | }
19 | private lazy var badgeImage: UIImageView = UIImageView().then {
20 | $0.contentMode = .scaleAspectFit
21 | $0.layer.cornerRadius = 50
22 | $0.layer.borderWidth = 3
23 | $0.layer.borderColor = UIColor.pointLight.cgColor
24 | $0.clipsToBounds = true
25 | }
26 | // MARK: - Properties
27 | static let identifer = "BadgeCell"
28 |
29 | // MARK: - Lifecycles
30 | override init(frame: CGRect) {
31 | super.init(frame: frame)
32 | setupSubviews()
33 | }
34 |
35 | required init?(coder: NSCoder) {
36 | fatalError("init(coder:) has not been implemented")
37 | }
38 | // MARK: - Helpers
39 | private func setupSubviews() {
40 | [badgeLabel, badgeImage].forEach { contentView.addSubview($0) }
41 |
42 | badgeImage.snp.makeConstraints {
43 | $0.top.centerX.equalToSuperview()
44 | $0.width.height.equalTo(100)
45 | }
46 |
47 | badgeLabel.snp.makeConstraints {
48 | $0.top.equalTo(badgeImage.snp.bottom).offset(5)
49 | $0.leading.trailing.bottom.equalToSuperview()
50 | $0.height.equalTo(20)
51 | }
52 | }
53 |
54 | func bind(badge: Badge) {
55 | badgeImage.image = UIImage(data: badge.image)
56 | badgeLabel.text = badge.name
57 | }
58 |
59 | func bind(pinCharacter: PinCharacter) {
60 | badgeImage.image = pinCharacter.image
61 | badgeLabel.text = pinCharacter.name
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/MyPage/View/InfoTextFieldView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InfoTextFieldView.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/12/08.
6 | //
7 |
8 | import UIKit
9 | import SnapKit
10 | import Then
11 |
12 | final class InfoTextFieldView: UIView {
13 |
14 | // MARK: - UI properties
15 | private var label: UILabel = UILabel().then {
16 | $0.textColor = .pointLight
17 | $0.font = .title
18 | }
19 |
20 | var textField: UITextField = UITextField().then {
21 | $0.textColor = .black
22 | $0.layer.cornerRadius = 10
23 | $0.layer.borderColor = UIColor.pointLight.cgColor
24 | $0.layer.borderWidth = 2
25 | $0.addLeftRightPadding()
26 | }
27 |
28 | // MARK: - Properties
29 |
30 | // MARK: - Lifecycles
31 | init(frame: CGRect, title: String) {
32 | label.text = title
33 | super.init(frame: frame)
34 | configureUI()
35 | }
36 |
37 | required init?(coder: NSCoder) {
38 | fatalError("init(coder:) has not been implemented")
39 | }
40 | }
41 |
42 | extension InfoTextFieldView {
43 | // MARK: - Helpers
44 | private func configureUI() {
45 | addSubview(label)
46 | addSubview(textField)
47 |
48 | label.snp.makeConstraints {
49 | $0.centerY.leading.equalToSuperview()
50 | $0.width.equalTo(60)
51 | }
52 | textField.snp.makeConstraints {
53 | $0.leading.equalTo(label.snp.trailing).offset(5)
54 | $0.trailing.centerY.equalToSuperview()
55 | $0.height.equalTo(40)
56 | }
57 | }
58 |
59 | func setContent(_ content: String) {
60 | textField.text = content
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/MyPage/View/MyPageHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MyPageHeaderView.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/30.
6 | //
7 |
8 | import UIKit
9 | import Then
10 | import SnapKit
11 |
12 | final class MyPageHeaderView: UICollectionReusableView {
13 | // MARK: - UI properties
14 | private lazy var titleLabel: UILabel = UILabel().then {
15 | $0.font = .smallTitle
16 | $0.textColor = .white
17 | }
18 | private lazy var moreButton: UIButton = UIButton().then {
19 | $0.setTitle("더보기 >", for: .normal)
20 | $0.setTitleColor(.white, for: .normal)
21 | $0.titleLabel?.font = .tinyTitle
22 | $0.isHidden = true
23 | }
24 |
25 | // MARK: - Properties
26 | static let identifer = "MyPageHeaderView"
27 | private var moreButtonHandler: () -> Void = {}
28 |
29 | // MARK: - Lifecycles
30 | override init(frame: CGRect) {
31 | super.init(frame: frame)
32 | configureUI()
33 | setUpMoreButton()
34 | }
35 |
36 | required init?(coder: NSCoder) {
37 | fatalError("init(coder:) has not been implemented")
38 | }
39 |
40 | // MARK: - Helpers
41 | private func configureUI() {
42 | backgroundColor = .pointLight
43 | layer.cornerRadius = 10
44 |
45 | [titleLabel, moreButton].forEach { addSubview($0) }
46 | titleLabel.snp.makeConstraints {
47 | $0.leading.equalToSuperview().offset(15)
48 | $0.top.bottom.equalToSuperview().inset(10)
49 | }
50 |
51 | moreButton.snp.makeConstraints {
52 | $0.trailing.equalToSuperview().offset(-15)
53 | $0.top.bottom.equalTo(titleLabel)
54 | }
55 | }
56 |
57 | private func setUpMoreButton() {
58 | moreButton.addTarget(self,
59 | action: #selector(moreButtonDidClick),
60 | for: .touchUpInside)
61 | }
62 |
63 | @objc func moreButtonDidClick() {
64 | moreButtonHandler()
65 | }
66 |
67 | func bind(title: String, moreButtonHandler: (() -> Void)?) {
68 | titleLabel.text = title
69 | if let handler = moreButtonHandler {
70 | moreButton.isHidden = false
71 | self.moreButtonHandler = handler
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/MyPage/View/SettingCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingCell.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/12/01.
6 | //
7 |
8 | import UIKit
9 | import Then
10 | import SnapKit
11 |
12 | final class SettingCell: UICollectionViewCell {
13 | // MARK: - UI properties
14 | private lazy var button: UIButton = UIButton().then {
15 | $0.titleLabel?.font = .commentBody
16 | $0.setTitleColor(.black, for: .normal)
17 | $0.contentHorizontalAlignment = .left
18 | }
19 |
20 | // MARK: - Properties
21 | static let identifer = "SettingCell"
22 | var buttonHandler: (() -> Void)?
23 |
24 | // MARK: - Lifecycles
25 | override init(frame: CGRect) {
26 | super.init(frame: frame)
27 | setupSubviews()
28 | setupButton()
29 | }
30 |
31 | required init?(coder: NSCoder) {
32 | fatalError("init(coder:) has not been implemented")
33 | }
34 |
35 | // MARK: - Helpers
36 | private func setupSubviews() {
37 | addSubview(button)
38 |
39 | button.snp.makeConstraints {
40 | $0.top.bottom.equalToSuperview()
41 | $0.leading.trailing.equalToSuperview().inset(10)
42 | }
43 | }
44 |
45 | private func setupButton() {
46 | button.addTarget(self,
47 | action: #selector(buttonDidClick),
48 | for: .touchUpInside)
49 | }
50 |
51 | @objc func buttonDidClick() {
52 | buttonHandler?()
53 | }
54 |
55 | func bind(title: String, handler: (() -> Void)?) {
56 | button.setTitle(title, for: .normal)
57 | self.buttonHandler = handler
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/MyPage/ViewModel/BadgeViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BadgeViewModel.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/30.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | class BadgeViewModel: BaseViewModel {
12 | struct Input {
13 |
14 | }
15 |
16 | struct Output {
17 | var brandNewBadges = BehaviorSubject<[Badge]>(value: [])
18 | var aquiredBadges = BehaviorSubject<[Badge]>(value: [])
19 | var inaquiredBadges = BehaviorSubject<[Badge]>(value: [])
20 | }
21 | var disposeBag = DisposeBag()
22 | let allBadges: [Badge]
23 | let ownedBadges: [Badge]
24 |
25 | init(allBadges: [Badge],
26 | ownedBadges: [Badge]
27 | ) {
28 | self.allBadges = allBadges
29 | self.ownedBadges = ownedBadges
30 | }
31 |
32 | func transform(input: Input) -> Output {
33 | let output = Output()
34 | output.brandNewBadges
35 | .onNext(ownedBadges)
36 | output.aquiredBadges
37 | .onNext(allBadges.filter { $0.isOwn })
38 | output.inaquiredBadges
39 | .onNext(allBadges.filter { !$0.isOwn })
40 | return output
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Record/View/RecordMainHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordHeaderView.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/15.
6 | //
7 |
8 | import UIKit
9 | import RxSwift
10 | import RxRelay
11 |
12 | class RecordMainHeaderView: UICollectionReusableView {
13 | // MARK: - UI properties
14 | private lazy var dateLabel = UILabel().then {
15 | $0.font = .largeTitle
16 | $0.textColor = .pointLight
17 | $0.textAlignment = .center
18 | }
19 |
20 | private lazy var distanceLabel = UILabel().then {
21 | $0.font = .smallTitle
22 | $0.textColor = .pointLight
23 | $0.textAlignment = .center
24 | }
25 |
26 | private lazy var kcalLabel = UILabel().then {
27 | $0.font = .smallTitle
28 | $0.textColor = .pointLight
29 | $0.textAlignment = .center
30 | }
31 |
32 | // MARK: - Properties
33 | static let identifier = "RecordHeaderView"
34 | var bindEvent = PublishRelay()
35 |
36 | // MARK: - Lifecycles
37 | override init(frame: CGRect) {
38 | super.init(frame: frame)
39 | setUpSubviews()
40 | }
41 |
42 | required init?(coder: NSCoder) {
43 | fatalError("init(coder:) has not been implemented")
44 | }
45 |
46 | // MARK: - Helpers
47 |
48 | private func setUpSubviews() {
49 | [dateLabel, distanceLabel, kcalLabel].forEach {
50 | addSubview($0)
51 | }
52 |
53 | configureUI()
54 | }
55 |
56 | private func configureUI() {
57 | dateLabel.snp.makeConstraints {
58 | $0.top.leading.trailing.equalToSuperview()
59 | $0.height.equalTo(50)
60 | }
61 |
62 | distanceLabel.snp.makeConstraints {
63 | $0.top.equalTo(dateLabel.snp.bottom)
64 | $0.leading.trailing.equalToSuperview()
65 | $0.height.equalTo(25)
66 | }
67 |
68 | kcalLabel.snp.makeConstraints {
69 | $0.top.equalTo(distanceLabel.snp.bottom)
70 | $0.leading.trailing.bottom.equalToSuperview()
71 | }
72 | }
73 |
74 | func bind(headerRecord: RecordViewHeaderRecord) {
75 | dateLabel.text = headerRecord.date
76 | distanceLabel.text = "누적 거리: \(headerRecord.distance.convertToDecimal) m"
77 | kcalLabel.text = "누적 칼로리: \(headerRecord.kcal.convertToDecimal) kcal"
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Record/View/RecordMapCategoryCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordMapCategoryCell.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/22.
6 | //
7 |
8 | import UIKit
9 |
10 | class RecordMapCategoryCell: UICollectionViewCell {
11 | // MARK: - UI properties
12 | private lazy var locationNameLabel = UILabel().then {
13 | $0.layer.backgroundColor = UIColor.pointLight.cgColor
14 | $0.layer.cornerRadius = 15
15 | $0.font = UIFont.boldBody
16 | $0.textColor = .white
17 | $0.textAlignment = .center
18 | }
19 | // MARK: - Properties
20 | static let identifier = "RecordMapCategoryCell"
21 |
22 | // MARK: - Lifecycles
23 | override init(frame: CGRect) {
24 | super.init(frame: frame)
25 | setUpSubviews()
26 | configureUI()
27 | }
28 |
29 | required init?(coder: NSCoder) {
30 | fatalError("init(coder:) has not been implemented")
31 | }
32 |
33 | // MARK: - Helpers
34 |
35 | private func setUpSubviews() {
36 | contentView.addSubview(locationNameLabel)
37 | }
38 |
39 | private func configureUI() {
40 | locationNameLabel.snp.makeConstraints {
41 | $0.top.bottom.leading.trailing.equalToSuperview()
42 | }
43 | }
44 |
45 | func setLocationName(name: String) {
46 | locationNameLabel.text = name
47 | }
48 |
49 | func getLocationName() -> String {
50 | guard let name = locationNameLabel.text else { return "" }
51 | return name
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Record/View/RecordMapHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordMapHeaderView.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/22.
6 | //
7 |
8 | import UIKit
9 | import RxSwift
10 | import RxRelay
11 |
12 | class RecordMapHeaderView: UICollectionReusableView {
13 | // MARK: - UI properties
14 | private lazy var mapName = UILabel().then {
15 | $0.textColor = .pointLight
16 | $0.font = .largeTitle
17 | $0.textAlignment = .right
18 | }
19 |
20 | private lazy var downButton = UIButton().then {
21 | $0.tintColor = .pointLight
22 | $0.setImage(SystemImageNameSpace.chevronDown.uiImage, for: .normal)
23 | }
24 |
25 | // MARK: - Properties
26 | static let identifier = "RecordMapHeaderView"
27 | var dropDownMenuTapEvent = PublishRelay()
28 |
29 | // MARK: - Lifecycles
30 | override init(frame: CGRect) {
31 | super.init(frame: frame)
32 | setUpSubviews()
33 | configureUI()
34 | }
35 |
36 | required init?(coder: NSCoder) {
37 | fatalError("init(coder:) has not been implemented")
38 | }
39 |
40 | // MARK: - Helpers
41 | private func setUpSubviews() {
42 | [mapName, downButton].forEach {
43 | addSubview($0)
44 | }
45 | }
46 |
47 | private func configureUI() {
48 | mapName.snp.makeConstraints {
49 | $0.top.bottom.equalToSuperview()
50 | $0.centerX.equalToSuperview()
51 | $0.width.greaterThanOrEqualTo(30)
52 | }
53 |
54 | downButton.snp.makeConstraints {
55 | $0.top.bottom.equalToSuperview()
56 | $0.leading.equalTo(mapName.snp.trailing).offset(5)
57 | $0.width.greaterThanOrEqualTo(30)
58 | }
59 | }
60 |
61 | func setMapName(mapName: String) {
62 | self.mapName.text = mapName
63 | }
64 |
65 | func setDropDownMenus(maps: [Map]) {
66 | var menuItems: [UIAction] = []
67 | maps.forEach {
68 | menuItems.append(
69 | UIAction(title: $0.name) { action in
70 | self.dropDownMenuTapEvent.accept(action.title)
71 | })
72 | }
73 | downButton.menu = UIMenu(children: menuItems)
74 | downButton.showsMenuAsPrimaryAction = true
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Record/View/RecordMapImageCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordMapImageCell.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/13.
6 | //
7 |
8 | import UIKit
9 |
10 | final class RecordMapImageCell: UICollectionViewCell {
11 | private lazy var imageView: UIImageView = UIImageView().then {
12 | $0.clipsToBounds = true
13 | $0.layer.cornerRadius = 10
14 | }
15 |
16 | static let identifier: String = "RecordMapImageCell"
17 |
18 | override init(frame: CGRect) {
19 | super.init(frame: frame)
20 | setupViews()
21 | configureUI()
22 | }
23 |
24 | required init?(coder: NSCoder) {
25 | fatalError("init(coder:) has not been implemented")
26 | }
27 |
28 | func setupViews() {
29 | contentView.addSubview(imageView)
30 | }
31 |
32 | func configureUI() {
33 | imageView.snp.makeConstraints {
34 | $0.edges.equalToSuperview()
35 | }
36 | }
37 |
38 | func bind(data: Data) {
39 | imageView.image = UIImage(data: data)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Record/ViewModel/RecordMainViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordViewModel.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/15.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxRelay
11 |
12 | class RecordMainViewModel: BaseViewModel {
13 |
14 | struct Input {
15 | var viewDidAppearEvent: Observable
16 | }
17 |
18 | struct Output {
19 | var weekDatas = PublishRelay<[RecordViewChartData]>()
20 | var recordSectionDatas = PublishRelay<(allDates: [String],
21 | totalRecordAtDate: [String: DayTotalRecord],
22 | recordsAtDate: [String: [Record]],
23 | mapAtMapId: [Int: Map])>()
24 | }
25 |
26 | var useCase: RecordMainViewUseCase
27 | var disposeBag = DisposeBag()
28 |
29 | init(useCase: RecordMainViewUseCase) {
30 | self.useCase = useCase
31 | }
32 |
33 | func transform(input: Input) -> Output {
34 | let output = Output()
35 |
36 | input.viewDidAppearEvent
37 | .subscribe(onNext: { [weak self] _ in
38 | guard let self else { return }
39 | self.useCase.loadMapData()
40 | .subscribe(onSuccess: { _ in
41 | self.useCase.loadRecordData()
42 | }).disposed(by: self.disposeBag)
43 | }).disposed(by: disposeBag)
44 |
45 | useCase.weekDatas
46 | .bind(to: output.weekDatas)
47 | .disposed(by: disposeBag)
48 |
49 | useCase.recordSectionDatas
50 | .bind(to: output.recordSectionDatas)
51 | .disposed(by: disposeBag)
52 |
53 | return output
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Acha/Acha/Presentation/TabBar/Record/ViewModel/RecordMapViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordMapViewModel.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/22.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxRelay
11 |
12 | final class RecordMapViewModel: BaseViewModel {
13 |
14 | struct Input {
15 | var viewDidAppearEvent: Observable
16 | var sectionHeaderCreateEvent: Observable
17 | var dropDownMenuTapEvent: Observable
18 | var categoryCellTapEvent: Observable
19 | }
20 |
21 | struct Output {
22 | var dropDownMenus = PublishRelay<[Map]>()
23 | var mapNameAndRecordDatas = PublishRelay<(mapImage: Data?, mapName: String, recordDatas: [Record])>()
24 | }
25 |
26 | private let useCase: RecordMapViewUseCase
27 | var disposeBag = DisposeBag()
28 |
29 | init(useCase: RecordMapViewUseCase) {
30 | self.useCase = useCase
31 | }
32 |
33 | func transform(input: Input) -> Output {
34 | let output = Output()
35 |
36 | input.viewDidAppearEvent
37 | .subscribe { [weak self] _ in
38 | guard let self else { return }
39 | self.useCase.loadMapData()
40 | self.useCase.getMapNameAndRecordsAtLocation(location: Locations.incheon.string)
41 | }.disposed(by: disposeBag)
42 |
43 | input.sectionHeaderCreateEvent
44 | .subscribe { [weak self] mapName in
45 | guard let self else { return }
46 | self.useCase.getDropDownMenus(mapName: mapName)
47 | }.disposed(by: disposeBag)
48 |
49 | input.dropDownMenuTapEvent
50 | .subscribe(onNext: { [weak self] mapName in
51 | guard let self else { return }
52 | self.useCase.getMapNameAndRecordDatasAtMapName(mapName: mapName)
53 | }).disposed(by: disposeBag)
54 |
55 | input.categoryCellTapEvent
56 | .subscribe(onNext: { [weak self] category in
57 | guard let self else { return }
58 | self.useCase.getMapNameAndRecordsAtLocation(location: category)
59 | }).disposed(by: disposeBag)
60 |
61 | useCase.dropDownMenus
62 | .bind(to: output.dropDownMenus)
63 | .disposed(by: disposeBag)
64 |
65 | useCase.mapNameAndRecordDatas
66 | .bind(to: output.mapNameAndRecordDatas)
67 | .disposed(by: disposeBag)
68 |
69 | return output
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Dependency/DIContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DIContainer.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/10.
6 | //
7 |
8 | import Foundation
9 |
10 | enum DIContainer {
11 |
12 | @propertyWrapper
13 | struct Resolve {
14 | private let type: T.Type
15 | private let container = DependenciesContainer.shared
16 |
17 | var wrappedValue: T { container.resolve(type) }
18 |
19 | init(_ type: T.Type) {
20 | self.type = type
21 | }
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Dependency/DependenciesContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DependenciesContainer.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/10.
6 | //
7 |
8 | import Foundation
9 |
10 | final class DependenciesContainer {
11 | static let shared = DependenciesContainer()
12 | private init () {}
13 |
14 | // DependencyKey 클래스를 이용해서 형과 이름을 사용해서 분류
15 | private var dependencies: [DependencyKey: Any] = [:]
16 |
17 | func register(
18 | _ type: T.Type,
19 | implement: Any,
20 | name: String? = nil
21 | ) {
22 | let dependencyKey = DependencyKey(type: type, name: name)
23 | dependencies[dependencyKey] = implement
24 | }
25 |
26 | func resolve(
27 | _ type: T.Type,
28 | name: String? = nil
29 | ) -> T {
30 | let dependencyKey = DependencyKey(type: type, name: name)
31 | if let dependency = dependencies[dependencyKey] as? T {
32 | return dependency
33 | } else {
34 | let protocolName = "\(type)".components(separatedBy: ".").last!
35 | fatalError("\(protocolName) 의 의존성을 해결할 수 없습니다.")
36 | }
37 | }
38 |
39 | func remove(type: T.Type) {
40 | let key = DependencyKey(type: type)
41 | _ = dependencies.removeValue(forKey: key)
42 | }
43 |
44 | func reset() {
45 | dependencies.removeAll()
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Dependency/DependencyKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DependencyKey.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/10.
6 | //
7 |
8 | import Foundation
9 |
10 | final class DependencyKey: Hashable, Equatable {
11 |
12 | private let type: Any.Type
13 | private let name: String?
14 |
15 | init(type: Any.Type, name: String? = nil) {
16 | self.type = type
17 | self.name = name
18 | }
19 |
20 | func hash(into hasher: inout Hasher) {
21 | hasher.combine(ObjectIdentifier(type))
22 | hasher.combine(name)
23 | }
24 |
25 | static func == (lhs: DependencyKey, rhs: DependencyKey) -> Bool {
26 | return lhs.type == rhs.type && lhs.name == rhs.name
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Enums/AnimationKeyPath.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AnimationKeyPath.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/30.
6 | //
7 |
8 | import Foundation
9 |
10 | enum AnimationKeyPath {
11 | case path
12 | case opacity
13 |
14 | var string: String {
15 | switch self {
16 | case .path:
17 | return "path"
18 | case .opacity:
19 | return "opacity"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Enums/Errors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Errors.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/14.
6 | //
7 |
8 | import Foundation
9 |
10 | enum Errors: Error {
11 | case decodeError
12 | case cannotDrawPolyLine
13 |
14 | var description: String {
15 | switch self {
16 | case .decodeError:
17 | return "decoding에 실패했습니다."
18 | case .cannotDrawPolyLine:
19 | return "땅의 경계선(PolyLine)을 그릴 수 없습니다."
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Enums/FirebaseRealtimeType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FirebaseRealtimeType.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/27.
6 | //
7 |
8 | import Foundation
9 |
10 | enum FirebaseRealtimeType {
11 | case mapList
12 | case recordList
13 | case record(id: Int)
14 | case user(id: String)
15 | case room(id: String)
16 | case postList
17 | case post(id: Int)
18 | case comment(postID: Int, commentID: Int)
19 | case badge
20 |
21 | var path: String {
22 | switch self {
23 | case .mapList:
24 | return "mapList"
25 | case .recordList:
26 | return "record"
27 | case .record(let id):
28 | return "record/\(id)"
29 | case .user(let id):
30 | return "User/\(id)"
31 | case .room(let id):
32 | return "Room/\(id)"
33 | case .post(let id):
34 | return "community/postList/\(id)"
35 | case .postList:
36 | return "community/postList"
37 | case .comment(let postID, let commentID):
38 | return "community/postList/\(postID)/comments/\(commentID)"
39 | case .badge:
40 | return "badge"
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Enums/FirebaseStorageType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FirebaseStorageType.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/07.
6 | //
7 |
8 | import Foundation
9 |
10 | enum FirebaseStorageType {
11 | case map
12 | case category(String)
13 | case badge
14 | case pinCharacter
15 |
16 | var path: String {
17 | switch self {
18 | case .map:
19 | return "Map"
20 | case .category(let name):
21 | return "Category/\(name)"
22 | case .badge:
23 | return "Badge"
24 | case .pinCharacter:
25 | return "PinCharacter"
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Enums/Locations.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Locations.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/24.
6 | //
7 |
8 | import Foundation
9 |
10 | enum Locations: CaseIterable {
11 | case seoul
12 | case incheon
13 | case gyeonggi
14 | case busan
15 | case gyeongbuk
16 | case gyeongnam
17 | case chungbuk
18 | case chungnam
19 |
20 | var string: String {
21 | switch self {
22 | case .seoul:
23 | return "서울"
24 | case .incheon:
25 | return "인천"
26 | case .gyeonggi:
27 | return "경기"
28 | case .busan:
29 | return "부산"
30 | case .gyeongbuk:
31 | return "경북"
32 | case .gyeongnam:
33 | return "경남"
34 | case .chungbuk:
35 | return "충북"
36 | case .chungnam:
37 | return "충남"
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Enums/PinCharacter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PinCharacter.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/12/13.
6 | //
7 |
8 | import UIKit
9 |
10 | enum PinCharacter: String, CaseIterable {
11 | case firstAnnotation
12 | case secondAnnotation
13 | case thirdAnnotation
14 | case fourthAnnotation
15 |
16 | var image: UIImage {
17 | (UIImage(named: self.rawValue) ?? .firstAnnotation).imageWith(newSize: CGSize(width: 30, height: 30))
18 | }
19 |
20 | var name: String {
21 | switch self {
22 | case .firstAnnotation:
23 | return "펭귄"
24 | case .secondAnnotation:
25 | return "강아지"
26 | case .thirdAnnotation:
27 | return "고양이"
28 | case .fourthAnnotation:
29 | return "토끼"
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Enums/SystemImageNameSpace.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SystemImageNameSpace.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/15.
6 | //
7 |
8 | import UIKit
9 |
10 | enum SystemImageNameSpace: String {
11 | case locationCircle = "location.circle"
12 | case chevronDown = "chevron.down"
13 | case ellipsis = "ellipsis"
14 | case xmark = "xmark"
15 |
16 | var uiImage: UIImage { UIImage(systemName: self.rawValue) ?? UIImage() }
17 |
18 | func systemImageColorChange(color: UIColor) -> UIImage {
19 | return self.uiImage.withTintColor(color, renderingMode: .alwaysOriginal)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Enums/TabBarType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TabBarType.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/14.
6 | //
7 |
8 | import Foundation
9 |
10 | enum TabBarType: CaseIterable {
11 | case home
12 | case record
13 | case community
14 | case myPage
15 |
16 | var pageNumber: Int {
17 | switch self {
18 | case .home:
19 | return 0
20 | case .record:
21 | return 1
22 | case .community:
23 | return 2
24 | case .myPage:
25 | return 3
26 | }
27 | }
28 |
29 | var pageTitle: String {
30 | switch self {
31 | case .home:
32 | return "땅따먹기"
33 | case .record:
34 | return "기록"
35 | case .community:
36 | return "커뮤니티"
37 | case .myPage:
38 | return "마이페이지"
39 | }
40 | }
41 |
42 | var iconImage: String {
43 | switch self {
44 | case .home:
45 | return "house"
46 | case .record:
47 | return "chart.bar"
48 | case .community:
49 | return "person.2"
50 | case .myPage:
51 | return "face.smiling"
52 | }
53 | }
54 |
55 | var selectedIconImage: String {
56 | switch self {
57 | case .home:
58 | return "house.fill"
59 | case .record:
60 | return "chart.bar.fill"
61 | case .community:
62 | return "person.2.fill"
63 | case .myPage:
64 | return "face.smiling.inverse"
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Error/FirebaseServiceError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FirebaseServiceError.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/11/23.
6 | //
7 |
8 | import Foundation
9 |
10 | enum FirebaseServiceError: Error {
11 | case nilDataError
12 | case fetchError
13 | }
14 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/CALayer+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CALayer+Extension.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/15.
6 | //
7 |
8 | import UIKit
9 |
10 | extension CALayer {
11 | func addBorder(
12 | directions: [UIRectEdge],
13 | color: UIColor,
14 | width: CGFloat
15 | ) {
16 | for direction in directions {
17 | let border = CALayer()
18 | switch direction {
19 | case .top:
20 | border.frame = CGRect.init(x: 0, y: 0, width: frame.width, height: width)
21 | case .bottom:
22 | border.frame = CGRect.init(x: 0, y: frame.height - width, width: frame.width, height: width)
23 | case .left:
24 | border.frame = CGRect.init(x: 0, y: 0, width: width, height: frame.height)
25 | case .right:
26 | border.frame = CGRect.init(x: frame.width - width, y: 0, width: width, height: frame.height)
27 | default:
28 | break
29 | }
30 | border.backgroundColor = color.cgColor
31 | self.addSublayer(border)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/CLLocationCoordinate2D+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CLLocationCoordinate2D+.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/15.
6 | //
7 |
8 | import MapKit
9 |
10 | // swiftlint:disable identifier_name
11 | extension CLLocationCoordinate2D {
12 | func distance(to: CLLocationCoordinate2D) -> CLLocationDistance {
13 | let from = CLLocation(latitude: latitude, longitude: longitude)
14 | let to = CLLocation(latitude: to.latitude, longitude: to.longitude)
15 | return to.distance(from: from)
16 | }
17 |
18 | var achaCoordinate: Coordinate {
19 | Coordinate(latitude: self.latitude, longitude: self.longitude)
20 | }
21 |
22 | static func from(coordiate: Coordinate) -> CLLocationCoordinate2D {
23 | CLLocationCoordinate2DMake(coordiate.latitude, coordiate.longitude)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/Collection+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Collection+.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/12/12.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Collection {
11 | // index에 해당하는 원소를 리턴. 없으면 nil
12 | subscript (safe index: Index) -> Element? {
13 | return indices.contains(index) ? self[index] : nil
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/Date+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Date+Extension.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/16.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Date {
11 | func convertToStringFormat(format: String) -> String {
12 | let dateFormatter = DateFormatter()
13 | dateFormatter.dateFormat = format
14 | dateFormatter.locale = Locale(identifier: "ko_kr")
15 | return dateFormatter.string(from: self)
16 | }
17 |
18 | var secondsSince1970: Int64 {
19 | Int64((self.timeIntervalSince1970).rounded())
20 | }
21 |
22 | init(seconds: Int64) {
23 | self = Date(timeIntervalSince1970: TimeInterval(seconds))
24 | }
25 |
26 | func since(_ from: Int64) -> Date {
27 | return Date(seconds: self.secondsSince1970 - from)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/Double+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Double+.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/16.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Double {
11 | func degreeToRadian() -> Double {
12 | self * .pi / 180
13 | }
14 |
15 | func radianToDegree() -> Double {
16 | self * 180.0 / .pi
17 | }
18 |
19 | var meterToKmString: String {
20 | String(format: "%.2f", self/1000) + "km"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/Encodable+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Encodable+Extension.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/22.
6 | //
7 |
8 | import Foundation
9 |
10 | // MARK: - struct > Dictonary 변환 코드
11 | extension Encodable {
12 | subscript(key: String) -> Any? {
13 | return dictionary[key]
14 | }
15 | var dictionary: [String: Any] {
16 | return (try? JSONSerialization.jsonObject(with: JSONEncoder().encode(self))) as? [String: Any] ?? [:]
17 | }
18 |
19 | var toJSON: Data? {
20 | try? JSONEncoder().encode(self)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/Int+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Int+Extension.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/16.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Int {
11 | func convertToHourMinuteSecondFormat() -> String {
12 | var time = self
13 | let day = String(format: "%02d", time/3600)
14 | time %= 3600
15 | let hour = String(format: "%02d", time/60)
16 | time %= 60
17 | let minute = String(format: "%02d", time)
18 | return "\(day):\(hour):\(minute)"
19 | }
20 |
21 | var convertToDecimal: String {
22 | let numberFormatter = NumberFormatter()
23 | numberFormatter.numberStyle = .decimal
24 |
25 | return numberFormatter.string(from: NSNumber(value: self))!
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/MKCircle+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MKCircle+.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/07.
6 | //
7 |
8 | import UIKit
9 | import MapKit
10 |
11 | extension MKCircle {
12 | enum CircleType: Int {
13 | case first = 0
14 | case second = 1
15 | case third = 2
16 | case fourth = 3
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/Reactive+/MKMapView+Rx.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MKMapView+Rx.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/12/08.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import RxCocoa
11 | import MapKit
12 |
13 | class RxMKMapViewDelegateProxy: DelegateProxy, DelegateProxyType, MKMapViewDelegate {
14 | static func registerKnownImplementations() {
15 | self.register { mapView -> RxMKMapViewDelegateProxy in
16 | RxMKMapViewDelegateProxy(parentObject: mapView, delegateProxy: self)
17 | }
18 | }
19 |
20 | static func currentDelegate(for object: MKMapView) -> MKMapViewDelegate? {
21 | return object.delegate
22 | }
23 |
24 | static func setCurrentDelegate(_ delegate: MKMapViewDelegate?, to object: MKMapView) {
25 | object.delegate = delegate
26 | }
27 | }
28 |
29 | extension Reactive where Base: MKMapView {
30 | var delegate: DelegateProxy {
31 | return RxMKMapViewDelegateProxy.proxy(for: self.base)
32 | }
33 |
34 | var didSelectAnnotation: Observable {
35 | return delegate.methodInvoked(#selector(MKMapViewDelegate.mapView(_:didSelect:)))
36 | .map { parameters in
37 | let annotaionView = (parameters[1] as? MKAnnotationView) ?? MKAnnotationView()
38 | return annotaionView.annotation
39 | }
40 | }
41 |
42 | var didDeselectAnnotation: Observable {
43 | return delegate.methodInvoked(#selector(MKMapViewDelegate.mapView(_:didDeselect:)))
44 | .map { parameters in
45 | let annotaionView = (parameters[1] as? MKAnnotationView) ?? MKAnnotationView()
46 | return annotaionView.annotation
47 | }
48 | }
49 |
50 | var regionDidChange: Observable {
51 | return delegate.methodInvoked(#selector(MKMapViewDelegate.mapView(_:regionDidChangeAnimated:)))
52 | .map { parameters in
53 | let mapView = (parameters[0] as? MKMapView) ?? MKMapView()
54 | return mapView.region
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/Reactive+/UIApplication+Rx.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIApplication+Rx.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/09.
6 | //
7 |
8 | import RxSwift
9 | import RxCocoa
10 | import UIKit
11 |
12 | public enum Appstate: Equatable {
13 | case active
14 | case inactive
15 | case background
16 | case terminated
17 | }
18 |
19 | extension Reactive where Base: UIApplication {
20 | var applicationWillEnterForeground: Observable {
21 | return NotificationCenter.default.rx.notification(UIApplication.willEnterForegroundNotification)
22 | .map { _ in
23 | return .active
24 | }
25 | }
26 |
27 | var applicationDidBecomActive: Observable {
28 | return NotificationCenter.default.rx.notification(UIApplication.didBecomeActiveNotification)
29 | .map { _ in
30 | return .active
31 | }
32 | }
33 |
34 | var applicationDidEnterBackground: Observable {
35 | return NotificationCenter.default.rx.notification(UIApplication.didEnterBackgroundNotification)
36 | .map { _ in
37 | return .background
38 | }
39 | }
40 |
41 | var applicationWillResignActive: Observable {
42 | return NotificationCenter.default.rx.notification(UIApplication.willResignActiveNotification)
43 | .map { _ in
44 | return .inactive
45 | }
46 | }
47 |
48 | var applicationWillTerminate: Observable {
49 | return NotificationCenter.default.rx.notification(UIApplication.willTerminateNotification)
50 | .map { _ in
51 | return .terminated
52 | }
53 | }
54 |
55 | public var appState: Observable {
56 | return Observable.of(
57 | applicationDidBecomActive,
58 | applicationWillResignActive,
59 | applicationWillEnterForeground,
60 | applicationDidEnterBackground,
61 | applicationWillTerminate
62 | )
63 | .merge()
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/Reactive+/UIView+Rx.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Rx+IndicatorAnimation.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/09.
6 | //
7 |
8 | import UIKit
9 | import RxSwift
10 | import Then
11 |
12 | extension Reactive where Base: UIView {
13 |
14 | var indicator: Binder {
15 | Binder(base) { base, indicator in
16 |
17 | if indicator {
18 | base.isUserInteractionEnabled = false
19 | let backView = UIView(frame: base.frame).then {
20 | $0.alpha = 0.6
21 | $0.backgroundColor = .black
22 | $0.tag = 444
23 | }
24 | let image = UIButton().then {
25 | $0.layer.borderColor = UIColor.pointDark.cgColor
26 | $0.layer.borderWidth = 4
27 | $0.layer.cornerRadius = 10
28 | $0.frame.size = CGSize(width: 60, height: 60)
29 | $0.tag = 444
30 | }
31 | base.addSubview(backView)
32 | backView.addSubview(image)
33 | image.center = base.center
34 | image.layer.add(animationGroup, forKey: nil)
35 | } else {
36 | base.isUserInteractionEnabled = true
37 | base.subviews.forEach { if $0.tag == 444 {
38 | $0.removeFromSuperview()
39 | } }
40 | }
41 | }
42 | }
43 |
44 | var animationGroup: CAAnimationGroup {
45 | let animationGroup = CAAnimationGroup()
46 | animationGroup.duration = 0.6
47 | animationGroup.fillMode = CAMediaTimingFillMode.forwards
48 | animationGroup.repeatCount = .infinity
49 |
50 | let animation3 = CABasicAnimation(keyPath: "position.x")
51 | animation3.fromValue = base.center.x-100
52 | animation3.toValue = base.center.x + 100
53 |
54 | let animation5 = CABasicAnimation(keyPath: "transform.rotation.x")
55 | animation5.fromValue = 0
56 | animation5.toValue = Double.pi / 3
57 |
58 | animationGroup.animations = [animation3, animation5]
59 |
60 | return animationGroup
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/Reactive+/UIViewCotnroller+Rx.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RxSwift+Extension.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/11/24.
6 | //
7 |
8 | import UIKit
9 | import RxSwift
10 | import RxCocoa
11 |
12 | public extension Reactive where Base: UIViewController {
13 | var viewDidLoad: ControlEvent {
14 | let source = self.methodInvoked(#selector(Base.viewDidLoad)).map { _ in }
15 | return ControlEvent(events: source)
16 | }
17 |
18 | var viewWillAppear: ControlEvent {
19 | let source = self.methodInvoked(#selector(Base.viewWillAppear)).map { _ in }
20 | return ControlEvent(events: source)
21 | }
22 | var viewDidAppear: ControlEvent {
23 | let source = self.methodInvoked(#selector(Base.viewDidAppear)).map { _ in }
24 | return ControlEvent(events: source)
25 | }
26 |
27 | var viewWillDisappear: ControlEvent {
28 | let source = self.methodInvoked(#selector(Base.viewWillDisappear)).map { _ in }
29 | return ControlEvent(events: source)
30 | }
31 | var viewDidDisappear: ControlEvent {
32 | let source = self.methodInvoked(#selector(Base.viewDidDisappear)).map { _ in }
33 | return ControlEvent(events: source)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Extension/String+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/21.
6 | //
7 |
8 | import UIKit
9 |
10 | extension String {
11 | func convertToDateFormat(format: String) -> Date {
12 | let dateFormatter = DateFormatter()
13 | dateFormatter.dateFormat = "yyyy-MM-dd"
14 | dateFormatter.locale = Locale(identifier: "ko_kr")
15 | if let date = dateFormatter.date(from: self) {
16 | return date
17 | }
18 | return Date()
19 | }
20 |
21 | func stringCheck(pattern: String) -> Bool {
22 | do {
23 | let regex = try NSRegularExpression(pattern: pattern, options: .caseInsensitive)
24 | let range = NSRange(location: 0, length: self.count)
25 | if regex.firstMatch(in: self, range: range) != nil {
26 | return true
27 | } else {
28 | return false
29 | }
30 | } catch {
31 | print(error.localizedDescription)
32 | return false
33 | }
34 | }
35 |
36 | func generateQRCode() -> UIImage? {
37 | let data = self.data(using: String.Encoding.ascii)
38 |
39 | if let filter = CIFilter(name: "CIQRCodeGenerator") {
40 | filter.setValue(data, forKey: "inputMessage")
41 | let transform = CGAffineTransform(scaleX: 3, y: 3)
42 |
43 | if let output = filter.outputImage?.transformed(by: transform) {
44 | return UIImage(ciImage: output)
45 | }
46 | }
47 | return nil
48 | }
49 |
50 | func stringLimit(_ number: Int) -> String {
51 | return self.count <= number ? self : self.map { String($0) }[0..
16 | let keyboardHeight: Driver
17 | private let disposeBag = DisposeBag()
18 |
19 | private init() {
20 | let keyboardWillChangeFrame = UIResponder.keyboardWillChangeFrameNotification
21 | let keyboardWillHide = UIResponder.keyboardWillHideNotification
22 | let keyboardEndUserKey = UIResponder.keyboardFrameEndUserInfoKey
23 |
24 | let defaultFrame = CGRect(
25 | x: 0,
26 | y: UIScreen.main.bounds.height,
27 | width: UIScreen.main.bounds.width,
28 | height: 0
29 | )
30 |
31 | let frameVariable = BehaviorRelay(value: defaultFrame)
32 |
33 | self.frame = frameVariable.asDriver().distinctUntilChanged()
34 | self.keyboardHeight = self.frame.map { UIScreen.main.bounds.height - $0.origin.y }
35 |
36 | // 키보드 프레임 변경
37 | let willChangeFrame = NotificationCenter.default.rx.notification(keyboardWillChangeFrame)
38 | .map { notification -> CGRect in
39 | let rectValue = notification.userInfo?[keyboardEndUserKey] as? NSValue
40 | return rectValue?.cgRectValue ?? defaultFrame
41 | }
42 |
43 | // 키보드가 없어짐
44 | let willHide = NotificationCenter.default.rx.notification(keyboardWillHide)
45 | .map { notification -> CGRect in
46 | let rectValue = notification.userInfo?[keyboardEndUserKey] as? NSValue
47 | return rectValue?.cgRectValue ?? defaultFrame
48 | }
49 |
50 | Observable.of(willChangeFrame, willHide)
51 | .merge()
52 | .bind(to: frameVariable)
53 | .disposed(by: disposeBag)
54 |
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Map/MapAnnotation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapAnnotation.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/15.
6 | //
7 |
8 | import MapKit
9 |
10 | // MapAnnotation은 NSObject를 상속해야함
11 | class MapAnnotation: NSObject, MKAnnotation {
12 |
13 | let map: Map
14 | let coordinate: CLLocationCoordinate2D
15 | let polyLine: MKPolyline
16 |
17 | init(map: Map, polyLine: MKPolyline) {
18 | self.map = map
19 | self.coordinate = CLLocationCoordinate2D(latitude: map.centerCoordinate.latitude,
20 | longitude: map.centerCoordinate.longitude)
21 | self.polyLine = polyLine
22 | super.init()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/DefaultKeychainService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultKeychainService.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/02.
6 | //
7 | import Foundation
8 | import RxSwift
9 |
10 | struct DefaultKeychainService: KeychainService {
11 |
12 | func get() -> String? {
13 | return try? KeyChainManager.get()
14 | }
15 |
16 | func save(uuid: String) {
17 | try? KeyChainManager.save(id: uuid)
18 | }
19 |
20 | func delete() {
21 | try? KeyChainManager.delete()
22 | }
23 |
24 | func update(uuid: String) {
25 | try? KeyChainManager.update(id: uuid)
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/DefaultRandomService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultRandomService.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/05.
6 | //
7 |
8 | import Foundation
9 |
10 | struct DefaultRandomService: RandomService {
11 | func make() -> String {
12 | let numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
13 | var random = ""
14 | for _ in 0..<16 {
15 | random += numbers.randomElement()!
16 | }
17 | return random
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/DefaultTimerService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultTimerService.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/23.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | final class DefaultTimerService: TimerService {
12 | var disposeBag = DisposeBag()
13 |
14 | func start() -> Observable {
15 | Observable
16 | .interval(.seconds(1), scheduler: MainScheduler.asyncInstance)
17 | .map { $0 + 1 }
18 | }
19 |
20 | func start(until: Int) -> Observable {
21 | Observable
22 | .timer(.seconds(until), scheduler: MainScheduler.asyncInstance)
23 | .map { _ in }
24 | }
25 |
26 | func stop() {
27 | disposeBag = DisposeBag()
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/Protocol/AuthService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthService.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/02.
6 | //
7 | import Foundation
8 | import RxSwift
9 |
10 | protocol AuthService {
11 |
12 | func signUp(data: SignUpData) -> Single
13 | func logIn(data: LoginData) -> Single
14 | func signOut() throws
15 | func delete() -> Single
16 | func update(email: String, password: String) -> Single
17 | }
18 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/Protocol/FirebaseStorageNetworkService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FirebaseStorageNetworkService.swift
3 | // Acha
4 | //
5 | // Created by 배남석 on 2022/12/07.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol FirebaseStorageNetworkService {
12 | func upload(type: FirebaseStorageType, data: Data, completion: @escaping (URL?) -> Void)
13 | func download(urlString: String, completion: @escaping (Data?) -> Void)
14 | func download(urlString: String) -> Observable
15 | }
16 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/Protocol/HealthKitService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HealthKitService.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/01.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol HealthKitService {
12 | /// 헬스 킷에 값 저장 가능
13 | func write(type: DefaultHealthKitServiceType) -> Observable
14 | /// 헬스 킷에서 최근 값 가져 옴
15 | func read(type: DefaultHealthKitServiceType) -> Observable
16 | /// 헬스 인증 요청
17 | func authorization() -> Observable
18 | }
19 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/Protocol/ImageCacheService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageCacheService.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/12/11.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol ImageCacheService {
12 | func isExist(imageURL: String) -> Bool
13 | func load(imageURL: String) -> Single
14 | func write(imageURL: String, image: Data)
15 | }
16 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/Protocol/KeychainService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeychainService.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/02.
6 | //
7 | import Foundation
8 |
9 | protocol KeychainService {
10 |
11 | /// uuid 받아 오는 메서드
12 | func get() -> String?
13 |
14 | /// uuid 삭제 메서드
15 | func delete()
16 |
17 | /// uuid 저장 메서드
18 | func save(uuid: String)
19 |
20 | /// uuid 업데이트 메서드
21 | func update(uuid: String)
22 | }
23 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/Protocol/LocationService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LocationService.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/27.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 | import CoreLocation
11 |
12 | protocol LocationService {
13 | var authorizationStatus: PublishSubject { get set }
14 | var userLocation: BehaviorSubject { get set }
15 |
16 | func start()
17 | func stop()
18 | }
19 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/Protocol/RandomService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RandomService.swift
3 | // Acha
4 | //
5 | // Created by hong on 2022/12/05.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol RandomService {
11 | /// 랜덤한 16자리 숫자 만들어 주는 메서드 ( 방 번호 만들기 )
12 | func make() -> String
13 | }
14 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/Protocol/RealtimeDatabaseNetworkService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RealtimeDatabaseNetworkService.swift
3 | // Acha
4 | //
5 | // Created by sangyeon on 2022/11/27.
6 | //
7 |
8 | import RxSwift
9 |
10 | protocol RealtimeDatabaseNetworkService {
11 | func fetch(type: FirebaseRealtimeType,
12 | child: String,
13 | value: Any?,
14 | limitCount: Int?) -> Single
15 | func fetch(type: FirebaseRealtimeType) -> Single
16 | func fetchAtKeyValue(type: FirebaseRealtimeType,
17 | value: Any,
18 | key: String) -> Single
19 | func upload(type: FirebaseRealtimeType, data: T) -> Single
20 | func delete(type: FirebaseRealtimeType) -> Single
21 | func observing(type: FirebaseRealtimeType) -> Observable
22 | func removeObserver(type: FirebaseRealtimeType)
23 | func terminate(type: FirebaseRealtimeType)
24 | }
25 |
--------------------------------------------------------------------------------
/Acha/Acha/Util/Service/Protocol/TimerService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TimerService.swift
3 | // Acha
4 | //
5 | // Created by 조승기 on 2022/11/23.
6 | //
7 |
8 | import Foundation
9 | import RxSwift
10 |
11 | protocol TimerService {
12 | var disposeBag: DisposeBag { get set }
13 |
14 | func start() -> Observable
15 | func start(until: Int) -> Observable
16 | func stop()
17 | }
18 |
--------------------------------------------------------------------------------
/Acha/AchaTests/AchaTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AchaTests.swift
3 | // AchaTests
4 | //
5 | // Created by sangyeon on 2022/11/11.
6 | //
7 |
8 | import XCTest
9 | @testable import Acha
10 |
11 | final class AchaTests: XCTestCase {
12 |
13 | override func setUpWithError() throws {
14 | // Put setup code here. This method is called before the invocation of each test method in the class.
15 | }
16 |
17 | override func tearDownWithError() throws {
18 | // Put teardown code here. This method is called after the invocation of each test method in the class.
19 | }
20 |
21 | func testExample() throws {
22 | // This is an example of a functional test case.
23 | // Use XCTAssert and related functions to verify your tests produce the correct results.
24 | // Any test you write for XCTest can be annotated as throws and async.
25 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
26 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
27 | }
28 |
29 | func testPerformanceExample() throws {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/Acha/AchaUITests/AchaUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AchaUITests.swift
3 | // AchaUITests
4 | //
5 | // Created by sangyeon on 2022/11/11.
6 | //
7 |
8 | import XCTest
9 |
10 | final class AchaUITests: XCTestCase {
11 |
12 | override func setUpWithError() throws {
13 | // Put setup code here. This method is called before the invocation of each test method in the class.
14 |
15 | // In UI tests it is usually best to stop immediately when a failure occurs.
16 | continueAfterFailure = false
17 |
18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
19 | }
20 |
21 | override func tearDownWithError() throws {
22 | // Put teardown code here. This method is called after the invocation of each test method in the class.
23 | }
24 |
25 | func testExample() throws {
26 | // UI tests must launch the application that they test.
27 | let app = XCUIApplication()
28 | app.launch()
29 |
30 | // Use XCTAssert and related functions to verify your tests produce the correct results.
31 | }
32 |
33 | func testLaunchPerformance() throws {
34 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
35 | // This measures how long it takes to launch your application.
36 | measure(metrics: [XCTApplicationLaunchMetric()]) {
37 | XCUIApplication().launch()
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Acha/AchaUITests/AchaUITestsLaunchTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AchaUITestsLaunchTests.swift
3 | // AchaUITests
4 | //
5 | // Created by sangyeon on 2022/11/11.
6 | //
7 |
8 | import XCTest
9 |
10 | final class AchaUITestsLaunchTests: XCTestCase {
11 |
12 | override class var runsForEachTargetApplicationUIConfiguration: Bool {
13 | true
14 | }
15 |
16 | override func setUpWithError() throws {
17 | continueAfterFailure = false
18 | }
19 |
20 | func testLaunch() throws {
21 | let app = XCUIApplication()
22 | app.launch()
23 |
24 | // Insert steps here to perform after app launch but before taking a screenshot,
25 | // such as logging into a test account or navigating somewhere in the app
26 |
27 | let attachment = XCTAttachment(screenshot: app.screenshot())
28 | attachment.name = "Launch Screen"
29 | attachment.lifetime = .keepAlways
30 | add(attachment)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Acha/GPX/WorkSpace by 25km:h.gpx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 13.91
5 |
6 |
7 |
8 | 14.48
9 |
10 |
11 |
12 | 13.91
13 |
14 |
15 |
16 | 13.38
17 |
18 |
19 |
20 | 13.81
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Acha/GPX/WorkSpace by 6km:h.gpx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 13.91
5 |
6 |
7 |
8 | 14.48
9 |
10 |
11 |
12 | 13.91
13 |
14 |
15 |
16 | 13.38
17 |
18 |
19 |
20 | 13.81
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Acha/GPX/국민대 12KMH.gpx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 101.94
5 |
6 |
7 |
8 | 102.61
9 |
10 |
11 |
12 | 102.81
13 |
14 |
15 |
16 | 100.18
17 |
18 |
19 |
20 | 101.83
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Acha/GPX/국민대 30KMH.gpx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 101.94
5 |
6 |
7 |
8 | 102.61
9 |
10 |
11 |
12 | 102.81
13 |
14 |
15 |
16 | 100.18
17 |
18 |
19 |
20 | 101.83
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Acha/GPX/부원여중 12KMH.gpx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 19.31
5 |
6 |
7 |
8 | 18.60
9 |
10 |
11 |
12 | 20.68
13 |
14 |
15 |
16 | 20.92
17 |
18 |
19 |
20 | 19.36
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Acha/GPX/부원여중 5MPH.gpx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 19.31
5 |
6 |
7 |
8 | 18.60
9 |
10 |
11 |
12 | 20.68
13 |
14 |
15 |
16 | 20.92
17 |
18 |
19 |
20 | 19.36
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Acha/GPX/부평동초등학교 20MPH.gpx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 11.73
5 |
6 |
7 |
8 | 11.81
9 |
10 |
11 |
12 | 12.35
13 |
14 |
15 |
16 | 12.27
17 |
18 |
19 |
20 | 12.19
21 |
22 |
23 |
24 | 11.74
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Acha/GPX/부평동초등학교 4.2MPH.gpx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 11.81
5 |
6 |
7 |
8 | 11.74
9 |
10 |
11 |
12 | 12.19
13 |
14 |
15 |
16 | 12.27
17 |
18 |
19 |
20 | 12.36
21 |
22 |
23 |
24 | 11.82
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Acha/GPX/부평동초등학교 4MPH.gpx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 11.81
5 |
6 |
7 |
8 | 11.74
9 |
10 |
11 |
12 | 12.19
13 |
14 |
15 |
16 | 12.27
17 |
18 |
19 |
20 | 12.36
21 |
22 |
23 |
24 | 11.82
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Acha/GPX/부평동초등학교 5MPH.gpx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 11.81
5 |
6 |
7 |
8 | 11.74
9 |
10 |
11 |
12 | 12.19
13 |
14 |
15 |
16 | 12.27
17 |
18 |
19 |
20 | 12.36
21 |
22 |
23 |
24 | 11.82
25 |
26 |
27 |
--------------------------------------------------------------------------------
/AchaLibrary/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
10 |
--------------------------------------------------------------------------------
/AchaLibrary/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/AchaLibrary/.swiftpm/xcode/xcshareddata/xcschemes/AchaLibrary.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/AchaLibrary/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.7
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "AchaLibrary",
8 | platforms: [
9 | .iOS(.v14)
10 | ],
11 | products: [
12 | // Products define the executables and libraries a package produces, and make them visible to other packages.
13 | .library(
14 | name: "AchaLibrary",
15 | targets: ["AchaLibrary"]),
16 | ],
17 | dependencies: [
18 | .package(url: "https://github.com/SnapKit/SnapKit", from: "5.0.0"),
19 | .package(url: "https://github.com/ReactiveX/RxSwift", from: "6.0.0"),
20 | .package(url: "https://github.com/devxoul/Then", from: "3.0.0"),
21 | .package(url: "https://github.com/firebase/firebase-ios-sdk.git", from: "9.0.0"),
22 | ],
23 | targets: [
24 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
25 | // Targets can depend on other targets in this package, and on products in packages this package depends on.
26 | .target(
27 | name: "AchaLibrary",
28 | dependencies: [
29 | .product(name: "FirebaseAuth", package: "firebase-ios-sdk"),
30 | .product(name: "FirebaseDatabase", package: "firebase-ios-sdk"),
31 | .product(name: "FirebaseAnalytics", package: "firebase-ios-sdk"),
32 | .product(name: "FirebaseFirestore", package: "firebase-ios-sdk"),
33 | .product(name: "FirebaseStorage", package: "firebase-ios-sdk"),
34 | .product(name: "RxCocoa", package: "RxSwift"),
35 | .product(name: "RxRelay", package: "RxSwift"),
36 | .product(name: "RxSwift", package: "RxSwift"),
37 | .product(name: "Then", package: "Then"),
38 | .product(name: "SnapKit", package: "SnapKit")
39 | ]),
40 | .testTarget(
41 | name: "AchaLibraryTests",
42 | dependencies: ["AchaLibrary"]),
43 | ]
44 | )
45 |
--------------------------------------------------------------------------------
/AchaLibrary/README.md:
--------------------------------------------------------------------------------
1 | # AchaLibrary
2 |
3 | A description of this package.
4 |
--------------------------------------------------------------------------------
/AchaLibrary/Sources/AchaLibrary/AchaLibrary.swift:
--------------------------------------------------------------------------------
1 | public struct AchaLibrary {
2 | public private(set) var text = "Hello, World!"
3 |
4 | public init() {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/AchaLibrary/Tests/AchaLibraryTests/AchaLibraryTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import AchaLibrary
3 |
4 | final class AchaLibraryTests: XCTestCase {
5 | func testExample() throws {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | XCTAssertEqual(AchaLibrary().text, "Hello, World!")
10 | }
11 | }
12 |
--------------------------------------------------------------------------------