Skip to content

Commit

Permalink
[CAT-104] UserDefaultsClient 모듈 추가 (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
devMinseok authored Aug 4, 2024
1 parent ccc826b commit 8e75679
Show file tree
Hide file tree
Showing 17 changed files with 260 additions and 41 deletions.
Binary file modified DependencyGraph/mohanyang_dev_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified DependencyGraph/mohanyang_prod_graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ public enum Core: String, Modulable {
case KeychainClient
case UserNotificationClient
case DatabaseClient
case UserDefaultsClient
}
6 changes: 6 additions & 0 deletions Projects/Core/Core/Sources/Exports.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@
@_exported import APIClientInterface
@_exported import KeychainClient
@_exported import KeychainClientInterface
@_exported import UserDefaultsClient
@_exported import UserDefaultsClientInterface
@_exported import UserNotificationClient
@_exported import UserNotificationClientInterface
@_exported import DatabaseClient
@_exported import DatabaseClientInterface
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public struct DatabaseClient {
/// Use create<T: Persistable>(object:).
public var create: @Sendable (any Persistable) async throws -> Void
/// Use read<T: Persistable>(_ type:).
public var read: @Sendable (any Persistable.Type) async throws -> [Object]
public var read: @Sendable (Object.Type) async throws -> [Object]
/// Use read<T: Persistable>(_ type:, predicateFormat:, args:).
public var readWithFilter: @Sendable (Object.Type, String, Any) async throws -> [Object]
/// Use update<T: Persistable>(_ object:)
Expand All @@ -36,7 +36,7 @@ public struct DatabaseClient {
}

public func read<T: Persistable>(_ type: T.Type) async throws -> [T] {
let results = try await self.read(type)
let results = try await self.read(type.ManagedObject)
return await Self.realmActor?.convertToPersistable(type: type, objects: results) ?? []
}

Expand Down
2 changes: 1 addition & 1 deletion Projects/Core/DatabaseClient/Sources/DatabaseClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ extension DatabaseClient: DependencyKey {
},
read: { type in
if let realmActor {
let results = await realmActor.read(type.ManagedObject as! Object.Type)
let results = await realmActor.read(type as! Object.Type)
return results
} else {
throw(NSError(domain: "Realm is not initialized", code: 0))
Expand Down
3 changes: 1 addition & 2 deletions Projects/Core/KeychainClient/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ let project: Project = .makeTMABasedProject(
targets: [
.sources,
.interface,
.tests,
.testing
.tests
],
dependencies: [
.interface: [
Expand Down
30 changes: 0 additions & 30 deletions Projects/Core/KeychainClient/Testing/KeychainClientTesting.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// UserDefaultsClientInterface.swift
// UserDefaultsClient
//
// Created by devMinseok on 8/4/24.
//

import Foundation

import Dependencies
import DependenciesMacros

@DependencyClient
public struct UserDefaultsClient {
// MARK: - Create, Update

public var setString: @Sendable (String?, _ key: String) async -> Void
public var setBool: @Sendable (Bool, _ key: String) async -> Void
public var setData: @Sendable (Data?, _ key: String) async -> Void
public var setDouble: @Sendable (Double, _ key: String) async -> Void
public var setInteger: @Sendable (Int, _ key: String) async -> Void

// MARK: - Read

public var stringForKey: @Sendable (String) -> String?
public var boolForKey: @Sendable (String) -> Bool = { _ in false }
public var dataForKey: @Sendable (String) -> Data?
public var doubleForKey: @Sendable (String) -> Double = { _ in 0.0 }
public var integerForKey: @Sendable (String) -> Int = { _ in 0 }


// MARK: - Delete

public var remove: @Sendable (String) async -> Void

// MARK: - Reset

public var removePersistentDomain: @Sendable (_ bundleId: String) -> Void
}

extension UserDefaultsClient: TestDependencyKey {
public static var testValue = Self()
public static var previewValue = Self()
}
28 changes: 28 additions & 0 deletions Projects/Core/UserDefaultsClient/Project.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// UserDefaultsClientTesting.swift
// UserDefaultsClientManifests
//
// Created by devMinseok on 8/4/24.
//

import ProjectDescription
import ProjectDescriptionHelpers

@_spi(Core)
@_spi(Shared)
import DependencyPlugin

let project: Project = .makeTMABasedProject(
module: Core.UserDefaultsClient,
scripts: [],
targets: [
.sources,
.interface,
.tests
],
dependencies: [
.interface: [
.dependency(rootModule: Shared.self)
]
]
)
34 changes: 34 additions & 0 deletions Projects/Core/UserDefaultsClient/Sources/UserDefaultsClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// UserDefaultsClient.swift
// UserDefaultsClient
//
// Created by devMinseok on 8/4/24.
//

import Foundation

import Dependencies

import UserDefaultsClientInterface

extension UserDefaultsClient: DependencyKey {
public static let liveValue: UserDefaultsClient = .live()

public static func live() -> UserDefaultsClient {
let userDefaults = { UserDefaults.standard }
return .init(
setString: { userDefaults().set($0, forKey: $1) },
setBool: { userDefaults().set($0, forKey: $1) },
setData: { userDefaults().set($0, forKey: $1) },
setDouble: { userDefaults().set($0, forKey: $1) },
setInteger: { userDefaults().set($0, forKey: $1) },
stringForKey: { userDefaults().string(forKey: $0) },
boolForKey: { userDefaults().bool(forKey: $0) },
dataForKey: { userDefaults().data(forKey: $0) },
doubleForKey: { userDefaults().double(forKey: $0) },
integerForKey: { userDefaults().integer(forKey: $0) },
remove: { userDefaults().removeObject(forKey: $0) },
removePersistentDomain: { userDefaults().removePersistentDomain(forName: $0) }
)
}
}
129 changes: 129 additions & 0 deletions Projects/Core/UserDefaultsClient/Tests/UserDefaultsClientTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//
// UserDefaultsClientTests.swift
// UserDefaultsClient
//
// Created by devMinseok on 8/4/24.
//

import XCTest

import UserDefaultsClient
import UserDefaultsClientInterface

import Dependencies

final class UserDefaultsClientTests: XCTestCase {
@Dependency(UserDefaultsClient.self) var userDefaultsClient

override func tearDown() {
withDependencies {
$0[UserDefaultsClient.self] = UserDefaultsClient.live()
} operation: {
userDefaultsClient.removePersistentDomain(bundleId: Bundle.main.bundleIdentifier ?? "")
}
super.tearDown()
}

func testSetString() async {
await withDependencies {
$0[UserDefaultsClient.self] = UserDefaultsClient.live()
} operation: {
// given
let testKey: String = "TestKey"
let testValue: String = "TestString"

// when
await userDefaultsClient.setString(testValue, key: testKey)

// then
let result = userDefaultsClient.stringForKey(testKey)
XCTAssertEqual(result, testValue)
}
}

func testSetBool() async {
await withDependencies {
$0[UserDefaultsClient.self] = UserDefaultsClient.live()
} operation: {
// given
let testKey: String = "TestKeyBool"
let testValue: Bool = true

// when
await userDefaultsClient.setBool(testValue, testKey)

// then
let result = userDefaultsClient.boolForKey(testKey)
XCTAssertEqual(result, testValue)
}
}

func testSetData() async {
await withDependencies {
$0[UserDefaultsClient.self] = UserDefaultsClient.live()
} operation: {
// given
let testKey: String = "TestKeyData"
let testValue: Data = "TestData".data(using: .utf8)!

// when
await userDefaultsClient.setData(testValue, testKey)

// then
let result = userDefaultsClient.dataForKey(testKey)
XCTAssertEqual(result, testValue)
}
}

func testSetDouble() async {
await withDependencies {
$0[UserDefaultsClient.self] = UserDefaultsClient.live()
} operation: {
// given
let testKey: String = "TestKeyDouble"
let testValue: Double = 123.456

// when
await userDefaultsClient.setDouble(testValue, testKey)

// then
let result = userDefaultsClient.doubleForKey(testKey)
XCTAssertEqual(result, testValue)
}
}

func testSetInteger() async {
await withDependencies {
$0[UserDefaultsClient.self] = UserDefaultsClient.live()
} operation: {
// given
let testKey: String = "TestKeyInteger"
let testValue: Int = 123

// when
await userDefaultsClient.setInteger(testValue, testKey)

// then
let result = userDefaultsClient.integerForKey(testKey)
XCTAssertEqual(result, testValue)
}
}

func testRemove() async {
await withDependencies {
$0[UserDefaultsClient.self] = UserDefaultsClient.live()
} operation: {
// given
let testKey: String = "TestKeyRemove"
let testValue: String = "TestString"
await userDefaultsClient.setString(testValue, testKey)

// when
await userDefaultsClient.remove(testKey)

// then
let result = userDefaultsClient.stringForKey(testKey)
XCTAssertNil(result)
}
}
}
12 changes: 10 additions & 2 deletions Projects/Core/UserNotificationClient/Sources/Implementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ extension UserNotificationClient {
withCompletionHandler completionHandler: @escaping () -> Void
) {
self.continuation.yield(
.didReceiveResponse(.init(rawValue: response)) { completionHandler() }
.didReceiveResponse(.init(rawValue: response)) {
Task { @MainActor in
completionHandler()
}
}
)
}

Expand All @@ -96,7 +100,11 @@ extension UserNotificationClient {
@escaping (UNNotificationPresentationOptions) -> Void
) {
self.continuation.yield(
.willPresentNotification(.init(rawValue: notification)) { completionHandler($0) }
.willPresentNotification(.init(rawValue: notification)) { options in
Task { @MainActor in
completionHandler(options)
}
}
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion Projects/Domain/Domain/Sources/Exports.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
//

@_exported import AppService
@_exported import AppServiceInterface
@_exported import PushService
2 changes: 1 addition & 1 deletion Projects/Feature/Feature/Sources/AppCore.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// AppCore.swift
// AppServiceInterface
// Feature
//
// Created by devMinseok on 7/22/24.
// Copyright © 2024 PomoNyang. All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion Projects/Feature/Feature/Sources/AppDelegateCore.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// AppDelegateCore.swift
// AppServiceInterface
// Feature
//
// Created by devMinseok on 7/22/24.
// Copyright © 2024 PomoNyang. All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion Projects/Feature/Feature/Sources/AppView.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// AppServcieView.swift
// AppServiceInterface
// Feature
//
// Created by devMinseok on 7/22/24.
// Copyright © 2024 PomoNyang. All rights reserved.
Expand Down

0 comments on commit 8e75679

Please sign in to comment.