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/#45] 온보딩 개발 #48

Merged
merged 10 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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