Skip to content

Commit

Permalink
Merge pull request #48 from SJU-CapstoneDesign/feat/#45-onboarding
Browse files Browse the repository at this point in the history
[Feat/#45] 온보딩 개발
  • Loading branch information
mooyoung2309 authored Nov 6, 2023
2 parents a65a943 + 75b2995 commit 7391d5f
Show file tree
Hide file tree
Showing 35 changed files with 914 additions and 30 deletions.
10 changes: 10 additions & 0 deletions D3N.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
</dict>
</plist>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions Targets/D3N/Resources/Assets.xcassets/Logo.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "1024.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
1 change: 0 additions & 1 deletion Targets/D3N/Sources/App/RootApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ struct RootApp: App {
RootView(
store: Store(initialState: RootStore.State()) {
RootStore()
._printChanges()
}
)
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
Expand Down
9 changes: 7 additions & 2 deletions Targets/D3N/Sources/App/RootStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ import ComposableArchitecture
struct RootStore: Reducer {

enum State: Equatable {
case onboarding(OnboardingNavigationStackStore.State)
case mainTab(MainTabStore.State)

init() {
self = .mainTab(.init())
self = .onboarding(.init())
// self = .mainTab(.init())
}
}

enum Action: Equatable {
case onboarding(OnboardingNavigationStackStore.Action)
case mainTab(MainTabStore.Action)
}

Expand All @@ -29,7 +32,9 @@ struct RootStore: Reducer {
default: return .none
}
}

.ifCaseLet(/State.onboarding, action: /Action.onboarding) {
OnboardingNavigationStackStore()
}
.ifCaseLet(/State.mainTab, action: /Action.mainTab) {
MainTabStore()
}
Expand Down
4 changes: 4 additions & 0 deletions Targets/D3N/Sources/App/RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ struct RootView: View {
var body: some View {
SwitchStore(self.store) {
switch $0 {
case .onboarding:
CaseLet(/RootStore.State.onboarding, action: RootStore.Action.onboarding) {
OnboardingNavigationStackView(store: $0)
}
case .mainTab:
CaseLet(/RootStore.State.mainTab, action: RootStore.Action.mainTab) {
MainTabView(store: $0)
Expand Down
6 changes: 6 additions & 0 deletions Targets/D3N/Sources/Config/D3N-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIUserInterfaceStyle</key>
<string>Auto</string>
<key>NSUserTrackingUsageDescription</key>
<string>Daily3News 앱에서 사용자에게 맞춤 광고를 위해 추적 권한을 요청합니다</string>
<key>UIRequiresFullScreen</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
Expand Down
28 changes: 19 additions & 9 deletions Targets/D3N/Sources/Core/LocalStorage/LocalStorageRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,28 @@

import Foundation

public struct LocalStorageRepository {
enum Keys: String {
case alreadySolvedNewsIds
public struct LocalStorageManager {
public enum Key: String {
case accessToken
case refreshToken
}

public static func saveAlreadySolvedNewsIds(ids: [Int]) {
var newIds = loadAlreadySolvedNewsIds() + ids
UserDefaults.standard.set(newIds, forKey: Keys.alreadySolvedNewsIds.rawValue)
public static func save(_ key: Key, value: String) {
UserDefaults.standard.set(value, forKey: key.rawValue)
}

public static func loadAlreadySolvedNewsIds() -> [Int] {
let ids = UserDefaults.standard.object(forKey: Keys.alreadySolvedNewsIds.rawValue) as? [Int]
return ids ?? []
public static func load(_ key: Key) -> String {
let value = UserDefaults.standard.object(forKey: key.rawValue) as? String
return value ?? ""
}

// public static func saveAlreadySolvedNewsIds(ids: [Int]) {
// var newIds = loadAlreadySolvedNewsIds() + ids
// UserDefaults.standard.set(newIds, forKey: Keys.alreadySolvedNewsIds.rawValue)
// }
//
// public static func loadAlreadySolvedNewsIds() -> [Int] {
// let ids = UserDefaults.standard.object(forKey: Keys.alreadySolvedNewsIds.rawValue) as? [Int]
// return ids ?? []
// }
}
68 changes: 68 additions & 0 deletions Targets/D3N/Sources/Domain/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// AuthClient.swift
// D3N
//
// Created by 송영모 on 11/3/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

import ComposableArchitecture
import Moya

struct AuthClient {
var appleLogin: (_ code: String, _ idToken: String) async -> Result<AuthEntity, D3NAPIError>
var appleUnlink: () async -> Result<AuthEntity, Error>
var refresh: () async -> Result<AuthEntity, Error>
var userOnboard: (_ nickname: String, _ gender: Gender, _ birthYear: Int, _ categoryList: [NewsField]) async -> Result<UserEntity, D3NAPIError>
}

extension AuthClient: TestDependencyKey {
static let previewValue = Self(
appleLogin: { code, idToken in
return .success(.init(accessToken: "", refreshToken: ""))
},
appleUnlink: { .init(.success(.init(accessToken: "", refreshToken: ""))) },
refresh: { .init(.success(.init(accessToken: "", refreshToken: ""))) },
userOnboard: { nickname, gender, birthYear, categoryList in .failure(.none) }
)

static let testValue = Self(
appleLogin: unimplemented("\(Self.self).appleLogin"),
appleUnlink: unimplemented("\(Self.self).appleUnlink"),
refresh: unimplemented("\(Self.self).refresh"),
userOnboard: unimplemented("\(Self.self).userOnboard")
)
}

extension DependencyValues {
var authClient: AuthClient {
get { self[AuthClient.self] }
set { self[AuthClient.self] = newValue }
}
}

// MARK: - Live API implementation

extension AuthClient: DependencyKey {
static let liveValue = AuthClient(
appleLogin: { code, idToken in
let target: TargetType = AuthService.appleLogin(code: code, idToken: idToken)
let response: Result<AppleLoginResponseDTO, D3NAPIError> = await D3NAPIkProvider.reqeust(target: target)
return response.map { $0.toEntity() }
},
appleUnlink: {
return .success(.init(accessToken: "", refreshToken: ""))

},
refresh: {
return .success(.init(accessToken: "", refreshToken: ""))
},
userOnboard: { nickname, gender, birthYear, categoryList in
let target: TargetType = AuthService.userOnboard(nickname: nickname, gender: gender, birthYear: birthYear, categoryList: categoryList)
let response: Result<UserOnboardResponseDTO, D3NAPIError> = await D3NAPIkProvider.reqeust(target: target)
return response.map { $0.toEntity() }
}
)
}
54 changes: 54 additions & 0 deletions Targets/D3N/Sources/Domain/Auth/AuthService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// AuthService.swift
// D3N
//
// Created by 송영모 on 11/3/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation
import Moya

public enum AuthService {
case appleLogin(code: String, idToken: String)
case appleUnlink
case refresh
case userOnboard(nickname: String, gender: Gender, birthYear: Int, categoryList: [NewsField])
}

extension AuthService: TargetType {
public var baseURL: URL { URL(string: Environment.baseURL)! }

public var path: String {
switch self {
case .appleLogin: return "auth/apple/login"
case .appleUnlink: return "auth/apple/unlink"
case .refresh: return "auth/refresh"
case .userOnboard: return "user/onboard"
}
}
public var method: Moya.Method {
switch self {
case .appleLogin: return .post
case .appleUnlink: return .delete
case .refresh: return .get
case .userOnboard: return .post
}
}
public var task: Task {
switch self {
case let .appleLogin(code: code, idToken: idToken):
return .requestJSONEncodable(AppleLoginRequestDTO(code: code, idToken: idToken))
case .appleUnlink:
return .requestPlain
case .refresh:
return .requestPlain
case let .userOnboard(nickname: nickname, gender: gender, birthYear: birthYear, categoryList: categoryList):
return .requestJSONEncodable(UserOnboardRequestDTO(nickname: nickname, gender: gender, birthYear: birthYear, categoryList: categoryList))
}
}

public var headers: [String: String]? {
return ["Authorization": "Bearer \(LocalStorageManager.load(.accessToken))"]
}
}
18 changes: 18 additions & 0 deletions Targets/D3N/Sources/Domain/Auth/DTO/AppleLoginRequestDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// AppleLoginRequestDTO.swift
// D3N
//
// Created by 송영모 on 11/3/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

struct AppleLoginRequestDTO: Codable {
let code, idToken: String

enum CodingKeys: String, CodingKey {
case code
case idToken = "id_token"
}
}
28 changes: 28 additions & 0 deletions Targets/D3N/Sources/Domain/Auth/DTO/AppleLoginResponseDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// AuthResponseDTO.swift
// D3N
//
// Created by 송영모 on 11/3/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

struct AppleLoginResponseDTO: Codable {
let appToken: String
let refreshToken: String
}

extension AppleLoginResponseDTO {
func toEntity(isShouldSaveAtLocalStorage: Bool = true) -> AuthEntity {
if isShouldSaveAtLocalStorage {
LocalStorageManager.save(.accessToken, value: self.appToken)
LocalStorageManager.save(.refreshToken, value: self.refreshToken)
}

return .init(
accessToken: self.appToken,
refreshToken: self.refreshToken
)
}
}
16 changes: 16 additions & 0 deletions Targets/D3N/Sources/Domain/Auth/DTO/UserOnboardRequestDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// UserOnboardRequestDTO.swift
// D3N
//
// Created by 송영모 on 11/4/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

struct UserOnboardRequestDTO: Codable {
let nickname: String
let gender: Gender
let birthYear: Int
let categoryList: [NewsField]
}
45 changes: 45 additions & 0 deletions Targets/D3N/Sources/Domain/Auth/DTO/UserOnboardResponseDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// UserOnboardResponseDTO.swift
// D3N
//
// Created by 송영모 on 11/4/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

import Foundation

struct UserOnboardResponseDTO: Codable {
let createdAt, modifiedAt, id, nickname: String
let gender: Gender
let birthYear: Int
let categoryList: [NewsField]
let scrapList: [Int]
let appleRefreshToken, refreshToken, memberProvider, roleType: String
let solvedQuizList: [UserOnboardResponseSolvedQuizDTO]
}

extension UserOnboardResponseDTO {
func toEntity() -> UserEntity {
return .init(
nickname: self.nickname,
gender: self.gender,
birthYear: self.birthYear,
categoryList: self.categoryList)
}
}

internal struct UserOnboardResponseSolvedQuizDTO: Codable {
let createdAt, modifiedAt: String
let id: Int
let user: String
let quizID, selectedAnswer, quizAnswer: Int
let quizResult: Bool

enum CodingKeys: String, CodingKey {
case createdAt, modifiedAt, id, user
case quizID = "quizId"
case selectedAnswer, quizAnswer, quizResult
}
}
14 changes: 14 additions & 0 deletions Targets/D3N/Sources/Domain/Auth/Entity/AuthEntity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// AuthEntity.swift
// D3N
//
// Created by 송영모 on 11/3/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

public struct AuthEntity: Codable, Equatable {
let accessToken: String
let refreshToken: String
}
16 changes: 16 additions & 0 deletions Targets/D3N/Sources/Domain/Auth/Entity/UserEntity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// UserEntity.swift
// D3N
//
// Created by 송영모 on 11/4/23.
// Copyright © 2023 sju. All rights reserved.
//

import Foundation

struct UserEntity: Codable {
let nickname: String
let gender: Gender
let birthYear: Int
let categoryList: [NewsField]
}
Loading

0 comments on commit 7391d5f

Please sign in to comment.