🟨 구현 화면

🟨 ProfileViewViewModel.swift
- getFormatterDate() 라는 메서드를 통해 날짜를 원하는 형식으로 변경하여 반환한다.
import Foundation
import Combine
import FirebaseAuth
final class ProfileViewViewModel: ObservableObject {
@Published var user: TwitterUser?
@Published var error: String?
// @Published var tweets: [Tweet] = []
private var subscriptions: Set<AnyCancellable> = []
func retreiveUser() {
guard let id = Auth.auth().currentUser?.uid else { return }
DatabaseManager.shared.collectionUsers(retreive: id)
.handleEvents(receiveOutput: { [weak self] user in
self?.user = user
// self?.fetchTweets()
})
.sink { [weak self] completion in
if case .failure(let error) = completion {
self?.error = error.localizedDescription
}
} receiveValue: { [weak self] user in
self?.user = user
}
.store(in: &subscriptions)
}
// 날짜 갖고오기
func getFormatterDate(with date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM YYYY"
return dateFormatter.string(from: date)
}
}
🟨 HomeViewController.swift
- TweetComposeViewController 로 이동하기 위한 버튼 생성
import UIKit
import FirebaseAuth
import Combine
class HomeViewController: UIViewController {
...
private lazy var composeTweetButton: UIButton = {
let button = UIButton(type: .system, primaryAction: UIAction { _ in
self.navigationToTweetCompose()
})
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .systemMint
button.tintColor = .label
let plussign = UIImage(systemName: "plus", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .bold))
button.setImage(plussign, for: .normal)
button.layer.cornerRadius = 30
button.clipsToBounds = true
return button
}()
private func navigationToTweetCompose() {
let TweetComposeVC = UINavigationController(rootViewController: TweetComposeViewController())
TweetComposeVC.modalPresentationStyle = .fullScreen
present(TweetComposeVC, animated: true)
}
...
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(timelineTableView)
view.addSubview(composeTweetButton)
...
configureConstraints()
}
...
private func configureConstraints() {
let composeTweetButtonConstraints = [
composeTweetButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -25),
composeTweetButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -120),
composeTweetButton.widthAnchor.constraint(equalToConstant: 60),
composeTweetButton.heightAnchor.constraint(equalToConstant: 60)
]
NSLayoutConstraint.activate(composeTweetButtonConstraints)
}
}
...
🟨 TweetComposeViewController.swift
import UIKit
import Combine
class TweetComposeViewController: UIViewController {
private var viewModel = TweetComposeViewViewModel()
private var subscriptions: Set<AnyCancellable> = []
private let tweetButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .systemMint
button.setTitle("Tweet", for: .normal)
button.layer.cornerRadius = 20
button.clipsToBounds = true
button.isEnabled = false
button.titleLabel?.font = .systemFont(ofSize: 18, weight: .semibold)
button.setTitleColor(.white, for: .normal)
button.setTitleColor(.white.withAlphaComponent(0.7), for: .disabled)
return button
}()
private let tweetContentTextView: UITextView = {
let textView = UITextView()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.layer.masksToBounds = true
textView.layer.cornerRadius = 8
textView.textContainerInset = .init(top: 15, left: 15, bottom: 15, right: 15)
textView.text = "What's happening?"
textView.textColor = .gray
textView.font = .systemFont(ofSize: 16)
return textView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
title = "Tweet"
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(didTapToCancel))
tweetContentTextView.delegate = self
view.addSubview(tweetButton)
view.addSubview(tweetContentTextView)
configureConstraints()
bindViews()
tweetButton.addTarget(self, action: #selector(didTapToTweet), for: .touchUpInside)
}
@objc private func didTapToTweet() {
viewModel.dispatchTweet()
}
private func bindViews() {
viewModel.$isValidToTweet.sink { [weak self] state in
self?.tweetButton.isEnabled = state
} .store(in: &subscriptions)
viewModel.$shouldDismissComposer.sink { [weak self] success in
if success {
self?.dismiss(animated: true)
}
}
.store(in: &subscriptions)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
viewModel.getUserData()
}
private func configureConstraints() {
let tweetButtonConstraints = [
tweetButton.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor, constant: -10),
tweetButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
tweetButton.widthAnchor.constraint(equalToConstant: 120),
tweetButton.heightAnchor.constraint(equalToConstant: 40)
]
let tweetContentTextViewConstraints = [
tweetContentTextView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tweetContentTextView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15),
tweetContentTextView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15),
tweetContentTextView.bottomAnchor.constraint(equalTo: tweetButton.topAnchor, constant: -10)
]
NSLayoutConstraint.activate(tweetButtonConstraints)
NSLayoutConstraint.activate(tweetContentTextViewConstraints)
}
@objc private func didTapToCancel() {
dismiss(animated: true)
}
}
extension TweetComposeViewController: UITextViewDelegate {
func textViewDidBeginEditing(_ textView: UITextView) {
if textView.textColor == .gray {
textView.textColor = .label
textView.text = ""
}
}
func textViewDidEndEditing(_ textView: UITextView) {
if textView.text.isEmpty {
textView.text = "What's happening?"
textView.textColor = .gray
}
}
func textViewDidChange(_ textView: UITextView) {
viewModel.tweetContent = textView.text
viewModel.validateToTweet()
}
}
🟨 Tweet.swift
import Foundation
struct Tweet: Codable, Identifiable {
var id = UUID().uuidString
let author: TwitterUser
let authorID: String
let tweetContent: String
var likesCount: Int
var likers: [String]
let isReply: Bool
let parentReference: String?
}
🟨 TweetComposeViewController.swift
import Foundation
import Combine
import FirebaseAuth
final class TweetComposeViewViewModel: ObservableObject {
private var subscriptions: Set<AnyCancellable> = []
@Published var isValidToTweet: Bool = false
@Published var error: String = ""
@Published var shouldDismissComposer: Bool = false
var tweetContent: String = ""
private var user: TwitterUser?
func getUserData() {
guard let userID = Auth.auth().currentUser?.uid else { return }
DatabaseManager.shared.collectionUsers(retreive: userID)
.sink { [weak self] completion in
if case .failure(let error) = completion {
self?.error = error.localizedDescription
}
} receiveValue: { [weak self] twitterUser in
self?.user = twitterUser
}
.store(in: &subscriptions)
}
func validateToTweet() {
isValidToTweet = !tweetContent.isEmpty
}
func dispatchTweet() {
guard let user = user else { return }
let tweet = Tweet(author: user, authorID: user.id, tweetContent: tweetContent, likesCount: 0, likers: [], isReply: false, parentReference: nil)
DatabaseManager.shared.collectionTweets(dispatch: tweet)
.sink { [weak self] completion in
if case .failure(let error) = completion {
self?.error = error.localizedDescription
}
} receiveValue: { [weak self] state in
self?.shouldDismissComposer = state
}
.store(in: &subscriptions)
}
}
'Clone App > Twitter' 카테고리의 다른 글
[Twitter Clone] Add Search Bar (0) | 2024.06.14 |
---|---|
[Twitter Clone] Show Tweet in Home View (0) | 2024.06.12 |
[Twitter Clone] Load Profile Data to profileView (0) | 2024.06.12 |
[Twitter Clone] Connect to Firebase Storage (0) | 2024.06.12 |
[Twitter Clone] Add ProfileDataFormView (0) | 2024.06.11 |