Skip to content

Commit

Permalink
Merge pull request #54 from Team-Ampersand/46-abstraction-module
Browse files Browse the repository at this point in the history
🔀 :: 추상화 모듈 작업
  • Loading branch information
baekteun authored Jun 29, 2023
2 parents 2bf4691 + 7d459e8 commit 28aa3dc
Show file tree
Hide file tree
Showing 17 changed files with 483 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public extension TargetDependency {
}

public extension TargetDependency.SPM {
static let GRDB = TargetDependency.external(name: "GRDB")
static let IQKeyboardManagerSwift = TargetDependency.external(name: "IQKeyboardManagerSwift")
static let Store = TargetDependency.external(name: "Store")
static let Moordinator = TargetDependency.external(name: "Moordinator")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public extension ModulePaths {

public extension ModulePaths {
enum Core: String, MicroTargetPathConvertable {
case Database
case KeyValueStore
case JwtStore
}
}
Expand Down
1 change: 1 addition & 0 deletions Projects/App/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ let targets: [Target] = [
infoPlist: .file(path: "Support/Info.plist"),
sources: ["Sources/**"],
resources: ["Resources/**"],
entitlements: "Support/Dotori.entitlements",
scripts: scripts,
dependencies: [
.feature(target: .RootFeature),
Expand Down
10 changes: 10 additions & 0 deletions Projects/App/Support/Dotori.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.security.application-groups</key>
<array>
<string>group.msg.dotori</string>
</array>
</dict>
</plist>
76 changes: 76 additions & 0 deletions Projects/Core/Database/Interface/LocalDatabase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import Foundation
import GRDB

public protocol LocalDatabase {
func save(record: some FetchableRecord & PersistableRecord) throws

func save(records: [some FetchableRecord & PersistableRecord]) throws

func readRecords<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
filter: [String: some DatabaseValueConvertible],
ordered: [some SQLOrderingTerm]
) throws -> [Record]

func readRecords<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
filter: [String: some DatabaseValueConvertible],
ordered: [some SQLOrderingTerm],
limit: Int,
offset: Int?
) throws -> [Record]

func readRecord<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
at key: some DatabaseValueConvertible
) throws -> Record?

func updateRecord<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
at key: some DatabaseValueConvertible,
transform: (inout Record) -> Void
) throws

func delete(
as record: some FetchableRecord & PersistableRecord
) throws

func delete<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
key: some DatabaseValueConvertible
) throws

func deleteAll<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type
) throws
}

public extension LocalDatabase {
func readRecords<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
filter: [String: some DatabaseValueConvertible],
ordered: [some SQLOrderingTerm],
limit: Int,
offset: Int?
) throws -> [Record] {
try self.readRecords(
as: record,
filter: filter,
ordered: ordered,
limit: limit,
offset: offset
)
}

func readRecords<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
filter: [String: some DatabaseValueConvertible],
ordered: [some SQLOrderingTerm]
) throws -> [Record] {
try self.readRecords(
as: record,
filter: filter,
ordered: ordered
)
}
}
21 changes: 21 additions & 0 deletions Projects/Core/Database/Project.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin

let project = Project.module(
name: ModulePaths.Core.Database.rawValue,
targets: [
.interface(module: .core(.Database), dependencies: [
.SPM.GRDB
]),
.implements(module: .core(.Database), dependencies: [
.core(target: .Database, type: .interface),
]),
.testing(module: .core(.Database), dependencies: [
.core(target: .Database, type: .interface)
]),
.tests(module: .core(.Database), dependencies: [
.core(target: .Database)
])
]
)
147 changes: 147 additions & 0 deletions Projects/Core/Database/Sources/GRDBLocalDatabase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import DatabaseInterface
import Foundation
import GRDB

final class GRDBLocalDatabase: LocalDatabase {
private let dbQueue: DatabaseQueue
private let migrator: DatabaseMigrator

init(migrate: (inout DatabaseMigrator) -> Void) {
var url = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.msg.dotori")!

if #available(iOS 16, macOS 13.0, *) {
url.append(path: "Dotori")
} else {
url.appendPathComponent("Dotori")
}

try? FileManager.default.createDirectory(
at: url,
withIntermediateDirectories: false,
attributes: [
FileAttributeKey.protectionKey: URLFileProtection.none
]
)

if #available(iOS 16.0, macOS 13.0, *) {
url.append(path: "Dotori.sqlite")
} else {
url.appendPathComponent("Dotori.sqlite")
}

var dir = ""

if #available(iOS 16.0, macOS 13.0, *) {
dir = url.path()
} else {
dir = url.path
}

