🟨 구현 화면


🟨 ProfileDataFormViewViewModel.swift
import Foundation
import Combine
import UIKit
import FirebaseAuth
import FirebaseStorage
final class ProfileDataFormViewViewModel: ObservableObject {
private var subscriptions: Set<AnyCancellable> = []
@Published var displayName: String?
@Published var username: String?
@Published var bio: String?
@Published var avatarPath: String?
@Published var imageData: UIImage?
@Published var isFormValid: Bool = false
@Published var error: String = ""
@Published var isOnboardingFinished: Bool = false
@Published var url: URL?
// user 정보를 적는데 필요한 간단한 규칙 정도
func validateUserProfileForm() {
guard let displayName = displayName,
displayName.count > 2,
let username = username,
username.count > 2,
let bio = bio,
bio.count > 2,
imageData != nil else {
isFormValid = false
return
}
isFormValid = true
}
func uploadAvatar() {
let randomID = UUID().uuidString
guard let imageData = imageData?.jpegData(compressionQuality: 0.5) else { return }
let metaData = StorageMetadata()
metaData.contentType = "image/jpeg"
StorageManager.shared.uploadProfilePhoto(with: randomID, image: imageData, metaData: metaData)
.flatMap({ metaData in
StorageManager.shared.getDownloadURL(for: metaData.path)
})
.sink { [weak self] completion in
if case .failure(let error) = completion {
self?.error = error.localizedDescription
}
} receiveValue: { [weak self] url in
self?.url = url
}
.store(in: &subscriptions)
}
}
🟨 StorageManager.swift
import Foundation
import Combine
import FirebaseStorageCombineSwift
import FirebaseStorage
enum FirestorageError: Error {
case invalidImageID
}
final class StorageManager {
static let shared = StorageManager()
let storage = Storage.storage()
func getDownloadURL(for id: String?) -> AnyPublisher<URL, Error> {
guard let id = id else {
return Fail(error: FirestorageError.invalidImageID)
.eraseToAnyPublisher()
}
return storage
.reference(withPath: id)
.downloadURL()
.print()
.eraseToAnyPublisher()
}
func uploadProfilePhoto(with randomID: String, image: Data, metaData: StorageMetadata) -> AnyPublisher<StorageMetadata, Error> {
return storage
.reference()
.child("images/\(randomID).jpg")
.putData(image, metadata: metaData)
.print()
.eraseToAnyPublisher()
}
}
🟨 ProfileDataFormViewController.swift
import UIKit
import PhotosUI
import Combine
class ProfileDataFormViewController: UIViewController {
private let viewModel = ProfileDataFormViewViewModel()
private var subscriptions: Set<AnyCancellable> = []
...
override func viewDidLoad() {
super.viewDidLoad()
...
submitButton.addTarget(self, action: #selector(didTapSubmit), for: .touchUpInside)
bindViews()
}
@objc private func didTapSubmit() {
viewModel.uploadAvatar()
}
@objc private func didUpdateDisplayName() {
viewModel.displayName = displayNameTextField.text
viewModel.validateUserProfileForm()
}
@objc private func didUpdateUsername() {
viewModel.username = usernameTextField.text
viewModel.validateUserProfileForm()
}
private func bindViews() {
displayNameTextField.addTarget(self, action: #selector(didUpdateDisplayName), for: .editingChanged)
usernameTextField.addTarget(self, action: #selector(didUpdateUsername), for: .editingChanged)
viewModel.$isFormValid.sink { [weak self] buttonState in
self?.submitButton.isEnabled = buttonState
}
.store(in: &subscriptions)
viewModel.$isOnboardingFinished.sink { [weak self] success in
if success {
self?.dismiss(animated: true)
}
}
.store(in: &subscriptions)
}
...
}
extension ProfileDataFormViewController: UITextViewDelegate, UITextFieldDelegate {
func textViewDidBeginEditing(_ textView: UITextView) {
scrollView.setContentOffset(CGPoint(x: 0, y: textView.frame.origin.y - 100), animated: true)
if textView.textColor == .gray {
textView.textColor = .label
textView.text = ""
}
}
func textViewDidEndEditing(_ textView: UITextView) {
scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
if textView.text.isEmpty {
textView.text = "Tell the world about yourself"
textView.textColor = .gray
}
}
// 텍스트 뷰에 작성한 값 델리게이트로 갖고오기
func textViewDidChange(_ textView: UITextView) {
viewModel.bio = textView.text
viewModel.validateUserProfileForm()
}
...
}
extension ProfileDataFormViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
for result in results {
result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] object, error in
if let image = object as? UIImage {
DispatchQueue.main.async {
self?.avatarPlaceholderImageView.image = image
self?.viewModel.imageData = image
self?.viewModel.validateUserProfileForm()
}
}
}
}
}
}
'Clone App > Twitter' 카테고리의 다른 글
[Twitter Clone] Connect to profileView (0) | 2024.06.12 |
---|---|
[Twitter Clone] Load Profile Data to profileView (0) | 2024.06.12 |
[Twitter Clone] Add ProfileDataFormView (0) | 2024.06.11 |
[Twitter Clone] Set user Info into firebase storage (1) | 2024.06.09 |
[Twitter Clone] Add signOut, Login View, Error View (0) | 2024.06.06 |