Issue with signup page using clean swift architecture in Xcode - swift

I am currently implementing a signup feature into a chat application I am working on. In my 'SignupViewController' I want to implement a function named 'signupButtonPressed' which routes me from a signup viewcontroller to a 'ListContacts' viewcontroller. If the signup fails, then a function called 'showValidationError' will execute. Code excerpt from my SignupViewController below:
#IBAction func signupButtonPressed(_ sender: Any) {
let request = Signup.Request(
name: fullNameTextField.text!,
email: emailTextField.text!,
password: passwordTextField.text!
)
interactor?.createAccount(request: request)
}
func showValidationError(_ message: String) {
let alertCtrl = UIAlertController(title: "Oops! An error occurred", message: message, preferredStyle: .alert)
alertCtrl.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: nil))
self.show(alertCtrl, sender: self)
}
I am using Swift Clean Architecture, so I will link the code to my Signup Router, Model, and Interactor files also:
1) Signupinteractor.swift:
import Foundation
protocol SignupBusinessLogic {
func createAccount(request: Signup.Request)
}
class SignupInteractor: SignupBusinessLogic {
var viewController: SignupFormErrorLogic?
var router: (NSObjectProtocol & SignupRoutingLogic)?
var worker = UsersWorker()
func createAccount(request: Signup.Request) -> Void {
self.worker.signup(request: request) { user, error in
guard error == nil else {
print(error!)
self.viewController?.showValidationError("Error creating account!")
return
}
self.router?.routeToListContacts()
}
}
}
2) SignupModels.swift:
import Foundation
enum Signup {
struct Request {
var name: String
var email: String
var password: String
}
struct Response {
var user: User?
init(data: [String:Any]) {
self.user = User(
id: data["id"] as! Int,
name: data["name"] as! String,
email: data["email"] as! String,
chatkit_id: data["chatkit_id"] as! String
)
}
}
}
3) SignupRouter.swift:
import UIKit
#objc protocol SignupRoutingLogic {
func routeToListContacts()
}
class SignupRouter: NSObject, SignupRoutingLogic {
weak var viewController: SignupViewController?
func routeToListContacts() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationVC = storyboard.instantiateViewController(withIdentifier: "MainNavigator") as! UINavigationController
viewController!.show(destinationVC, sender: nil)
}
}
The signup function in my UsersWorker.swift file:
func signup(request: Signup.Request, completionHandler: #escaping (User?, UsersStoreError?) -> Void) {
let params: Parameters = [
"name": request.name,
"email": request.email,
"password": request.password
]
postRequest("/api/users/signup", params: params, headers: nil) { data in
guard data != nil else {
return completionHandler(nil, UsersStoreError.CannotSignup)
}
let response = Signup.Response(data: data!)
CurrentUserIDDataStore().setID(CurrentUserID(id: response.user?.chatkit_id))
let request = Login.Account.Request(
email: request.email,
password: request.password
)
self.login(request: request) { token, error in
guard error == nil else {
return completionHandler(nil, UsersStoreError.CannotLogin)
}
DispatchQueue.main.async {
completionHandler(response.user, nil)
}
}
}
}
When I enter signup details into the signup UITextfields (fullNameTextField; emailTextField; passwordTextField) and press the signup button, an error called 'CannotSignup' triggers. Unsure why however. This case can also be found in my UsersWorker.swift file:
enum UsersStoreError: Error {
case CannotLogin
case CannotSignup
case CannotFetchChatkitToken
}
Would be great if anyone is able to look over the code to get an idea for what the issue might be, and how I might resolve it? If any further info is required just ask!

Most probably the API call is failing, Please make a check on return HTTP status code instead of data. In some cases the API call can be success without any response data
Ideally send an Error instance as well along with data back from postRequest method

Related

Can someone explain what "extra trailing closure passed in call" means?