if #available(iOS 16.0, macOS 13.0, *) {
dir.replace("%20", with: " ")
} else {
dir = dir.replacingOccurrences(of: "%20", with: " ")
}

do {
self.dbQueue = try DatabaseQueue(path: dir)
} catch {
fatalError()
}

var migrator = DatabaseMigrator()
migrate(&migrator)
self.migrator = migrator
try? self.migrator.migrate(self.dbQueue)
}

func save(record: some FetchableRecord & PersistableRecord) throws {
try self.dbQueue.write { db in
try record.save(db)
}
}

func save(records: [some FetchableRecord & PersistableRecord]) throws {
try self.dbQueue.write { db in
try records.forEach {
try $0.save(db)
}
}
}

func readRecords<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
filter: [String: some DatabaseValueConvertible],
ordered: [some SQLOrderingTerm]
) throws -> [Record] {
try self.dbQueue.read { db in
try record
.filter(key: filter)
.order(ordered)
.fetchAll(db)
}
}

func readRecords<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
filter: [String: some DatabaseValueConvertible],
ordered: [some SQLOrderingTerm],
limit: Int,
offset: Int?
) throws -> [Record] {
try self.dbQueue.read { db in
try record
.filter(key: filter)
.order(ordered)
.limit(limit, offset: offset)
.fetchAll(db)
}
}

func readRecord<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
at key: some DatabaseValueConvertible
) throws -> Record? {
try dbQueue.read { db in
try record.fetchOne(db, key: key)
}
}

func updateRecord<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
at key: some DatabaseValueConvertible,
transform: (inout Record) -> Void
) throws {
try dbQueue.write { db in
if var value = try record.fetchOne(db, key: key) {
try value.updateChanges(db, modify: transform)
}
}
}

func delete(
as record: some FetchableRecord & PersistableRecord
) throws {
try dbQueue.write { db in
_ = try record.delete(db)
}
}

func delete<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
key: some DatabaseValueConvertible
) throws {
try dbQueue.write { db in
_ = try record.deleteOne(db, key: key)
}
}

func deleteAll<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type
) throws {
try dbQueue.write { db in
_ = try record.deleteAll(db)
}
}
}
76 changes: 76 additions & 0 deletions Projects/Core/Database/Testing/MockLocalDatabase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import DatabaseInterface
import GRDB

final class MockLocalDatabase: LocalDatabase {
var saveCallCount = 0
func save(record: some FetchableRecord & PersistableRecord) throws {
saveCallCount += 1
}

var allSaveCallCount = 0
func save(records: [some FetchableRecord & PersistableRecord]) throws {
allSaveCallCount += 1
}

var readRecordsCallCount = 0
func readRecords<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
filter: [String: some DatabaseValueConvertible],
ordered: [some SQLOrderingTerm]
) throws -> [Record] {
readRecordsCallCount += 1
return []
}

var readRecordsWithLimitCallCount = 0
func readRecords<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
filter: [String: some DatabaseValueConvertible],
ordered: [some SQLOrderingTerm],
limit: Int,
offset: Int?
) throws -> [Record] {
readRecordsWithLimitCallCount += 1
return []
}

var readRecordCallCount = 0
func readRecord<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
at key: some DatabaseValueConvertible
) throws -> Record? {
readRecordCallCount += 1
return nil
}

var updateRecordCallCount = 0
func updateRecord<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
at key: some DatabaseValueConvertible,
transform: (inout Record) -> Void
) throws {
updateRecordCallCount += 1
}

var deleteCallCount = 0
func delete(
as record: some FetchableRecord & PersistableRecord
) throws {
deleteCallCount += 1
}

var deleteKeyCallCount = 0
func delete<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type,
key: some DatabaseValueConvertible
) throws {
deleteKeyCallCount += 1
}

var deleteAllCallCount = 0
func deleteAll<Record: FetchableRecord & PersistableRecord>(
as record: Record.Type
) throws {
deleteAllCallCount += 1
}
}
11 changes: 11 additions & 0 deletions Projects/Core/Database/Tests/DatabaseTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import XCTest

final class DatabaseTests: XCTestCase {
override func setUpWithError() throws {}

override func tearDownWithError() throws {}

func testExample() {
XCTAssertEqual(1, 1)
}
}
8 changes: 8 additions & 0 deletions Projects/Core/KeyValueStore/Interface/KeyValueStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Foundation

public protocol KeyValueStore {
func save(key: String, value: Any)
func load(key: String) -> Any?
func load<T>(key: String) -> T?
func delete(key: String)
}
Loading

0 comments on commit 28aa3dc

Please sign in to comment.