Skip to content

Commit

Permalink
Merge pull request #89 from boostcampwm-2024/feature/photoObject
Browse files Browse the repository at this point in the history
[Feature] 사진 오브젝트 설계, 유즈케이스 구현
  • Loading branch information
taipaise authored Nov 19, 2024
2 parents fc98c79 + 9973ffa commit 35c267f
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 1 deletion.
28 changes: 28 additions & 0 deletions Domain/Domain.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@
objects = {

/* Begin PBXBuildFile section */
003D5A2B2CEB1FF0005F3D09 /* PhotoObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003D5A2A2CEB1FEA005F3D09 /* PhotoObject.swift */; };
003D5A2D2CEB21AA005F3D09 /* AddPhotoUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003D5A2C2CEB217E005F3D09 /* AddPhotoUseCase.swift */; };
003D5A2F2CEB21B6005F3D09 /* AddPhotoUseCaseInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003D5A2E2CEB21B2005F3D09 /* AddPhotoUseCaseInterface.swift */; };
004B217D2CEB2B2300A5BEB8 /* DomainError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 004B217C2CEB2B2300A5BEB8 /* DomainError.swift */; };
00683D692CE37F2F000D28E4 /* DrawingObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00683D682CE37F2F000D28E4 /* DrawingObject.swift */; };
00683D722CE3A74A000D28E4 /* DrawObjectUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00683D712CE3A74A000D28E4 /* DrawObjectUseCase.swift */; };
007BCEDC2CEB852C009E6935 /* AddPhotoUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007BCEDB2CEB852C009E6935 /* AddPhotoUseCaseTests.swift */; };
0080E8BB2CE2ECD80095B958 /* WhiteboardObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0080E8BA2CE2ECC60095B958 /* WhiteboardObject.swift */; };
0080E8CE2CE4463B0095B958 /* WhiteboardObjectRepositoryInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0080E8CD2CE4462E0095B958 /* WhiteboardObjectRepositoryInterface.swift */; };
0080E91A2CE4B0880095B958 /* DrawObjectUseCaseInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0080E9182CE4B0880095B958 /* DrawObjectUseCaseInterface.swift */; };
Expand Down Expand Up @@ -62,8 +67,13 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
003D5A2A2CEB1FEA005F3D09 /* PhotoObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoObject.swift; sourceTree = "<group>"; };
003D5A2C2CEB217E005F3D09 /* AddPhotoUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddPhotoUseCase.swift; sourceTree = "<group>"; };
003D5A2E2CEB21B2005F3D09 /* AddPhotoUseCaseInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddPhotoUseCaseInterface.swift; sourceTree = "<group>"; };
004B217C2CEB2B2300A5BEB8 /* DomainError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainError.swift; sourceTree = "<group>"; };
00683D682CE37F2F000D28E4 /* DrawingObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawingObject.swift; sourceTree = "<group>"; };
00683D712CE3A74A000D28E4 /* DrawObjectUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawObjectUseCase.swift; sourceTree = "<group>"; };
007BCEDB2CEB852C009E6935 /* AddPhotoUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddPhotoUseCaseTests.swift; sourceTree = "<group>"; };
0080E8BA2CE2ECC60095B958 /* WhiteboardObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhiteboardObject.swift; sourceTree = "<group>"; };
0080E8CD2CE4462E0095B958 /* WhiteboardObjectRepositoryInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhiteboardObjectRepositoryInterface.swift; sourceTree = "<group>"; };
0080E9182CE4B0880095B958 /* DrawObjectUseCaseInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawObjectUseCaseInterface.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -112,6 +122,14 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
004B217B2CEB2B0B00A5BEB8 /* Common */ = {
isa = PBXGroup;
children = (
004B217C2CEB2B2300A5BEB8 /* DomainError.swift */,
);
path = Common;
sourceTree = "<group>";
};
00683D702CE3A72F000D28E4 /* UseCase */ = {
isa = PBXGroup;
children = (
Expand All @@ -121,13 +139,15 @@
5BDFD9392CE2EE3100DA4F5B /* WhiteboardUseCase.swift */,
00D2DD852CE887540089F0BA /* ManageWhiteboardObjectUseCase.swift */,
00D2DD902CE88D260089F0BA /* ManageWhiteboardToolUseCase.swift */,
003D5A2C2CEB217E005F3D09 /* AddPhotoUseCase.swift */,
);
path = UseCase;
sourceTree = "<group>";
};
0080E8592CE19EBD0095B958 /* Sources */ = {
isa = PBXGroup;
children = (
004B217B2CEB2B0B00A5BEB8 /* Common */,
00D2DD962CE8A4DB0089F0BA /* Model */,
0080E8CB2CE4461F0095B958 /* Interface */,
0080E8B92CE2ECB50095B958 /* Entity */,
Expand All @@ -153,6 +173,7 @@
5BDFD9332CE1F7DB00DA4F5B /* Whiteboard.swift */,
0080E8BA2CE2ECC60095B958 /* WhiteboardObject.swift */,
00683D682CE37F2F000D28E4 /* DrawingObject.swift */,
003D5A2A2CEB1FEA005F3D09 /* PhotoObject.swift */,
);
path = Entity;
sourceTree = "<group>";
Expand Down Expand Up @@ -185,6 +206,7 @@
5B6542472CE44631000168AD /* WhiteboardUseCaseInterface.swift */,
00D2DD832CE8864B0089F0BA /* ManageWhiteboardObjectUseCaseInterface.swift */,
00D2DD872CE88BD80089F0BA /* ManageWhiteboardToolUseCaseInterface.swift */,
003D5A2E2CEB21B2005F3D09 /* AddPhotoUseCaseInterface.swift */,
);
path = UseCase;
sourceTree = "<group>";
Expand All @@ -195,6 +217,7 @@
0080E9562CE4D8760095B958 /* DrawObjectUseCaseTests.swift */,
00D2DD922CE88EC70089F0BA /* ManageWhiteboardToolUseCaseTests.swift */,
00D2DD942CE88EDA0089F0BA /* ManageWhiteboardObjectsUseCaseTests.swift */,
007BCEDB2CEB852C009E6935 /* AddPhotoUseCaseTests.swift */,
);
path = DomainTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -373,6 +396,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
007BCEDC2CEB852C009E6935 /* AddPhotoUseCaseTests.swift in Sources */,
00D2DD952CE88EDA0089F0BA /* ManageWhiteboardObjectsUseCaseTests.swift in Sources */,
0080E9582CE4D8760095B958 /* DrawObjectUseCaseTests.swift in Sources */,
00D2DD932CE88EC70089F0BA /* ManageWhiteboardToolUseCaseTests.swift in Sources */,
Expand All @@ -383,7 +407,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
003D5A2F2CEB21B6005F3D09 /* AddPhotoUseCaseInterface.swift in Sources */,
A8E97C052CE5E3AB00B28063 /* ProfileUseCaseInterface.swift in Sources */,
004B217D2CEB2B2300A5BEB8 /* DomainError.swift in Sources */,
A8E97BDB2CE5A6D500B28063 /* ProfileRepositoryInterface.swift in Sources */,
A8E97BD92CE5A6B800B28063 /* ProfileIcon.swift in Sources */,
A8E97C072CE5E45500B28063 /* WhiteboardObjectSendUseCase.swift in Sources */,
Expand All @@ -395,13 +421,15 @@
5B6542482CE44631000168AD /* WhiteboardUseCaseInterface.swift in Sources */,
00D2DD842CE8864B0089F0BA /* ManageWhiteboardObjectUseCaseInterface.swift in Sources */,
00D2DD882CE88BD80089F0BA /* ManageWhiteboardToolUseCaseInterface.swift in Sources */,
003D5A2B2CEB1FF0005F3D09 /* PhotoObject.swift in Sources */,
0080E8CE2CE4463B0095B958 /* WhiteboardObjectRepositoryInterface.swift in Sources */,
0080E8BB2CE2ECD80095B958 /* WhiteboardObject.swift in Sources */,
6FBC909B2CE5A6AE000FEB5A /* WhiteObjectSendUseCaseInterface.swift in Sources */,
6F21477B2CE4CEAE00B55E2C /* TextObject.swift in Sources */,
00683D722CE3A74A000D28E4 /* DrawObjectUseCase.swift in Sources */,
00D2DD8F2CE88C640089F0BA /* WhiteboardTool.swift in Sources */,
0080E91A2CE4B0880095B958 /* DrawObjectUseCaseInterface.swift in Sources */,
003D5A2D2CEB21AA005F3D09 /* AddPhotoUseCase.swift in Sources */,
00D2DD912CE88D260089F0BA /* ManageWhiteboardToolUseCase.swift in Sources */,
00D2DD862CE887540089F0BA /* ManageWhiteboardObjectUseCase.swift in Sources */,
00683D692CE37F2F000D28E4 /* DrawingObject.swift in Sources */,
Expand Down
27 changes: 27 additions & 0 deletions Domain/Domain/Sources/Common/DomainError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// DomainError.swift
// Domain
//
// Created by 이동현 on 11/18/24.
//

