Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] 상상의 닉네임 뷰 구현 및 리팩토링(?) #48

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
54 changes: 49 additions & 5 deletions SOPKATHON-iOS-TEAM2/SOPKATHON-iOS-TEAM2.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@
3663C2AD2BF9459E004C07AC /* Pretendard-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3663C2A42BF9459E004C07AC /* Pretendard-Thin.otf */; };
3663C2AF2BF950AC004C07AC /* SharedInstaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3663C2AE2BF950AC004C07AC /* SharedInstaView.swift */; };
367B146B2BF8EDEA000DBA22 /* Adjust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 367B146A2BF8EDEA000DBA22 /* Adjust.swift */; };
3689D5E62C0E3C260060B674 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3689D5E52C0E3C260060B674 /* HomeViewModel.swift */; };
3689D5E82C0F74540060B674 /* BaseNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3689D5E72C0F74540060B674 /* BaseNetwork.swift */; };
3689D5EA2C0F74BC0060B674 /* QuestionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3689D5E92C0F74BC0060B674 /* QuestionViewModel.swift */; };
3689D5ED2C0F77A60060B674 /* QuestionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3689D5EC2C0F77A60060B674 /* QuestionService.swift */; };
3689D5F02C0F7F5D0060B674 /* AttemptService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3689D5EF2C0F7F5D0060B674 /* AttemptService.swift */; };
3689D5F22C0F84FD0060B674 /* SharedInstaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3689D5F12C0F84FD0060B674 /* SharedInstaView.swift */; };
3689D5F42C0F89F70060B674 /* SolvedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3689D5F32C0F89F70060B674 /* SolvedViewModel.swift */; };
AF2B55F42BF939B700F382A0 /* APITarget+Members.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF2B55F32BF939B700F382A0 /* APITarget+Members.swift */; };
AF2B55FE2BF93B7800F382A0 /* APITarget+Questions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF2B55FD2BF93B7800F382A0 /* APITarget+Questions.swift */; };
AF2B56002BF93F9E00F382A0 /* APITarget+Attempts.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF2B55FF2BF93F9E00F382A0 /* APITarget+Attempts.swift */; };
Expand Down Expand Up @@ -116,6 +123,13 @@
3663C2A42BF9459E004C07AC /* Pretendard-Thin.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "Pretendard-Thin.otf"; path = "../../../../../../../../../../Downloads/Pretendard-1.3.9 2/public/static/Pretendard-Thin.otf"; sourceTree = "<group>"; };
3663C2AE2BF950AC004C07AC /* SharedInstaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedInstaView.swift; sourceTree = "<group>"; };
367B146A2BF8EDEA000DBA22 /* Adjust.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Adjust.swift; sourceTree = "<group>"; };
3689D5E52C0E3C260060B674 /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
3689D5E72C0F74540060B674 /* BaseNetwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseNetwork.swift; sourceTree = "<group>"; };
3689D5E92C0F74BC0060B674 /* QuestionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestionViewModel.swift; sourceTree = "<group>"; };
3689D5EC2C0F77A60060B674 /* QuestionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestionService.swift; sourceTree = "<group>"; };
3689D5EF2C0F7F5D0060B674 /* AttemptService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttemptService.swift; sourceTree = "<group>"; };
3689D5F12C0F84FD0060B674 /* SharedInstaView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharedInstaView.swift; sourceTree = "<group>"; };
3689D5F32C0F89F70060B674 /* SolvedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolvedViewModel.swift; sourceTree = "<group>"; };
AF2B55F32BF939B700F382A0 /* APITarget+Members.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APITarget+Members.swift"; sourceTree = "<group>"; };
AF2B55FD2BF93B7800F382A0 /* APITarget+Questions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APITarget+Questions.swift"; sourceTree = "<group>"; };
AF2B55FF2BF93F9E00F382A0 /* APITarget+Attempts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APITarget+Attempts.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -189,6 +203,26 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
3689D5EB2C0F77980060B674 /* Service */ = {
isa = PBXGroup;
children = (
3689D5EC2C0F77A60060B674 /* QuestionService.swift */,
3689D5EF2C0F7F5D0060B674 /* AttemptService.swift */,
);
path = Service;
sourceTree = "<group>";
};
3689D5EE2C0F77FE0060B674 /* Base */ = {
isa = PBXGroup;
children = (
AFD35AAB2BF526D700354EE5 /* Config.swift */,
AFD35AAF2BF5270200354EE5 /* MoyaLogginPlugin.swift */,
AFD35AB12BF5270700354EE5 /* NetworkResult.swift */,
3689D5E72C0F74540060B674 /* BaseNetwork.swift */,
);
path = Base;
sourceTree = "<group>";
};
AF42C37B2BF941D2009A250D /* Recovered References */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -276,6 +310,9 @@
AFD35A732BF51E1600354EE5 /* Model */ = {
isa = PBXGroup;
children = (
3689D5E52C0E3C260060B674 /* HomeViewModel.swift */,
3689D5E92C0F74BC0060B674 /* QuestionViewModel.swift */,
3689D5F32C0F89F70060B674 /* SolvedViewModel.swift */,
AFD35A872BF51F7900354EE5 /* MockModel.swift */,
AFB3AB1E2BF9512900D76C23 /* TodayQuiz.swift */,
);
Expand All @@ -292,7 +329,7 @@
2E559B192BF911C5000FBE89 /* QuestionViewController.swift */,
2E559B1B2BF9166E000FBE89 /* CustomButton.swift */,
3663C2802BF915C3004C07AC /* SolvedViewController.swift */,
3663C2AE2BF950AC004C07AC /* SharedInstaView.swift */,
3689D5F12C0F84FD0060B674 /* SharedInstaView.swift */,
);
path = ViewController;
sourceTree = "<group>";
Expand Down Expand Up @@ -360,11 +397,10 @@
AFD35A7C2BF51E8C00354EE5 /* Network */ = {
isa = PBXGroup;
children = (
3689D5EE2C0F77FE0060B674 /* Base */,
3689D5EB2C0F77980060B674 /* Service */,
AFD35A8A2BF5200300354EE5 /* DTO */,
AFD35A892BF5200000354EE5 /* API */,
AFD35AAB2BF526D700354EE5 /* Config.swift */,
AFD35AAF2BF5270200354EE5 /* MoyaLogginPlugin.swift */,
AFD35AB12BF5270700354EE5 /* NetworkResult.swift */,
);
path = Network;
sourceTree = "<group>";
Expand Down Expand Up @@ -529,23 +565,26 @@
367B146B2BF8EDEA000DBA22 /* Adjust.swift in Sources */,
3663C2812BF915C3004C07AC /* SolvedViewController.swift in Sources */,
AFD35A882BF51F7900354EE5 /* MockModel.swift in Sources */,
3689D5F42C0F89F70060B674 /* SolvedViewModel.swift in Sources */,
DD834FBE2BF8E5CC0014D066 /* UIImage+.swift in Sources */,
AFD695712BF92F7600FADE46 /* DTO+GetQuestionsResponse.swift in Sources */,
3663C2AF2BF950AC004C07AC /* SharedInstaView.swift in Sources */,
AFD35A5F2BF51DB700354EE5 /* AppDelegate.swift in Sources */,
AF2B56002BF93F9E00F382A0 /* APITarget+Attempts.swift in Sources */,
AF2B55FE2BF93B7800F382A0 /* APITarget+Questions.swift in Sources */,
2E559B1A2BF911C5000FBE89 /* QuestionViewController.swift in Sources */,
AFD695852BF9342300FADE46 /* DTO+GetRecordListResponse.swift in Sources */,
3689D5E82C0F74540060B674 /* BaseNetwork.swift in Sources */,
AFD695792BF931DB00FADE46 /* DTO+PostCorrectInfoResponse.swift in Sources */,
DD834FC22BF8EAAA0014D066 /* UILabel+.swift in Sources */,
AFD35AAC2BF526D700354EE5 /* Config.swift in Sources */,
DD834FC02BF8E5F00014D066 /* UIView+.swift in Sources */,
AFD695832BF933D100FADE46 /* DTO+GetQuestionDescriptionRequest.swift in Sources */,
AFD695892BF9349900FADE46 /* DTO+CreateMemberResponse.swift in Sources */,
3689D5F22C0F84FD0060B674 /* SharedInstaView.swift in Sources */,
AFB3AB1F2BF9512900D76C23 /* TodayQuiz.swift in Sources */,
AFD35A8E2BF5202200354EE5 /* APITarget+Namespace.swift in Sources */,
AFD35A902BF5204E00354EE5 /* UIColor+.swift in Sources */,
3689D5EA2C0F74BC0060B674 /* QuestionViewModel.swift in Sources */,
AFD695732BF92F8000FADE46 /* DTO+GetQuestionsRequest.swift in Sources */,
AFD35AB22BF5270700354EE5 /* NetworkResult.swift in Sources */,
AFD35A7E2BF51F5F00354EE5 /* MockFonts.swift in Sources */,
Expand All @@ -555,14 +594,17 @@
DD834FC42BF8EFCF0014D066 /* AnswerViewController.swift in Sources */,
DD834FCB2BF911B10014D066 /* UIFont+.swift in Sources */,
DD834FBC2BF8E5B30014D066 /* UIViewController+.swift in Sources */,
3689D5F02C0F7F5D0060B674 /* AttemptService.swift in Sources */,
AFD6957B2BF931E600FADE46 /* DTO+PostCorrectInfoRequest.swift in Sources */,
AFD35A822BF51F6800354EE5 /* DoYouKnowThisColors.swift in Sources */,
DD834FC62BF8F16F0014D066 /* AnswerTableViewCell.swift in Sources */,
3689D5ED2C0F77A60060B674 /* QuestionService.swift in Sources */,
AFD695752BF9301E00FADE46 /* DTO+GetDetailQuestionRequest.swift in Sources */,
AFD6957F2BF932EA00FADE46 /* DTO+GetCorrectInfoRequest.swift in Sources */,
AF2B55F42BF939B700F382A0 /* APITarget+Members.swift in Sources */,
AFD35A612BF51DB700354EE5 /* SceneDelegate.swift in Sources */,
2E559B1C2BF9166F000FBE89 /* CustomButton.swift in Sources */,
3689D5E62C0E3C260060B674 /* HomeViewModel.swift in Sources */,
AF6565472BF8EED4001D7D22 /* HomeView.swift in Sources */,
AFD35A802BF51F6400354EE5 /* MockImages.swift in Sources */,
AFD35A962BF5207F00354EE5 /* DTO+Namespace.swift in Sources */,
Expand Down Expand Up @@ -726,6 +768,7 @@
FacebookDisplayName = "${FacebookDisplayName}";
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "SOPKATHON-iOS-TEAM2/Common/Resources/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "너 이거 알아?";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main;
Expand Down Expand Up @@ -759,6 +802,7 @@
FacebookDisplayName = "${FacebookDisplayName}";
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "SOPKATHON-iOS-TEAM2/Common/Resources/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "너 이거 알아?";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@
//

import UIKit
import SwiftUI

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let isLogin = UserDefaults.standard.bool(forKey: "isLogin")
var rootViewController: UIViewController
guard let windowScene = scene as? UIWindowScene else { return }
self.window = UIWindow(windowScene: windowScene)
let navigationController = UINavigationController(rootViewController: ArchiveViewController())

if isLogin {
rootViewController = UIHostingController(rootView: HomeView())
} else {
rootViewController = ViewController()
}

let navigationController = UINavigationController(rootViewController: rootViewController)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,19 @@ extension UILabel {
self.attributedText = attributedString
}
}