I'm receiving the error "Extra trailing closure passed in call" on the authViewModel.fetchUser() function. From what I've gathered researching online this means that fetchuser can't have the trailing closure (the brackets), I am confused about what in my fetchuser function says that it cannot have the {} after the call. Or maybe I'm not understanding the error at all. Thank you in advance!
FeedCellViewModel
import Foundation
class FeedCellViewModel: ObservableObject {
#Published var posts = [Post]()
let service = PostService()
let authViewModel = AuthViewModel()
init() {
fetchPosts()
}
func fetchPosts() {
service.fetchPosts { posts in
self.posts = posts
for i in 0 ..< posts.count {
let uid = posts[i].uid
self.authViewModel.fetchUser() { user in
self.posts[i].user = user
}
}
}
}
}
AuthViewModel
import SwiftUI
import FirebaseAuth
import FirebaseCore
import FirebaseStorage
import FirebaseFirestore
import FirebaseFirestoreSwift
class AuthViewModel: NSObject, ObservableObject {
#Published var userSession: FirebaseAuth.User?
#Published var currentUser: User?
#Published var selectedImage: UIImage?
#Published var didAuthenticateUser = false
private var tempUserSession: FirebaseAuth.User?
private let service = UserService()
static let shared = AuthViewModel()
override init() {
super.init()
userSession = Auth.auth().currentUser
fetchUser()
}
func login(withEmail email: String, password: String) {
Auth.auth().signIn(withEmail: email, password: password) { result, error in
if let error = error {
print("DEBUG: Failed to sign in with error \(error.localizedDescription)")
return
}
self.userSession = result?.user
self.fetchUser()
}
}
func register(withEmail email: String, password: String, fullname: String) {
Auth.auth().createUser(withEmail: email, password: password) { result, error in
if let error = error {
print("DEBUG: Failed to register with error \(error.localizedDescription)")
return
}
guard let user = result?.user else { return }
self.tempUserSession = user
let data = ["email": email,
"fullname": fullname,
"uid": user.uid]
COLLECTION_USERS
.document(user.uid)
.setData(data) { _ in
self.didAuthenticateUser = true
}
self.uploadProfileImage(self.selectedImage)
self.fetchUser()
}
}
func signOut() {
// sets user session to nil so we show login view
self.userSession = nil
// signs user out on server
try? Auth.auth().signOut()
}
func uploadProfileImage(_ image: UIImage?) {
guard let uid = tempUserSession?.uid else { return }
ImageUploader.uploadImage(image: image) { profileImageUrl in
Firestore.firestore().collection("users")
.document(uid)
.updateData(["profileImageUrl": profileImageUrl]) { _ in
self.userSession = self.tempUserSession
}
}
}
func fetchUser() {
guard let uid = userSession?.uid else { return }
COLLECTION_USERS.document(uid).getDocument { snapshot, _ in
guard let user = try? snapshot?.data(as: User.self) else { return }
self.currentUser = user
}
}
}
You're seeing this error because your fetchUser() function doesn't take a closure parameter (or any parameters for that matter).
A trailing closure is just a nicer way of passing a closure as a parameter, given it's the last parameter to a method.
Try running this example in a playground to get a feel for this:
func hello(closure: () -> Void) {
print("calling closure")
closure()
print("finished")
}
// these are the same
hello(closure: { print("hello!!") })
hello { print("hello!!") }
If you want to provide the user in a closure to the caller, return the user as a parameter to the closure in addition to setting the currentUser.
func fetchUser(finishedFetching: #escaping (User) -> Void) {
guard let uid = userSession?.uid else { return }
COLLECTION_USERS.document(uid).getDocument { snapshot, _ in
guard let user = try? snapshot?.data(as: User.self) else { return }
self.currentUser = user
finishedFetching(user)
}
}
Read more about closures in the Swift language guide.
They are essentially unnamed functions that you can store and pass around.
You'll learn there why I marked our closure as #escaping.

Swift - Refactoring code. Closure and inout?

