Final (22)
내용이 많아 이어서 작성…
커뮤니티 유저 프로필 구현
ChatManager 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import FoundationAdd
import FirebaseStorage
import FirebaseDatabase
import FirebaseFirestore
class ChatManager {
func getSenders(channelName: String, completion: @escaping ([String]) -> Void) {
channelCollection.whereField(db_channelName, isEqualTo: channelName).getDocuments { (snapshot, error) in
guard let snapshot = snapshot, error == nil else {
print("Error fetching channel: \(String(describing: error))")
completion([])
return
}
var senderIds = Set<String>()
for document in snapshot.documents {
let threadCollection = channelCollection.document(document.documentID).collection("thread")
threadCollection.getDocuments { (threadSnapshot, error) in
guard let threadSnapshot = threadSnapshot, error == nil else {
print("Error fetching thread: \(String(describing: error))")
return
}
for threadDocument in threadSnapshot.documents {
if let senderId = threadDocument.data()["senderId"] as? String {
senderIds.insert(senderId)
}
}
completion(Array(senderIds))
}
}
}
}
}
보내는 사람이 누군지를 알아내는 함수이다.
ChatVC - 보낸 사람 프로필 이미지 표시 기능 정리
개요
채팅 화면에서 각 발신자의 프로필 이미지를 메시지 셀 옆에 표시하기 위해 다음을 구현했다:
- 본인 및 채널 내 발신자의 프로필 이미지 URL을 불러오기
- 이미지 캐싱을 통한 중복 요청 방지
- MessagesKit 델리게이트를 활용한 셀 아바타 표시
1. 주요 프로퍼티
역할 요약:
chatFirestoreStream
: 채팅 메시지를 수신하는 스트림chatManager
: 채널에 포함된 사용자 ID 리스트를 불러오기 위한 매니저profileImageUrls
: senderId → 이미지 URL 매핑imageCache
: senderId → UIImage 매핑 (중복 요청 방지 목적)
정의:
1
2
3
4
5
let chatFirestoreStream = ChatFirestoreStream()
let chatManager = ChatManager()
private var profileImageUrls = [String: String]()
private var imageCache = [String: UIImage]()
2. viewDidLoad() 내부 동작
무엇을 하나?
- 본인 프로필 이미지 먼저 불러와 저장
- 이후 채널 내 모든 sender의 이미지를 요청
- 메시지 콜렉션뷰 초기화 및 스크롤 위치 조정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
override func viewDidLoad() {
super.viewDidLoad()
configureColor()
fetchDisplayNameAndProfileImage { [weak self] displayName, imageUrl in
self?.currentDisplayName = displayName ?? "Unknown"
if let profileImageUrl = imageUrl, let userId = self?.user?.uid {
self?.profileImageUrls[userId] = profileImageUrl
}
self?.messagesCollectionView.reloadData()
DispatchQueue.main.async {
self?.messagesCollectionView.scrollToLastItem()
}
}
getSenderImage() // 🔽 아래에서 설명
confirmDelegates()
removeOutgoingMessageAvatars()
addCameraBarButtonToMessageInputBar()
listenToMessages()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
messagesCollectionView.addGestureRecognizer(tapGesture)
}
3. 사용자 이미지 수집 흐름
getSenderImage()
설명: 채널 내 모든 발신자 UID를 가져온 뒤 각자의 이미지 URL을 가져온다.
1
2
3
4
5
func getSenderImage() {
chatManager.fetchAllSenderIds(channelId: channelId) { [weak self] senderIds in
self?.fetchProfileImages(for: senderIds)
}
}
fetchProfileImages(for:)
설명: senderId 리스트를 받아 각각 반복적으로 이미지 요청
1
2
3
4
5
func fetchProfileImages(for senderIds: [String]) {
for senderId in senderIds {
fetchUserDataAndProfileImage(for: senderId)
}
}
fetchUserDataAndProfileImage(for:)
설명: 해당 UID의 Firebase 데이터베이스에서 닉네임과 프로필 이미지 URL을 가져옴. 가져온 URL은 profileImageUrls
에 저장.
1
2
3
4
5
6
7
8
9
func fetchUserDataAndProfileImage(for uid: String? = nil) {
guard let uid = uid ?? user?.uid else { return }
UserManager().fetchUserData(uid: uid) { [weak self] user in
guard let user = user else { return }
self?.profileImageUrls[uid] = user.profileImageUrl
self?.messagesCollectionView.reloadData()
}
}
fetchDisplayNameAndProfileImage()
설명: 로그인한 본인 계정의 닉네임과 이미지 URL을 불러오는 래퍼 메서드
1
2
3
4
5
func fetchDisplayNameAndProfileImage(completion: @escaping (String?, String?) -> Void) {
fetchUserDataAndProfileImage { user in
completion(user?.nickName, user?.profileImageUrl)
}
}
4. 이미지 사전 로딩 처리
preloadProfileImages(for:)
설명: 메시지를 받아왔을 때, 발신자 목록을 기준으로 이미지가 아직 캐시되지 않은 경우 미리 받아두는 역할
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func preloadProfileImages(for messages: [Message]) {
let senderIds = messages.map { $0.sender.senderId }
let uniqueSenderIds = Set(senderIds)
for senderId in uniqueSenderIds {
if imageCache[senderId] == nil {
if let urlString = profileImageUrls[senderId],
let url = URL(string: urlString) {
KingfisherManager.shared.retrieveImage(with: url) { result in
if case .success(let value) = result {
self.imageCache[senderId] = value.image
self.messagesCollectionView.reloadData()
}
}
}
}
}
}
5. 메시지 셀 구성 (UICollectionViewDataSource)
cellForItemAt
설명: MessagesKit이 메시지 셀을 생성할 때 avatarView
를 구성하기 위해 호출됨
1
2
3
4
5
6
7
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! MessageContentCell
let message = messages[indexPath.section]
configureAvatarView(cell.avatarView, for: message, at: indexPath, in: collectionView as! MessagesCollectionView)
return cell
}
6. MessagesKit Avatar 관련 델리게이트
avatarFor(message:)
설명: 아바타에 표시할 이미지를 제공함. 캐시에 있으면 이미지, 없으면 이니셜만 보여줌.
1
2
3
4
5
6
7
8
9
func avatarFor(message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> Avatar {
let senderId = message.sender.senderId
if let image = imageCache[senderId] {
return Avatar(image: image)
} else {
return Avatar(initials: String(senderId.prefix(1)))
}
}
avatarSize(for:)
설명: 아바타 뷰의 크기 설정
1
2
3
4
func avatarSize(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize {
return CGSize(width: 30, height: 30)
}
configureAvatarView(_:for:at:in:)
설명: 아바타 뷰에 이미지를 적용하는 메서드
profileImageUrls
에서 URL 가져오기- Kingfisher로 다운로드
imageCache
에 저장 후 뷰 업데이트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) {
let senderId = message.sender.senderId
if let cachedImage = imageCache[senderId] {
avatarView.image = cachedImage
return
}
if let urlString = profileImageUrls[senderId],
let url = URL(string: urlString) {
avatarView.kf.setImage(with: url) { result in
if case .success(let value) = result {
self.imageCache[senderId] = value.image
}
}
}
}
정리
파트 | 기능 | 설명 |
---|---|---|
profileImageUrls | senderId → 이미지 URL | 사용자별 이미지 주소 저장 |
imageCache | senderId → UIImage | 다운로드된 이미지 메모리 캐시 |
fetchUserDataAndProfileImage | 사용자 데이터 가져오기 | Firebase에서 nickname + 이미지 URL 획득 |
getSenderImage | 모든 발신자 이미지 요청 시작 | 채널 내 전체 sender들 대상 |
preloadProfileImages | 캐시 없는 경우 이미지 사전 로딩 | 메시지 수신 시 빠른 표시 대비 |
configureAvatarView | 셀에 이미지 적용 | Kingfisher 비동기 다운로드 및 캐시 저장 |
이 구현은 메시지 화면에서 사용자 아바타를 시각적으로 빠르고 정확하게 보여주기 위한 전체 처리 흐름이다.
성능 개선을 위한 캐시 전략, UX를 고려한 비동기 처리, MessagesKit의 델리게이트 활용이 핵심 포인트다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.