import Foundation

public enum DomainError {
case cannotWriteFile
case cannotCreateDirectory
case cannotFindDirectory
}

extension DomainError: LocalizedError {
public var errorDescription: String? {
switch self {
case .cannotWriteFile:
return "파일을 쓸 수 없습니다."
case .cannotCreateDirectory:
return "디렉터리를 생성할 수 없습니다."
case .cannotFindDirectory:
return "디렉터리를 찾을 수 없습니다."
}
}
}
2 changes: 1 addition & 1 deletion Domain/Domain/Sources/Entity/DrawingObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//
import Foundation

public class DrawingObject: WhiteboardObject {
public final class DrawingObject: WhiteboardObject {
public private(set) var points: [CGPoint]
public let lineWidth: CGFloat

Expand Down
25 changes: 25 additions & 0 deletions Domain/Domain/Sources/Entity/PhotoObject.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// PhotoObject.swift
// Domain
//
// Created by 이동현 on 11/18/24.
//

import Foundation

public final class PhotoObject: WhiteboardObject {
public let photoURL: URL

public init(
id: UUID,
position: CGPoint,
size: CGSize,
photoURL: URL
) {
self.photoURL = photoURL
super.init(
id: id,
position: position,
size: size)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// AddPhotoUseCaseInterface.swift
// Domain
//
// Created by 이동현 on 11/18/24.
//

import Foundation

public protocol AddPhotoUseCaseInterface {

/// 사진 오브젝트를 추가합니다.
/// - Parameters:
/// - imageData: 추가할 사진의 데이터
/// - position: 사진을 추가할 위치 (origin)
/// - size: 사진 객체
/// - Returns:
func addPhoto(
imageData: Data,
position: CGPoint,
size: CGSize
) throws -> PhotoObject
}
55 changes: 55 additions & 0 deletions Domain/Domain/Sources/UseCase/AddPhotoUseCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// AddPhotoUseCase.swift
// Domain
//
// Created by 이동현 on 11/18/24.
//

import Foundation

public final class AddPhotoUseCase: AddPhotoUseCaseInterface {
private let photoDirectory: URL
private let fileManager: FileManager

public init() throws {
fileManager = FileManager.default
guard
let documentDirectory = fileManager
.urls(for: .documentDirectory, in: .userDomainMask)
.first
else { throw DomainError.cannotCreateDirectory }
photoDirectory = documentDirectory.appending(path: "photos")

if !fileManager.fileExists(atPath: photoDirectory.path()) {
do {
try fileManager.createDirectory(at: photoDirectory, withIntermediateDirectories: true)
} catch {
throw DomainError.cannotCreateDirectory
}
}
}

public func addPhoto(
imageData: Data,
position: CGPoint,
size: CGSize
) throws -> PhotoObject {
let uuid = UUID()
let photoname = "\(uuid.uuidString).jpg"
let photoURL = photoDirectory.appending(path: photoname)

do {
try imageData.write(to: photoURL)
} catch {
throw DomainError.cannotWriteFile
}

let photoObject = PhotoObject(
id: uuid,
position: position,
size: size,
photoURL: photoURL)

return photoObject
}
}
53 changes: 53 additions & 0 deletions Domain/DomainTests/AddPhotoUseCaseTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// AddPhotoUseCaseTests.swift
// DomainTests
//
// Created by 이동현 on 11/18/24.
//

import Domain
import XCTest

final class AddPhotoUseCaseTests: XCTestCase {
private var useCase: AddPhotoUseCase!

override func setUp() {
super.setUp()

do {
useCase = try AddPhotoUseCase()
} catch {
XCTFail("init photoUseCase should success.")
}
}

override func tearDown() {
useCase = nil
super.tearDown()
}

// 사진 객체 생성 성공 테스트
func testAddPhotoSuccess() throws {
// 준비
let dummyImageData = Data()
let position = CGPoint(x: 100, y: 100)
let size = CGSize(width: 200, height: 200)
let photoObject: PhotoObject

// 실행
do {
photoObject = try useCase.addPhoto(
imageData: dummyImageData,
position: position,
size: size)
} catch {
XCTFail("photoObject should not fail.")
return
}

// 검증
XCTAssertEqual(photoObject.photoURL.lastPathComponent.suffix(4), ".jpg")
XCTAssertEqual(photoObject.position, position)
XCTAssertEqual(photoObject.size, size)
}
}

0 comments on commit 35c267f

Please sign in to comment.