Skip to content
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
74 changes: 35 additions & 39 deletions Wable-iOS/Data/Mapper/CommentMapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
enum CommentMapper { }

extension CommentMapper {
static func toDomain(_ response: [DTO.Response.FetchUserComments]) -> [UserComment] {
static func toDomain(_ response: [DTO.Response.FetchUserComments]) -> [Comment] {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone(abbreviation: "KST")
Comment on lines 15 to 16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

고정 포맷 파싱에는 en_US_POSIX와 식별자 기반 타임존(Asia/Seoul) 사용 권장

약어("KST")는 환경별로 인식 실패 가능성이 있고, 고정 포맷에는 en_US_POSIX가 정석입니다. 아래처럼 교체 및 보강을 제안합니다.

         dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
-        dateFormatter.timeZone = TimeZone(abbreviation: "KST")
+        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
+        dateFormatter.timeZone = TimeZone(identifier: "Asia/Seoul")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone(abbreviation: "KST")
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(identifier: "Asia/Seoul")
🤖 Prompt for AI Agents
In Wable-iOS/Data/Mapper/CommentMapper.swift around lines 15 to 16, the
DateFormatter currently uses the "KST" abbreviation which can be unreliable and
lacks the recommended fixed-format locale; update the formatter to set locale =
Locale(identifier: "en_US_POSIX") and timeZone = TimeZone(identifier:
"Asia/Seoul") (with an optional fallback to .current or GMT if the identifier
returns nil) while keeping the same dateFormat so fixed-format parsing is robust
and deterministic.

Expand All @@ -29,30 +29,29 @@ extension CommentMapper {
postStatus = .normal
}

return UserComment(
comment: CommentInfo(
author: User(
id: comment.memberID,
nickname: comment.memberNickname,
profileURL: url,
fanTeam: fanTeam
),
id: comment.commentID,
text: comment.commentText,
createdDate: date,
status: postStatus,
like: Like(
status: comment.isLiked,
count: comment.commentLikedNumber
),
opacity: Opacity(value: comment.memberGhost)
return Comment(
id: comment.commentID,
author: User(
id: comment.memberID,
nickname: comment.memberNickname,
profileURL: url,
fanTeam: fanTeam
),
contentID: comment.contentID
text: comment.commentText,
contentID: comment.contentID,
isDeleted: false,
createdDate: date,
parentContentID: -1,
children: [],
likeCount: comment.likedCount,
isLiked: comment.isLiked,
opacity: Opacity(value: comment.memberGhost),
status: postStatus
)
}
}

static func toDomain(_ response: [DTO.Response.FetchContentComments]) -> [ContentComment] {
static func toDomain(_ contentID: Int, _ response: [DTO.Response.FetchContentComments]) -> [Comment] {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

Comment on lines 55 to 57
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

타임존/로케일 누락으로 시간 불일치(+9h) 및 기기 로케일 의존 위험

두 번째 매핑 함수에서는 timeZone/locale 설정이 없어서 디바이스 기본값을 따릅니다. 첫 번째 함수(KST 지정)와 결과가 달라질 수 있고, Asia/Seoul 기준 서비스라면 최대 +9시간 오차가 발생합니다. 고정 포맷 파싱에서는 en_US_POSIX 로케일 지정도 필수에 가깝습니다.

즉시 아래처럼 보완해 주세요.

         dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
+        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
+        dateFormatter.timeZone = TimeZone(identifier: "Asia/Seoul")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(identifier: "Asia/Seoul")
🤖 Prompt for AI Agents
In Wable-iOS/Data/Mapper/CommentMapper.swift around lines 55 to 57, the
DateFormatter is missing explicit timezone and locale settings causing
device-dependent parsing and up to +9h discrepancies; set the formatter's
timeZone to Asia/Seoul (or TimeZone(secondsFromGMT: 9*3600)) and its locale to
en_US_POSIX so the fixed "yyyy-MM-dd HH:mm:ss" format parses consistently across
devices.

Expand All @@ -70,27 +69,24 @@ extension CommentMapper {
postStatus = .normal
}

return ContentComment(
comment: CommentInfo(
author: User(
id: comment.memberID,
nickname: comment.memberNickname,
profileURL: url,
fanTeam: fanTeam
),
id: comment.commentID,
text: comment.commentText,
createdDate: date,
status: postStatus,
like: Like(
status: comment.isLiked,
count: comment.commentLikedNumber
),
opacity: Opacity(value: comment.memberGhost)
return Comment(
id: comment.commentID,
author: User(
id: comment.memberID,
nickname: comment.memberNickname,
profileURL: url,
fanTeam: fanTeam
),
parentID: comment.parentCommentID,
text: comment.commentText,
contentID: contentID,
isDeleted: comment.isDeleted,
childs: comment.childComments.map(toDomain) ?? []
createdDate: date,
parentContentID: comment.parentCommentID,
children: CommentMapper.toDomain(contentID, comment.childComments ?? []),
likeCount: comment.likedCount,
isLiked: comment.isLiked,
opacity: Opacity(value: comment.memberGhost),
status: postStatus
)
}
}
Expand Down
12 changes: 6 additions & 6 deletions Wable-iOS/Data/Mapper/ContentMapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
enum ContentMapper { }

extension ContentMapper {
static func toDomain(_ response: DTO.Response.FetchContent, _ id: Int) -> ContentTemp {
static func toDomain(_ response: DTO.Response.FetchContent, _ id: Int) -> Content {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:SS"
dateFormatter.timeZone = TimeZone(abbreviation: "KST")
Expand All @@ -29,7 +29,7 @@ extension ContentMapper {
postStatus = .normal
}

return ContentTemp(
return Content(
id: id,
author: User(
id: response.memberID,
Expand All @@ -50,7 +50,7 @@ extension ContentMapper {
)
}

static func toDomain(_ response: [DTO.Response.FetchContents]) -> [ContentTemp] {
static func toDomain(_ response: [DTO.Response.FetchContents]) -> [Content] {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:SS"
dateFormatter.timeZone = TimeZone(abbreviation: "KST")
Expand All @@ -70,7 +70,7 @@ extension ContentMapper {
postStatus = .normal
}

return ContentTemp(
return Content(
id: content.contentID,
author: User(
id: content.memberID,
Expand All @@ -92,7 +92,7 @@ extension ContentMapper {
}
}

static func toDomain(_ response: [DTO.Response.FetchUserContents]) -> [ContentTemp] {
static func toDomain(_ response: [DTO.Response.FetchUserContents]) -> [Content] {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:SS"
dateFormatter.timeZone = TimeZone(abbreviation: "KST")
Expand All @@ -112,7 +112,7 @@ extension ContentMapper {
postStatus = .normal
}

return ContentTemp(
return Content(
id: content.contentID,
author: User(
id: content.memberID,
Expand Down
44 changes: 23 additions & 21 deletions Wable-iOS/Data/RepositoryImpl/CommentRepositoryImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ final class CommentRepositoryImpl {
}

extension CommentRepositoryImpl: CommentRepository {
func fetchUserCommentList(memberID: Int, cursor: Int) -> AnyPublisher<[UserComment], WableError> {
func fetchUserCommentList(memberID: Int, cursor: Int) -> AnyPublisher<[Comment], WableError> {
return provider.request(
.fetchUserCommentList(
memberID: memberID,
Expand All @@ -29,7 +29,7 @@ extension CommentRepositoryImpl: CommentRepository {
.mapWableError()
}

func fetchUserCommentList(memberID: Int, cursor: Int) async throws -> [UserComment] {
func fetchUserCommentList(memberID: Int, cursor: Int) async throws -> [Comment] {
do {
let response = try await provider.request(
.fetchUserCommentList(memberID: memberID, cursor: cursor),
Expand All @@ -41,15 +41,15 @@ extension CommentRepositoryImpl: CommentRepository {
}
}

func fetchContentCommentList(contentID: Int, cursor: Int) -> AnyPublisher<[ContentComment], WableError> {
func fetchContentCommentList(contentID: Int, cursor: Int) -> AnyPublisher<[Comment], WableError> {
return provider.request(
.fetchContentCommentList(
contentID: contentID,
cursor: cursor
),
for: [DTO.Response.FetchContentComments].self
)
.map(CommentMapper.toDomain)
.map { CommentMapper.toDomain(contentID, $0) }
.mapWableError()
}

Expand Down Expand Up @@ -90,24 +90,24 @@ extension CommentRepositoryImpl: CommentRepository {
}

struct MockCommentRepository: CommentRepository {
func fetchUserCommentList(memberID: Int, cursor: Int) -> AnyPublisher<[UserComment], WableError> {
func fetchUserCommentList(memberID: Int, cursor: Int) -> AnyPublisher<[Comment], WableError> {
.fail(.unknownError)
}

func fetchUserCommentList(memberID: Int, cursor: Int) async throws -> [UserComment] {
func fetchUserCommentList(memberID: Int, cursor: Int) async throws -> [Comment] {
if cursor < .zero {
return Array(Self.mockUserComments.prefix(10))
}

guard let index = Self.mockUserComments.firstIndex(where: { $0.comment.id == cursor }) else {
guard let index = Self.mockUserComments.firstIndex(where: { $0.id == cursor }) else {
return []
}
let start = index + 1
let end = min(start + 10, Self.mockUserComments.count)
return Array(Self.mockUserComments[start..<end])
}

func fetchContentCommentList(contentID: Int, cursor: Int) -> AnyPublisher<[ContentComment], WableError> {
func fetchContentCommentList(contentID: Int, cursor: Int) -> AnyPublisher<[Comment], WableError> {
.fail(.unknownError)
}

Expand All @@ -123,7 +123,7 @@ struct MockCommentRepository: CommentRepository {
.fail(.unknownError)
}

static let mockUserComments: [UserComment] = {
static let mockUserComments: [Comment] = {
let mockContentID = -1
let mockUser = User(
id: 167,
Expand All @@ -132,18 +132,20 @@ struct MockCommentRepository: CommentRepository {
fanTeam: .t1
)

let temp: [UserComment] = (1...52).map { number in
UserComment(
comment: CommentInfo(
author: mockUser,
id: number,
text: "\(number)번째",
createdDate: .now,
status: .normal,
like: Like(status: false, count: 0),
opacity: .init(value: 0)
),
contentID: mockContentID
let temp: [Comment] = (1...52).map { number in
Comment(
id: number,
author: mockUser,
text: "\(number)번째",
contentID: mockContentID,
isDeleted: nil,
createdDate: .now,
parentContentID: -1,
children: [],
likeCount: 0,
isLiked: false,
opacity: .init(value: 0),
status: .normal
)
}
return temp
Expand Down
8 changes: 4 additions & 4 deletions Wable-iOS/Data/RepositoryImpl/ContentRepositoryImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ extension ContentRepositoryImpl: ContentRepository {
}
}

func fetchContentInfo(contentID: Int) -> AnyPublisher<ContentTemp, WableError> {
func fetchContentInfo(contentID: Int) -> AnyPublisher<Content, WableError> {
provider.request(
.fetchContentInfo(contentID: contentID),
for: DTO.Response.FetchContent.self
Expand All @@ -63,7 +63,7 @@ extension ContentRepositoryImpl: ContentRepository {
.mapWableError()
}

func fetchContentList(cursor: Int) -> AnyPublisher<[ContentTemp], WableError> {
func fetchContentList(cursor: Int) -> AnyPublisher<[Content], WableError> {
provider.request(
.fetchContentList(cursor: cursor),
for: [DTO.Response.FetchContents].self
Expand All @@ -72,7 +72,7 @@ extension ContentRepositoryImpl: ContentRepository {
.mapWableError()
}

func fetchUserContentList(memberID: Int, cursor: Int) -> AnyPublisher<[ContentTemp], WableError> {
func fetchUserContentList(memberID: Int, cursor: Int) -> AnyPublisher<[Content], WableError> {
provider.request(
.fetchUserContentList(memberID: memberID, cursor: cursor),
for: [DTO.Response.FetchUserContents].self
Expand All @@ -81,7 +81,7 @@ extension ContentRepositoryImpl: ContentRepository {
.mapWableError()
}

func fetchUserContentList(memberID: Int, cursor: Int) async throws -> [ContentTemp] {
func fetchUserContentList(memberID: Int, cursor: Int) async throws -> [Content] {
do {
let response = try await provider.request(
.fetchUserContentList(memberID: memberID, cursor: cursor),
Expand Down
35 changes: 3 additions & 32 deletions Wable-iOS/Domain/Entity/Comment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,17 @@

import Foundation

// MARK: - 댓글 핵심 정보
// MARK: - 댓글 정보

struct CommentInfo: Identifiable, Hashable {
let author: User
struct Comment: Identifiable, Hashable, Likable {
let id: Int
let text: String
let createdDate: Date?

var status: PostStatus
var like: Like
var opacity: Opacity
}

// MARK: - 유저가 작성한 댓글

struct UserComment: Hashable {
let comment: CommentInfo
let contentID: Int
}

// MARK: - 게시물 댓글

struct ContentComment: Hashable {
let comment: CommentInfo
let parentID: Int
let isDeleted: Bool
let childs: [ContentComment]
}

// MARK: - 댓글 정보 (임시)

struct CommentTemp: Likable {
let author: User
let text: String
let commentID: Int
let contentID: Int
let isDeleted: Bool?
let createdDate: Date?
let parentContentID: Int?
let children: [ContentComment]
let children: [Comment]

var likeCount: Int
var isLiked: Bool
Expand Down
31 changes: 2 additions & 29 deletions Wable-iOS/Domain/Entity/Content.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,9 @@

import Foundation

// MARK: - 게시물 리스트
// MARK: - 게시물 정보

struct Content: Hashable {
let content: UserContent
let isDeleted: Bool
}

struct UserContent: Identifiable, Hashable {
let id: Int
let contentInfo: ContentTemp
}

// MARK: - 게시물 상세 정보

struct ContentInfo: Hashable {
let author: User
let createdDate: Date?
let title: String
let imageURL: URL?
let text: String

var status: PostStatus
var like: Like
var opacity: Opacity
var commentCount: Int
}

// MARK: - 게시물 정보 (임시)

struct ContentTemp: Identifiable, Hashable, Likable {
struct Content: Identifiable, Hashable, Likable {
let id: Int
let author: User
let text: String
Expand Down
Loading