I have a class thats used to help manage the process of users sending emails with MFMailComposeViewControllerDelegate.
The code is rather long, and I'm using it inside of almost all of my ViewControllers. And I figure I should be able to add it as an extension to UIVIewController. And so I'm currently trying to do that, but at a loss of how to do it correctly.
Working Code:
Struct:
struct Feedback {
let recipients = [R.App.Contact.email] // [String]
let subject: String
let body: String
let footer: String
}
Class:
final class FeedbackManager: NSObject, MFMailComposeViewControllerDelegate {
private var feedback: Feedback
private var completion: ((Result<MFMailComposeResult,Error>)->Void)?
override init() {
fatalError("Use FeedbackManager(feedback:)")
}
init?(feedback: Feedback) {
print("init()")
guard MFMailComposeViewController.canSendMail() else {
return nil
}
self.feedback = feedback
}
func send(on viewController: UIViewController, completion:(#escaping(Result<MFMailComposeResult,Error>)->Void)) {
print("send()")
var appVersion = ""
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
appVersion = version
}
let mailVC = MFMailComposeViewController()
self.completion = completion
mailVC.mailComposeDelegate = self
mailVC.setToRecipients(feedback.recipients)
mailVC.setSubject(feedback.subject)
mailVC.setMessageBody("<p>\(feedback.body)<br><br><br><br><br>\(feedback.footer)<br>Potfolio: (\(appVersion))<br>\(UIDevice.modelName) (\(UIDevice.current.systemVersion))</p>", isHTML: true)
DispatchQueue.main.async {
viewController.present(mailVC, animated:true)
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
print("mailComposeController()")
if let error = error {
completion?(.failure(error))
controller.dismiss(animated: true)
} else {
completion?(.success(result))
controller.dismiss(animated: true)
}
}
}
Implementation:
var feedbackManager: FeedbackManager?
func sendEmail() {
let feedback = Feedback(subject: "subject", body: "body", footer: "footer")
if let manager = FeedbackManager(feedback: feedback) {
self.feedbackManager = manager
self.feedbackManager?.send(on: self) { [weak self] result in
switch result {
case .failure(let error):
print("error: ", error.localizedDescription)
case .success(_):
print("Success")
}
self?.feedbackManager = nil
}
} else { // Cant Send Email:
let failedMenu = UIAlertController(title: "Please email " + R.App.Contact.email, message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
DispatchQueue.main.async {
self.present(failedMenu, animated: true)
}
}
}
My attempt to refractor:
import UIKit
extension UIViewController {
func refactoredEmail(on viewController: UIViewController, with feedback: Feedback, manager: inout FeedbackManager?) -> FeedbackManager? {
guard let letManager = FeedbackManager(feedback: feedback) else {
let failedMenu = UIAlertController(title: "Please email " + R.App.Contact.email, message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
DispatchQueue.main.async {
viewController.present(failedMenu, animated: true)
}
return nil
}
manager = letManager
manager?.send(on: self) { [weak manager] result in
switch result {
case .failure(let error):
print("error: ", error.localizedDescription)
case .success(_):
print("Success")
}
manager = nil
}
return letManager
}
}
Implementation:
var feedbackManager: FeedbackManager?
func sendEmail() {
let feedback = Feedback(subject: "subject", body: "body", footer: "footer")
refactoredEmail(on: self, with: feedback, manager: &feedbackManager)
}
FeedbackManager Class is the same as above. --
As is, this is functional, but, refactoredEmail uses feedbackManager as an inout parameter. And feedbackManager is not returned to nil after completion.
I'm not entirely sure how bad (or not) it is to use inout. And I don't fully understand closures. However, my understanding is that the FeedbackManager Class must be a class because it subclasses MFMailComposeViewControllerDelegate. And because of this, each ViewController needs it own reference to the class. Which is where var feedbackManager: FeedbackManager? comes in.
feedbackManager can then run the code inside the FeedbackManager Class. Which presents the MFMailComposeViewController on top of the ViewController that feedbackManager is in. But since now refactoredEmail() is an extension of UIViewController, my guess is the closure is capturing incorrectly?
In conclusion: My goal is to refractor the code so that implementing it in each view controller can hopefully be cut down in comparison with the first implementation code block.
Also, how bad is it to use inout?
I'm not sure why manager is not nil after completion, but I can show you other way of implementation.
You could use associated objects:
protocol FeedbackShowable: class {
func giveFeedback(with feedback: Feedback)
}
extension UIViewController: FeedbackShowable {
private enum AssociatedObjectKeys {
static var feedbackManager: String = "feedbackManager"
}
private var feedbackManager: FeedbackManager? {
get {
return objc_getAssociatedObject(self, &AssociatedObjectKeys.feedbackManager) as? FeedbackManager
}
set {
objc_setAssociatedObject(self,
&AssociatedObjectKeys.feedbackManager,
newValue as FeedbackManager?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
func giveFeedback(with feedback: Feedback) {
let feedbackManager = FeedbackManager(feedback: feedback)
self.feedbackManager = feedbackManager
feedbackManager.send(on: self) { [weak self] result in
self?.feedbackManager = nil
}
}
}

Cannot convert value of type `AuthResultCallback`?

I know about other similar questions, but they are to do with Auth.auth().signIn and signUp respectively, where there is a completion handler argument present within the function.
I'm trying to include a sign in the anonymous function to my SessionStore class so that my app observes the state of the user, i.e., whether he is signed in (anonymously or otherwise) or signed out, and accordingly display the relevant view.
However, when trying to add anonymous sign up to my SessionStore class, I get the following error:
Cannot convert value of type 'AuthResultCallback' (aka '(Optional<User>, Optional<Error>) -> ()') to expected argument type 'AuthDataResultCallback?' (aka 'Optional<(Optional<AuthDataResult>, Optional<Error>) -> ()>')
My code is as follows:
class SessionStore: ObservableObject {
var didChange = PassthroughSubject<SessionStore, Never>()
#Published var session: User? {didSet {self.didChange.send(self)}}
var handle: AuthStateDidChangeListenerHandle?
func listen() {
handle = Auth.auth().addStateDidChangeListener({ (auth, user) in
if let user = user {
self.session = User(uid: user.uid, email: user.email)
} else {
self.session = nil
}
})
}
func signUp(email: String, password: String, handler: #escaping AuthDataResultCallback) {
Auth.auth().createUser(withEmail: email, password: password, completion: handler)
}
func signIn(email: String, password: String, handler: #escaping AuthDataResultCallback) {
Auth.auth().signIn(withEmail: email, password: password, completion: handler)
}
func signOut() {
do {
try Auth.auth().signOut()
self.session = nil
} catch {
print("Error signing user out")
}
}
func signUpAnonymously(handler: #escaping AuthResultCallback) {
Auth.auth().signInAnonymously(completion: handler) // **ERROR APPEARS HERE**
}
func unbind() {
if let handle = handle {
Auth.auth().removeStateDidChangeListener(handle)
}
}
deinit {
unbind()
}
}
struct User {
var uid: String
var email: String?
init(uid: String, email: String?) {
self.uid = uid
self.email = email
}
}
Can someone tell me how to fix this error?
The code snippet you pasted is from an outdated tutorial. As of Xcode 11 beta 5, you no longer need to use PassthroughSubject to send notifications about property changes. Sarun's blog post has a great and concise section on what has changed, I encourage you to read it.
Please also note that you don't need to define your own User class if you only care about the user's ID and their email address, as these attributes are already defined on Firebase's own User type (by way of implementing the UserInfo protocol).
Here is a snippet that shows how to implement the callback you're interested in (see registerStateListener).
class AuthenticationService: ObservableObject {
#Published var user: User?
private var handle: AuthStateDidChangeListenerHandle?
init() {
registerStateListener()
}
func signIn() {
if Auth.auth().currentUser == nil {
Auth.auth().signInAnonymously()
}
}
func signOut() {
do {
try Auth.auth().signOut()
}
catch {
print("Error when trying to sign out: \(error.localizedDescription)")
}
}
func updateDisplayName(displayName: String, completionHandler: #escaping (Result<User, Error>) -> Void) {
if let user = Auth.auth().currentUser {
let changeRequest = user.createProfileChangeRequest()
changeRequest.displayName = displayName
changeRequest.commitChanges { error in
if let error = error {
completionHandler(.failure(error))
}
else {
if let updatedUser = Auth.auth().currentUser {
print("Successfully updated display name for user [\(user.uid)] to [\(updatedUser.displayName ?? "(empty)")]")
// force update the local user to trigger the publisher
self.user = updatedUser
completionHandler(.success(updatedUser))
}
}
}
}
}
private func registerStateListener() {
if let handle = handle {
Auth.auth().removeStateDidChangeListener(handle)
}
self.handle = Auth.auth().addStateDidChangeListener { (auth, user) in
print("Sign in state has changed.")
self.user = user
if let user = user {
let anonymous = user.isAnonymous ? "anonymously " : ""
print("User signed in \(anonymous)with user ID \(user.uid). Email: \(user.email ?? "(empty)"), display name: [\(user.displayName ?? "(empty)")]")
}
else {
print("User signed out.")
self.signIn()
}
}
}
}
If you're interested in more background, check out my article series about building a to-do app with SwiftUI and Firebase: part 1 (building the UI using SwiftUI), part 2 (storing data in Firestore and using Firebase Anonymous Auth), part 3 (Sign in with Apple)

How to perform segue ONLY if token not nil swift 5

As the title is saying, im working on a login. The tokenHandler is already working and im using a KeychainAccess.
Here my tokenHandler class:
import KeychainAccess
class TokenHandler {
func saveUsernameToKeyChain(username: String) {
do {
try keychain.set(username, key: "myUsername")
} catch let error {
print(error)
}
}
func getUsernameFromKeyChain() -> String? {
return keychain[string: "myUsername" ]
}
func saveUserPasswordToKeyChain(password: String) {
do {
try keychain.set(password, key: "UserPassword")
} catch let error {
print(error)
}
}
func getUserPasswordFromKeyChain() -> String? {
return keychain[string: "UserPassword"]
}
let keychain = Keychain(service: "com.mybackendpage")
func getTokenFromKeyChain() -> String? {
return keychain[string: "myToken"]
}
func saveTokenToKeyChain(token: String) {
do {
try keychain.set(token, key: "myToken")
}
catch let error {
print(error)
}
}
func saveRefreshTokenToKeyChain(refreshToken: String) {
do {
try keychain.set(refreshToken, key: "myRefreshToken")
}
catch let error {
print(error)
}
}
func loginToAPI(username: String, password: String) -> Any {
guard let url = URL(string: "https:mypage.com") else
{
return ""
}
let jsonData = try? JSONSerialization.data(withJSONObject: [
"email": username,
"password": password
])
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// insert json data to the request
request.httpBody = jsonData
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else { print(error!.localizedDescription); return }
guard let data = data else { print("Empty data"); return }
if let str = String(data: data, encoding: .utf8) {
print(str)
}
}.resume()
return "TOKENSTRING"
}
}
And here my LoginVC class:
class LoginViewController: UIViewController {
let tokenHandler = TokenHandler()
#IBOutlet weak var usernameTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
let username = tokenHandler.getUsernameFromKeyChain()
let userPassword = tokenHandler.getUserPasswordFromKeyChain()
#IBAction func unwindToLogin(_ unwindSegue: UIStoryboardSegue) {
print("-unwinding success-")
}
// func for the login button:
#IBAction func loginButton(_ sender: UIButton) {
activityIndicator.startAnimating()
loginWithCredentials()
let token = tokenHandler.getTokenFromKeyChain()
if token != nil {
performSegue(withIdentifier: "segueToNavigation", sender: self)
} else if ( token == nil ){
// create the alert:
let alert = UIAlertController(title: "Wrong login data", message: "Please try again.", preferredStyle: UIAlertController.Style.alert)
// add an action to the button:
alert.addAction(UIAlertAction( title: "Ok", style: UIAlertAction.Style.default, handler: nil ))
// show the alert:
self.presentingViewController
print("-Token could not be created.-")
}
else {
// create the alert:
let alert = UIAlertController(title: "Wrong login data", message: "Please try again.", preferredStyle: UIAlertController.Style.alert)
// add an action to the button:
alert.addAction(UIAlertAction( title: "Ok", style: UIAlertAction.Style.default, handler: nil ))
// show the alert:
self.presentingViewController
print("-Token could not be created.-")
}
}
func loginWithCredentials() {
let username: String = usernameTextField.text!
let password: String = passwordTextField.text!
let authResponse = tokenHandler.loginToAPI(username: username, password: password)
}
}
Im still not skilled swift programmer, so I will be happy if any of you could give me some good advices. I was reading and trying to work with the delegate principle, but frankly, my guts are telling me, that this is not what I need.
I was reading about
PerformSegueWithIdentifier
but not really understand how to transform it into my code...
The segues which I have included storyboardwise are working, but unfortunately also, if the test user didn't do the login. So, im pressing the login button w/o any username and userpwd and im getting anyway to the next View. Not cool, so help me please :)
EDIT: I changed performSegue to shouldPerformSegue but im still getting access to the next View w/o any permission.
EDIT: Im getting:
-Token could not be created.-
{"message":"The given data was invalid.","errors":{"email":["The email field is required."],"password":["The password field is required."]}}
So the error is correct, but by pressing on the "Login" Button im still getting to the next View.
EDIT:
Ive tried a few changes, now I have for eg:
if tokenHandler.getTokenFromKeyChain() != nil
instead of
let token = tokenHandler.getTokenFromKeyChain()
if token != nil
Apparently, nothing what im doing in this IBAction for the LoginButton does anything different. What am I missing?
Well, it looks to me, that if you did once call saveTokenToKeyChain() ever since you've been running the app on your device/simulator, then the KeyChain will hold some string there, as I can't see where you set it to nil (I can't see where you set it at all, but let's suppose that you deleted the code that saves the token). So what your current logic does is that it performs the segue if you have some string saved as token (no matter if it's empty string, random string or an expired token). Whatever was left there, getTokenFromKeyChain() will return you a string so your if token != nil will always evaluate to true.
On the other hand, if you clear the KeyChain data, as I can't find any piece of code that saves the token, the UI will always say that the login fails, even if it actually succeeds.
So you should handle the login success/failure with properly writing/deleting the token to/from KeyChain.

"Flag" / Report user functionality to work — Swift / Xcode / Firebase

I'm trying to allow a user to report a user (in a Tinder-like app). To report, this button takes the user to a new VC to elaborate the issue as an email.
What I'm missing:
How can I add the reporter's and reportee's Firebase unique ID to the email (or whatever form of communication)? (So then I can investigate and take action as needed)
Here's what I have:
The code to properly send an email...
func configureMailController() -> MFMailComposeViewController {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self
mailComposerVC.setToRecipients(["RadiusAppHelp#gmail.com"])
mailComposerVC.setSubject("Reporting user")
mailComposerVC.setMessageBody("Please include as much detail as possible:", isHTML: false)
return mailComposerVC
}
func showMailError() {
let sendMailErrorAlert = showAlert(withTitle: "Could not send message", message: "Please try again")
let dismiss = UIAlertAction(title: "Okay", style: .default, handler: nil)
// sendMailErrorAlert.addAction(dismiss)
// self.present(sendMailErrorAlert, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
}
The code to pull the other user's ID in the swipe view VC...
var otherUsersId = ""
var currentlyViewedUserId: String?
firebaseServer.fetchUsers {[weak self] (usersDict) in
self?.usersDict = usersDict
let fetchedUsers = Array(usersDict.values)
self?.filterBlockedUsers(from: fetchedUsers)
self?.loadFirstUser()
self?.cardView.reloadData()
}
func loadFirstUser() {
if users.count > 0 {
let imageView = UIImageView()
let storage = Storage.storage()
let storageRef = storage.reference(withPath:
"\(users[0].userId!)/photos/\(0)")
currentlyViewedUserId = users[0].userId
PhotoUploader.downloadImageUrl(from:
storageRef) { (url) in
guard let url = url else { return }
imageView.downloaded(from: url,
contentMode: .scaleAspectFill)
}
nameLbl.text = users[0].firstName
setupDetailsFor(user: users[0])
infoCollectionView.reloadData()
}
}
As well as the code to block a user (but blocking / reporting functions work independently).
Any help is greatly appreciated!
Okay, I found at least a temporary fix...
(Not using Firestore however, which I'll eventually need to implement — https://www.youtube.com/watch?v=Ofux_4c94FI)
In FirebaseFunctions.swift...
// Report Someone
func reportSomeone(with userId: String, completion:
#escaping (Error?) -> Void) {
let usersRef = db.child("users")
if let uid = Auth.auth().currentUser?.uid {
usersRef.child("\(uid)/report/\ .
(userId)").setValue(true) { (error, dbref) in
completion(error)
}
}
}
// Set Preferences for Reporting
func reportPreferences(with userId: String,
completion: #escaping (Error?) -> Void) {
let usersRef = db.child("users")
if let uid = Auth.auth().currentUser?.uid {
usersRef.child("\(uid)/preferences/\ .
(userId)").setValue(true) { (error, dbref) in
completion(error)
}
}
}
In User.swift...
var report: [String: Bool]? = [:]
func makeDictionary() -> [String: Any] {
print("")
return [
"report": report ?? [:]
]
static func makeObjectFrom(_ dictionary: [String:
Any]) -> LocalUser {
let report = dictionary["report"] as?
[String:Bool] ?? [:]
}
let localUser = LocalUser(report: report)
In the View Controller...
import Firebase
var currentUserId = Auth.auth().currentUser?.uid
var otherUsersId = ""
// Report Button
var isCurrentUserReported = false
var isOtherUserReported = false
var currentlyViewedUserId: String?
// FILTER REPORTED USERS
func filterReportUsers(from users: [LocalUser]) {
var notReportUsers = users
var notReportUsersDict = self.usersDict
var reportUsers = newUser.report ?? [:]
if let currentUserId =
Auth.auth().currentUser?.uid {
reportUsers[currentUserId] = true
}
for (userId, report) in reportUsers ?? [:] {
print("UserId...", userId)
print("UserHere...", usersDict[userId])
if let user = usersDict[userId] {
notReportUsersDict.removeValue(forKey:
userId)
}
}
let notReport = Array(notReportUsersDict.values)
self.users = notReport
}
This isn't perfect, but it works!