I am following a pinterest style UICollectionViewLayout tutorial, however the tutorial has a Photos Model and my photos are in URL format so I am not able to have a return function in the delegate method. How can I return my image to fill this requirement. image
extension PagesCollectionViewController : PinterestLayoutDelegate {
func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath:IndexPath) -> CGFloat {
return userArray[indexPath.row].photoURL
// return photos[indexPath.item].image.size.height
}
import Foundation
import Firebase
import FirebaseDatabase
struct Users {
var email: String!
var uid: String!
var ref: DatabaseReference!
var location: String?
var photoURL: String!
var biography: String?
var key: String?
var name: String!
var phoneNumber: NSNumber?
var password: String!
init(snapshot: DataSnapshot) {
if let snap = snapshot.value as? [String: Any]{
self.password = snap["password"] as! String
self.email = snap["email"] as! String
self.uid = snap["uid"] as! String
self.phoneNumber = Double(snap["phoneNumber"] as! String) as? NSNumber
self.location = snap["location"] as! String
self.photoURL = snap["photoURL"] as! String
self.biography = snap["biography"] as! String
self.name = snap["firstLastName"] as! String
}
}
init(data: NSDictionary) {
self.password = data.value(forKey: "password") as! String
self.email = data.value(forKey: "email") as! String
self.uid = data.value(forKey: "uid") as! String
self.phoneNumber = Double(data.value(forKey: "phoneNumber") as! String) as? NSNumber
self.location = data.value(forKey: "location") as! String
self.photoURL = data.value(forKey: "photoURL") as! String
self.biography = data.value(forKey: "biography") as! String
self.name = data.value(forKey: "firstLastName") as! String
}
init(email: String, uid: String) {
self.email = email
self.uid = uid
self.ref = Database.database().reference()
}
}
Related
I am implementing a social media application using Swift. In the MyProfileViewController, I have used UIImagePickerController to change the current user's profile picture. However, usage of UIImagePickerController causes a duplicate of table view rows even though I handle the table view in viewDidLoad, not in viewWillAppear.
To illustrate, here is my viewDidLoad function.
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
self.automaticallyAdjustsScrollViewInsets = false
ref = Database.database().reference()
storageRef = Storage.storage().reference()
ref.child("users").child((Auth.auth().currentUser?.uid)!).observe(.value, with: {
(snapshot) in
let value = snapshot.value as? NSDictionary
let userId = value?["id"] as! String
let username = value?["username"] as! String
let email = value?["userEmail"] as! String
let profilePictureUrl = value?["profilePicture"] as! String
self.currentUser = User(id: userId, username: username, email: email, profilePicture: profilePictureUrl)
self.tableView.reloadData()
})
ref.child("posts").observe(.childAdded, with: {
(snapshot) in
let value = snapshot.value as? NSDictionary
let id = value?["id"] as! String
let tags = value?["tags"] as! String
let facultyName = value?["faculty"] as! String
let courseName = value?["course"] as! String
let questionTitle = value?["title"] as! String
let questionText = value?["description"] as! String
let dateAndTime = value?["dateAndTime"] as! String
let userID = value?["user-id"] as! String
self.ref.child("users").child(userID).observe(.value, with: {
(snapshot) in
let value = snapshot.value as? NSDictionary
let userId = value?["id"] as! String
let username = value?["username"] as! String
let email = value?["userEmail"] as! String
let profilePictureUrl = value?["profilePicture"] as! String
let post = Post(id: id, tags: tags, facultyName: facultyName, courseName: courseName, questionTitle: questionTitle, questionText: questionText, dateAndTime : dateAndTime, username: username)
if userID == Auth.auth().currentUser?.uid {
self.posts.insert(post, at: 0)
}
self.postDictionary[id] = post
let member = User(id: userId, username: username, email: email, profilePicture: profilePictureUrl)
if post.username == member.username {
self.userDictionary[id] = member
}
self.tableView.reloadData()
})
})
}
And here is my UIImagePickerController function:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let indexPath = NSIndexPath(row: 0, section: 0)
let cell = tableView.cellForRow(at: indexPath as IndexPath) as! MyProfileInfoCell!
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
selectedImage = image
cell?.profilePicture.contentMode = .scaleAspectFit
cell?.profilePicture.image = image
}
var data = NSData()
data = UIImageJPEGRepresentation((cell?.profilePicture.image!)!, 0.8)! as NSData
let filePath = "\((Auth.auth().currentUser?.uid)!)" // path where you wanted to store img in storage
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
self.storageRef = Storage.storage().reference()
self.storageRef.child(filePath).putData(data as Data, metadata: metaData) {
(metaData,error) in
if let error = error {
print(error.localizedDescription)
return
} else{
let downloadURL = metaData!.downloadURL()!.absoluteString
self.ref.child("users").child((Auth.auth().currentUser?.uid)!).updateChildValues(["profilePicture" : downloadURL])
}
}
self.dismiss(animated: true, completion: nil)
print(self.posts.count)
}
My table view consists of 2 prototype cells. The first prototype cell is for the profile information, in which changing profile picture is handled. The second prototype cell is for showing the posts posted by that user. The problem occurs here. When I change my profile picture, the posts rows are being duplicated.
Can anyone help me with this?
The observe method adds an observer to "users" on your DB. Every time you changes the "users" on DB the following code is executed and a post is added to tableview:
self.ref.child("users").child(userID).observe(.value, with: {
(snapshot) in
let value = snapshot.value as? NSDictionary
let userId = value?["id"] as! String
let username = value?["username"] as! String
let email = value?["userEmail"] as! String
let profilePictureUrl = value?["profilePicture"] as! String
let post = Post(id: id, tags: tags, facultyName: facultyName, courseName: courseName, questionTitle: questionTitle, questionText: questionText, dateAndTime : dateAndTime, username: username)
if userID == Auth.auth().currentUser?.uid {
self.posts.insert(post, at: 0)
}
self.postDictionary[id] = post
let member = User(id: userId, username: username, email: email, profilePicture: profilePictureUrl)
if post.username == member.username {
self.userDictionary[id] = member
}
self.tableView.reloadData()
})
When you update the profile picture the DB is updated too and the above code is executed. That is why the posts are duplicated. To fix that changes the method from observe to observeSingleEvent. The observeSingleEvent method requests data from DB once.
self.ref.child("users").child(userID).observeSingleEvent(.value, with: {
(snapshot) in
let value = snapshot.value as? NSDictionary
let userId = value?["id"] as! String
let username = value?["username"] as! String
let email = value?["userEmail"] as! String
let profilePictureUrl = value?["profilePicture"] as! String
let post = Post(id: id, tags: tags, facultyName: facultyName, courseName: courseName, questionTitle: questionTitle, questionText: questionText, dateAndTime : dateAndTime, username: username)
if userID == Auth.auth().currentUser?.uid {
self.posts.insert(post, at: 0)
}
self.postDictionary[id] = post
let member = User(id: userId, username: username, email: email, profilePicture: profilePictureUrl)
if post.username == member.username {
self.userDictionary[id] = member
}
self.tableView.reloadData()
})
I'm comfortable with initializing objects in general but my method only seems to work when all parameters must exist on the database. I have looked at this which works. But I now don't know how to initialize it with a nest array.
This is how I would normally initialise an object:
class User {
var firstName: String
var lastName: String
var summary: String?
var groups = [Group]()
var ref: DatabaseReference?
var key: String
init?(from snapshot: DataSnapshot) {
let snapshotValue = snapshot.value as? [String: Any]
guard let firstName = snapshotValue?["firstName"] as? String, let lastName = snapshotValue?["lastName"] as? String, let summary = snapshotValue?["summary"] as? String else { return nil }
self.firstName = firstName
self.lastName = lastName
self.summary = summary
self.key = snapshot.key
self.groups(snapshot: snapshot.childSnapshot(forPath: "groups"))
}
func groups(snapshot: DataSnapshot) {
for child in snapshot.children {
guard let group = child as? DataSnapshot else { continue }
}
}
Just don't make the optional properties a part of that guard (that's what prevents the execution to finish it:
init?(from snapshot: DataSnapshot) {
let snapshotValue = snapshot.value as? [String: Any]
guard let firstName = snapshotValue?["firstName"] as? String,
let lastName = snapshotValue?["lastName"] as? String else { return nil }
self.firstName = firstName
self.summary = snapshotValue?["summary"] as? String
self.key = snapshot.key
self.groups(snapshot: snapshot.childSnapshot(forPath: "groups"))
}
Optionally, you can use if let syntax:
if let summary = snapshotValue?["summary"] as? String {
self.summary = summary
}
But in this case this is simpler:
self.summary = snapshotValue?["summary"] as? String
I want to fetch data, I have done this multiple times but for some reason it is not working and I see no reason as to why.
My code for fetching the data
func fetchCurrentUserInfo() {
let currentUser = Auth.auth().currentUser!
let currentUserRef = dataBaseRef.child("users").child(currentUser.uid)
currentUserRef.observeSingleEvent(of: .value, with: { (snapshot) in
let user = UserClass(snapshot: snapshot)
self.downloadImageFromFirebase(urlString: user.photoURL!)
self.userName.text = user.getFullName()
//SD Cache
self.userProfileImage.sd_setImage(with: URL(string: user.photoURL!), placeholderImage: UIImage(named: "UserImageTemplate"))
}) { (error) in
let alert = SCLAlertView()
_ = alert.showError("OOPS!", subTitle: error.localizedDescription)
}
}
My user class file
struct UserClass {
var firstName: String?
var email: String?
var photoURL: String?
var uid: String?
var ref: DatabaseReference!
var key: String?
var lastName: String?
var accountType: String?
init(snapshot: DataSnapshot){
key = snapshot.key
ref = snapshot.ref
accountType = (snapshot.value! as! NSDictionary)["Account Type"] as? String
firstName = (snapshot.value! as! NSDictionary)["First Name"] as? String
email = (snapshot.value! as! NSDictionary)["email"] as? String
uid = (snapshot.value! as! NSDictionary)["uid"] as? String
photoURL = (snapshot.value! as! NSDictionary)["photoURL"] as? String
lastName = (snapshot.value! as! NSDictionary)["Last Name"] as? String
}
func getFullName() -> String {
return ("\(firstName!) \(lastName!)")
}
}
}
I have set the read and write rules to true. When I call the fetchCurrentUserInfo function, it returns all the data for the first name, last name, key, etc. but it returns the photo URL, UID or email as nil giving me the error: fatal error: unexpectedly found nil while unwrapping an Optional value.
Your crash log says it all. You're trying to force unwrap an optional variable by as! casting. Make sure you have valid values in your snapshot.
Also, a better model would like so, this is safer:
struct UserClass {
var firstName: String!
var email: String!
var photoURL: String!
var uid: String!
var ref: DatabaseReference!
var key: String!
var lastName: String!
var accountType: String!
init?(snapshot: DataSnapshot?) {
guard let value = snapshot?.value as? [String: AnyObject],
let accountType = value["Account Type"] as? String,
let firstName = value["First Name"] as? String,
let email = value["email"] as? String,
let uid = value["uid"] as? String,
let photoURL = value["photoURL"] as? String,
let lastName = value["Last Name"] as? String else {
return nil
}
self.key = snapshot?.key
self.ref = snapshot?.ref
self.accountType = accountType
self.firstName = firstName
self.email = email
self.uid = uid
self.photoURL = photoURL
self.lastName = lastName
}
func getFullName() -> String {
return ("\(firstName!) \(lastName!)")
}
}
I'm trying to save CNContact details into my UserDefaults but it fails.
Any other way I could save CNContact ?
struct User {
var firstName: String = ""
var lastName: String = ""
var email: String = ""
var phoneNumber: String = ""
var company: String = ""
var assistantContact: CNContact?
init(firstName: String, lastName: String, email: String, phoneNumber: String, company: String, assistantContact: CNContact? = nil) {
self.firstName = firstName
self.lastName = lastName
self.email = email
self.phoneNumber = phoneNumber
self.company = company
self.assistantContact = assistantContact
}
func encode() -> Dictionary<String, AnyObject> {
var dictionary : Dictionary = Dictionary<String, AnyObject>()
dictionary["firstName"] = firstName as AnyObject?
dictionary["lastName"] = lastName as AnyObject?
dictionary["email"] = email as AnyObject?
dictionary["phoneNumber"] = phoneNumber as AnyObject?
dictionary["company"] = company as AnyObject?
dictionary["assistantContact"] = assistantContact as AnyObject?
return dictionary
}
init(dictionary: Dictionary<String, AnyObject>) {
if let firstName = dictionary["firstName"] as? String {
self.firstName = firstName
}
if let lastName = dictionary["lastName"] as? String {
self.lastName = lastName
}
if let email = dictionary["email"] as? String {
self.email = email
}
if let phoneNumber = dictionary["phoneNumber"] as? String {
self.phoneNumber = phoneNumber
}
if let company = dictionary["company"] as? String {
self.company = company
}
if let assistantContact = dictionary["assistantContact"] as? CNContact {
self.assistantContact = assistantContact
}
}
}
First Encode Contacts Array using.
let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: contactsArray)
Now save encodedData in UserDefaults.
For decode contacts array,
if let contacts: [CNContact] = decodedData as? [CNContact] {
//Decoded Contacts Array
}
I am getting an error that in the line "self.email = (snapshot.value as! NSDictionary)["email"] as! String" saying that it could not cast value of type 'NSNull'. How do I fix the error? I thought that since I have a function toAnyObject that it would convert everything to a String.
struct User {
var email: String!
var uid: String!
var ref: DatabaseReference!
var key: String = ""
init(snapshot: DataSnapshot) {
self.email = (snapshot.value as! NSDictionary)["email"] as! String
self.uid = (snapshot.value as! NSDictionary)["uid"] as! String
self.ref = snapshot.ref
self.key = snapshot.key
}
init(email: String, uid: String) {
self.email = email
self.uid = uid
self.ref = Database.database().reference()
}
func toAnyObject() -> [String: Any] {
return ["email":self.email, "uid":self.uid]
}
}
As El Captain v2.0 pointed out, don´t force unwrap a value as it might be nil and you´ll get a crash. Use if let instead:
// if snap has a value, then you can use the value. Otherwise not, you can call an else if you want to.
if let snap = snapshot.value as? [String:Any] {
self.email = snap["email"] as! String
}
And I would skip the as! String too and would have done it like this instead, incase of you don´t get the email value.
if let snap = snapshot.value as? [String:Any], let email = snap["email"] as? String {
// Use email in here now
print(email)
}
So if you have other fields you want to get, just add them into the if-statement.
struct User {
var email: String!
var uid: String!
var ref: DatabaseReference!
var key: String = ""
init(snapshot: DataSnapshot){
self.email = (snapshot.value as! NSDictionary)["email"] as! String
self.uid = (snapshot.value as! NSDictionary)["uid"] as! String
self.ref = snapshot.ref
self.key = snapshot.key
}
init(email: String, uid: String){
if let snap = snapshot.value as? [String:Any] {
// Do not unwrap forcefully, instead use as mentioned below
// so that in case snap["email"] gives null that could be
//handled.
//Also please check that whether you have "firstname" var
//present in not in your code and dictionary both.
self.email = snap["email"] as? String ?? "" //Provide some default value or empty string
}
self.uid = uid
self.ref = Database.database().reference()
}
func toAnyObject() -> [String: Any]{
return ["email":self.email, "uid":self.uid]
}
}