Skip to content
Open
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
21 changes: 9 additions & 12 deletions Topical Dictionary/Controller/AccountViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,31 @@
import UIKit
import FirebaseAuth

class AccountViewController: UIViewController, AccountViewModelDelegate {
class AccountViewController: BaseViewController, AccountViewModelDelegate {

@IBOutlet weak var accountTableView: UITableView!

var viewModel: AccountViewModel?
private let authService = AuthService.shared

override func viewDidLoad() {
super.viewDidLoad()

viewModel = AccountViewModel(delegate: self)

accountTableView.dataSource = self
accountTableView.delegate = self

}


override func configureViewModel() {
viewModel = AccountViewModel(delegate: self)
baseViewModel = viewModel
}

func logout() {
let alert = UIAlertController(title: "Loging out", message: "You are currently logging out. Do you want to continue?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Logout", style: .cancel, handler: { _ in
let auth = Auth.auth()
do {
try auth.signOut()
try authService.signOut()
} catch let signoutError as NSError {
print("Error while signing out: \(signoutError)")
}
Expand All @@ -53,12 +56,6 @@ class AccountViewController: UIViewController, AccountViewModelDelegate {

// MARK: - View Model Delegate

func didErrorOccured(_ viewModel: AccountViewModel, error: Error) {
let alert = UIAlertController(title: "Something Went Wrong", message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}

func didUpdateName(_ viewModel: AccountViewModel, name: String) {
DispatchQueue.main.async {
self.accountTableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .fade)
Expand Down
40 changes: 40 additions & 0 deletions Topical Dictionary/Controller/BaseViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// BaseViewController.swift
// Topical Dictionary
//
// Created by İbrahim Ethem Karalı on 2025-02-14.
//

import UIKit

class BaseViewController: UIViewController, BaseViewModelDelegate {
var baseViewModel: BaseViewModelProtocol? {
didSet {
baseViewModel?.delegate = self
}
}

override func viewDidLoad() {
super.viewDidLoad()
configureViewModel()
bindViewModel()
}

func configureViewModel() {
// Override in subclasses to create view model.
}

func bindViewModel() {
// Override in subclasses to bind view model outputs.
}

func didReceiveError(_ error: Error) {
presentError(error)
}

func presentError(_ error: Error) {
let alert = UIAlertController(title: "Something Went Wrong", message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
}
9 changes: 8 additions & 1 deletion Topical Dictionary/Controller/DictionaryViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import UIKit
import FirebaseCore
import FirebaseFirestore

class DictionaryViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, WordManagerDelegate, HeadCellDelegate, WordDetailViewDelegate, DictionaryServiceDelegate {
class DictionaryViewController: BaseViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, WordManagerDelegate, HeadCellDelegate, WordDetailViewDelegate, DictionaryServiceDelegate {

enum sections: Int {
case head = 0
Expand All @@ -21,6 +21,8 @@ class DictionaryViewController: UIViewController, UITableViewDelegate, UITableVi

@IBOutlet var wordsTableView: UITableView!
@IBOutlet weak var favoriteButton: UIBarButtonItem!

var viewModel: DictionaryViewModel?

lazy var selectedDictionary = DictionaryModel()
var searchedWord: WordData?
Expand Down Expand Up @@ -53,6 +55,11 @@ class DictionaryViewController: UIViewController, UITableViewDelegate, UITableVi
setFavoriteImage(isFavorite, favoriteButton)

}

override func configureViewModel() {
viewModel = DictionaryViewModel()
baseViewModel = viewModel
}

private func fireBaseSettings() {
let settings = FirestoreSettings()
Expand Down
9 changes: 8 additions & 1 deletion Topical Dictionary/Controller/HomeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ enum SortingType {
case zToA
}

class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, DictionariesManagerDelegate {
class HomeViewController: BaseViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, DictionariesManagerDelegate {

// MARK: Implementations
var db: Firestore!

@IBOutlet var mainTableView: UITableView!
@IBOutlet var favButton: UIBarButtonItem!

var viewModel: HomeViewModel?

private var isFav = false
private var stockRightBarItems: [UIBarButtonItem]?
Expand Down Expand Up @@ -68,6 +70,11 @@ class HomeViewController: UIViewController, UITableViewDelegate, UITableViewData

dictionaryManager.setListener()
}

override func configureViewModel() {
viewModel = HomeViewModel()
baseViewModel = viewModel
}

// MARK: - Bar Button Actions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
//

import UIKit
import FirebaseAuth

class EmailLoginViewController: UIViewController, UITextFieldDelegate {
class EmailLoginViewController: BaseViewController, UITextFieldDelegate {

@IBOutlet weak var loginOrRegisterSegment: UISegmentedControl!

Expand All @@ -23,6 +22,8 @@ class EmailLoginViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var fullnameTextField: UITextField!

@IBOutlet weak var privacyPolicy: UILabel! // add real terms and conditions

var viewModel: EmailLoginViewModel?

override func viewDidLoad() {
super.viewDidLoad()
Expand All @@ -46,6 +47,11 @@ class EmailLoginViewController: UIViewController, UITextFieldDelegate {
privacyPolicy.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapPrivacyPolicy)))

}

override func configureViewModel() {
viewModel = EmailLoginViewModel()
baseViewModel = viewModel
}

@objc func didTapPrivacyPolicy() {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PrivacyViewController")
Expand All @@ -66,6 +72,12 @@ class EmailLoginViewController: UIViewController, UITextFieldDelegate {
self.myActivityIndicator?.endActivity()
}
}

override func didReceiveError(_ error: Error) {
myActivityIndicator?.endActivity()
view.isUserInteractionEnabled = true
super.didReceiveError(error)
}

@IBAction func loginOrRegister(_ sender: UISegmentedControl) {
view.endEditing(true)
Expand Down Expand Up @@ -96,41 +108,24 @@ class EmailLoginViewController: UIViewController, UITextFieldDelegate {
if loginOrRegisterSegment.selectedSegmentIndex == 0 {
// Login
view.isUserInteractionEnabled = false
Auth.auth().signIn(withEmail: emailTextField.text!, password: passwordTextField.text!) { (authDataResult, error) in
if error != nil {
self.displayAlert(detail: error!.localizedDescription, title: "Something Went Wrong")
self.view.isUserInteractionEnabled = true

return
}

viewModel?.login(email: emailTextField.text ?? "", password: passwordTextField.text ?? "") { authDataResult in
if let authDataRes = authDataResult {
print("user signed in: UID: \(authDataRes.user.uid)")
print("is email verified: \(authDataRes.user.isEmailVerified)")
self.myActivityIndicator?.endActivity()
}
self.myActivityIndicator?.endActivity()
self.view.isUserInteractionEnabled = true
}

} else {
if let password = passwordTextField.text,
password == confirmPasswordTextField.text,
let email = emailTextField.text, let name = fullnameTextField.text {
Auth.auth().createUser(withEmail: email, password: password) { authDataResult, error in
if let err = error {
self.displayAlert(detail: err.localizedDescription, title: "Something Went Wrong")
return
viewModel?.register(email: email, password: password, displayName: name) { authDataResult in
if let authDataRes = authDataResult {
print("user signed up: UID: \(authDataRes.user.uid)")
}
self.myActivityIndicator?.endActivity()
let changeRequest = authDataResult?.user.createProfileChangeRequest()
changeRequest?.displayName = name
changeRequest?.commitChanges(completion: { error in
if error != nil {
print("Error while setting display name: \(error!.localizedDescription)")
} else {
print("success")
}
})
}
} else {
print("Passwords doesn't match")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import CryptoKit
import AuthenticationServices
import NVActivityIndicatorView

class LoginViewController: UIViewController, UIScrollViewDelegate, GIDSignInDelegate {
class LoginViewController: BaseViewController, UIScrollViewDelegate, GIDSignInDelegate {

fileprivate var currentNonce: String?
fileprivate var myActivityIndicator: MyActivityIndicator?
Expand All @@ -29,7 +29,9 @@ class LoginViewController: UIViewController, UIScrollViewDelegate, GIDSignInDele
@IBOutlet weak var facebookButton: UIButton!
@IBOutlet weak var appleButton: UIButton!
@IBOutlet weak var googleButton: UIButton!


var viewModel: LoginViewModel?

let fbLoginManager = LoginManager()

@IBOutlet weak var scrollView: UIScrollView!
Expand All @@ -56,6 +58,11 @@ class LoginViewController: UIViewController, UIScrollViewDelegate, GIDSignInDele
GIDSignIn.sharedInstance()?.delegate = self

}

override func configureViewModel() {
viewModel = LoginViewModel()
baseViewModel = viewModel
}

// MARK: Facebook login

Expand Down
76 changes: 76 additions & 0 deletions Topical Dictionary/Service/AuthService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// AuthService.swift
// Topical Dictionary
//
// Created by İbrahim Ethem Karalı on 2025-02-14.
//

import Foundation
import FirebaseAuth

enum AuthServiceError: Error {
case missingUser
}

final class AuthService {
static let shared = AuthService()

private let auth: Auth

init(auth: Auth = .auth()) {
self.auth = auth
}

var currentUser: User? {
auth.currentUser
}

func loginMethod(for user: User?) -> AuthProvider? {
let providerID = user?.providerData.first?.providerID
switch providerID {
case AuthProvider.email.rawValue:
return AuthProvider.email
case AuthProvider.facebook.rawValue:
return AuthProvider.facebook
case AuthProvider.google.rawValue:
return AuthProvider.google
case AuthProvider.apple.rawValue:
return AuthProvider.apple
default:
return nil
}
}

func signOut() throws {
try auth.signOut()
}

func signIn(email: String, password: String, completion: @escaping (AuthDataResult?, Error?) -> Void) {
auth.signIn(withEmail: email, password: password, completion: completion)
}

func register(email: String, password: String, displayName: String, completion: @escaping (AuthDataResult?, Error?) -> Void) {
auth.createUser(withEmail: email, password: password) { authDataResult, error in
if let error = error {
completion(authDataResult, error)
return
}
guard let _ = authDataResult?.user else {
completion(authDataResult, AuthServiceError.missingUser)
return
}
self.updateDisplayName(displayName) { updateError in
completion(authDataResult, updateError)
}
}
}

func updateDisplayName(_ name: String, completion: @escaping (Error?) -> Void) {
guard let change = auth.currentUser?.createProfileChangeRequest() else {
completion(AuthServiceError.missingUser)
return
}
change.displayName = name
change.commitChanges(completion: completion)
}
}
Loading