extension UITextField {

func setPlaceholderColor(_ placeholderColor: UIColor) {
guard let string = self.placeholder else {
return
}
attributedPlaceholder = NSAttributedString(string: string, attributes: [.foregroundColor: placeholderColor])
}

func addLeftPadding(_ value: Double) {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: value, height: self.frame.height))
self.leftView = paddingView
self.leftViewMode = ViewMode.always
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"images" : [
{
"filename" : "Frame 136.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions SOPKATHON-iOS-TEAM2/SOPKATHON-iOS-TEAM2/Model/HomeViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// HomeViewModel.swift
// SOPKATHON-iOS-TEAM2
//
// Created by 이지희 on 6/4/24.
//

import SwiftUI

import Moya


final class HomeViewModel: ObservableObject {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • viewModel에서 콜백지옥이 우려된다면 Moya를 async / await 방식으로 사용해보려고 구현해보아도 괜찮을 지도..?!

@State var homeQuestions: [DTO.GetQuestionsResponse.Question] = []
private let networkService: QuestionsNetworkService

init(networkService: QuestionsNetworkService = QuestionsNetworkService()) {
self.networkService = networkService
self.fetchQuestions(completion: { })
}


/// 질문 목록 가져오기
func fetchQuestions(completion: @escaping () -> Void) {
networkService.getQuestions { [weak self] result in
switch result {
case .success(let response):
response.data.forEach { question in

}
case .failure(let error):
print(error)
}
completion()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// SolvedViewModel.swift
// SOPKATHON-iOS-TEAM2
//
// Created by 이지희 on 6/5/24.
//

import Foundation

struct Question {
let type: Int
let question: String
let answer: [Option]
let nextQuestionID: Int
}

struct Option {
let option: String
let isCorrect: Bool
}


final class QuestionViewModel: ObservableObject {
private let networkService: QuestionsNetworkService

var questions: [Question] = []
var questionDescription: String = ""
var errorMessage: String?

init(networkService: QuestionsNetworkService = QuestionsNetworkService()) {
self.networkService = networkService
}

/// 질문 상세 정보 가져오기
func fetchDetailQuestion(questionId: Int, completion: @escaping () -> Void) {
let request = DTO.GetDetailQuestionRequest(questionId: questionId)
networkService.getDetailQuestions(requestDTO: request) { [weak self] result in
switch result {
case .success(let response):
let data = response.data
let options = data.options.map { option in
Option(option: option.option, isCorrect: option.isAnswer)
}
self?.questions.append(Question(type: data.type,
question: data.question,
answer: options,
nextQuestionID: data.nextQuestionID))
case .failure(let error):
self?.errorMessage = error.localizedDescription
}
completion()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// SolvedViewModel.swift
// SOPKATHON-iOS-TEAM2
//
// Created by 이지희 on 6/5/24.
//

import Foundation

final class SolvedViewModel {
var questionTitle: String = "" {
didSet {
self.onUpdate?()
}
}
var answerText: String = "" {
didSet {
self.onUpdate?()
}
}
var questionDescription: String = "" {
didSet {
self.onUpdate?()
}
}

var onUpdate: (() -> Void)?

private let networkService: QuestionsNetworkService

init(networkService: QuestionsNetworkService, questionId: Int) {
self.networkService = networkService
let memberId = UserDefaults.standard.integer(forKey: "memberID")
print(memberId)
fetchQuestionDescription(questionId: questionId, memberId: memberId)
}

func fetchQuestionDescription(questionId: Int, memberId: Int) {
let request = DTO.GetQuestionDescriptionRequest(memberId: memberId, questionId: questionId)
networkService.getQuestionDescription(requestDTO: request) { [weak self] result in
DispatchQueue.main.async {
switch result {
case .success(let response):
let data = response.data
self?.questionTitle = data.question
self?.answerText = data.answer
self?.questionDescription = data.solution
case .failure(let error):
print(error)
}
}
}
Comment on lines +40 to +52
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 이런 부분이 걱정된다면, 레포지토리를 중간에 놔둬도 괜찮을 것 같아!
  • 지희가 생각하기에 이 부분에서 Rx를 사용할 경우 어떤 이점이 존재할 것 같아?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 좀 편하게 해보려고 rx 도입 생각한건데, 뭔가 다시 생각해보니까 라이브러리 도입 없이도 충분히 고칠 수 있을 것 같다!
아키텍처부터 좀 다시 고민해볼게요!!

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

import Foundation

import Moya

protocol NetworkServiceProtocol {
associatedtype Target: TargetType
var provider: MoyaProvider<Target> { get }
func request<T: Decodable>(_ target: Target, completion: @escaping (Result<T, Error>) -> Void)
}

class NetworkService<T: TargetType>: NetworkServiceProtocol {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 추상화 되게 깔끔하게 잘 한 것 같은데?? 나도 Rx를 많이 써보지 않아서, Rx를 사용하면 어떤 추가적인 장점이 있을 것 같아?

typealias Target = T
let provider: MoyaProvider<T>

init(provider: MoyaProvider<T> = MoyaProvider<T>(plugins: [NetworkLoggerPlugin()])) {
self.provider = provider
}

func request<U: Decodable>(_ target: T, completion: @escaping (Result<U, Error>) -> Void) {
provider.request(target) { result in
switch result {
case .success(let response):
do {
let decoder = JSONDecoder()
let decoded = try decoder.decode(U.self, from: response.data)
completion(.success(decoded))
} catch let error {
completion(.failure(error))
}
case .failure(let error):
completion(.failure(error))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import Foundation

import Moya

enum NetworkResult<T> {
case success(T) // 서버 통신 성공했을 때,
case requestErr // 요청 에러 발생했을 때,
Expand Down
Loading