I'm using the firebase for chat application and I've already stored the images in firebase storage now I want to get the url to fetch the user profile but it's not working even I've tried but nothing is working
Here is the function on line 3rd it generates an error (Value of type 'DatabaseReference' has no member 'downloadURL')
func downloadURL(for path: String, completion: #escaping (Result<URL, Error>) -> Void) {
let refrence = database.child(path)
refrence.downloadURL(completion: { url, error in //here downnloadUrl not working perfectly
guard let url = url, error == nil else {
completion(.failure(StorageErrors.failedToGetDownloadUrl))
return
}
completion(.success(url))
})
}
NetworkingService Class
struct NetworkingService{
static let shared = NetworkingService()
private init(){
}
private let database = Database.database().reference() //creating refrence
public func test(){
database.child("name").setValue(["faheem":true])
}
static func emailForImage(emailAddress: String) -> String{
var safeEmail = emailAddress.replacingOccurrences(of: ".", with: "-")
safeEmail = safeEmail.replacingOccurrences(of: "#", with: "-")
return safeEmail
}
func downloadURL(for path: String, completion: #escaping (Result<URL, Error>) -> Void) {
let refrence = database.child(path)
refrence.downloadURL(completion: { url, error in
guard let url = url, error == nil else {
completion(.failure(StorageErrors.failedToGetDownloadUrl))
return
}
completion(.success(url))
})
}
func userExists(email: String,completion: #escaping((Bool) -> Void)){ //return boolen
var safeEmail = email.replacingOccurrences(of: ".", with: "-")
safeEmail = safeEmail.replacingOccurrences(of: "#", with: "-")
database.child(safeEmail).observeSingleEvent(of: .value) { (snapshot) in
guard let foundEmail = snapshot.value as? String else {
completion(false)
return
}
}
completion(true)
}
func insertUser(user: CUser, completion: #escaping(Result<CUser,Error>) -> Void){
database.child(user.identifier).setValue(user.getDict) { (err, dbRef) in
if err != nil{
completion(.failure(err!))
}else{
completion(.success(user))
}
}
}
}
StoreManager Class
struct StoreManager{
static let shared = StoreManager()
private let storageRefrence = Storage.storage().reference()
func uploadProfilePic(data: Data, fileName: String, completion: #escaping(Result<String,Error>) -> Void){
storageRefrence.child("images/\(fileName)").putData(data,metadata: nil) { (metaData, error) in
guard error == nil else {
completion(.failure(AppError.failedToUpload))
print("faheem ye he error \(error?.localizedDescription)")
return
}
self.storageRefrence.child("images/\(fileName)").downloadURL { (url, error) in
guard let url = url else {
completion(.failure(AppError.failedtoDownloadUrl))
return
}
let urlString = url.absoluteString
print("download url is\(urlString)")
completion(.success(urlString))
}
}
}
}
SignupUserClass
class SignUpUserViewController: UIViewController {
#IBOutlet weak var userProfile: UIImageView!
#IBOutlet weak var lname: UITextField!
#IBOutlet weak var fname: UITextField!
#IBOutlet weak var email: UITextField!
#IBOutlet weak var password: UITextField!
var imagePicker = UIImagePickerController() // for imagepicker
private let spinner = JGProgressHUD(style: .dark)
var user:CUser!
override func viewDidLoad() {
super.viewDidLoad()
password.isSecureTextEntry = true
userProfile.layer.masksToBounds = true
userProfile.layer.borderWidth = 2
userProfile.layer.borderColor = UIColor.lightGray.cgColor
userProfile.layer.cornerRadius = userProfile.frame.size.height / 2
// for rounder image
userProfile.isUserInteractionEnabled = true /
let gesture = UITapGestureRecognizer(target: self, action: #selector(userProfileChange)) //photopcker
userProfile.addGestureRecognizer(gesture)
}
#objc private func userProfileChange(){
print("profile pic changed")
getPhoto()
}
private func insertUser(_ user:CUser){
NetworkingService.shared.insertUser(user: user) { (result) in
switch result{
case .success(let user):
print("User inserted: ", user.getDict)
CUser.shared = user
print("sahed Data is fahem\(CUser.shared)")
case .failure(let error):
print("Error: ",error.localizedDescription)
self.showAlert(message: error.localizedDescription)
}
}
}
private func createUser(_ user:CUser){
FirebaseAuth.Auth.auth().createUser(withEmail: user.email, password: user.password) { [weak self] (respomse, error) in
guard let data = respomse else {
print("Error: ",error?.localizedDescription ?? "")
self?.showAlert(message: error?.localizedDescription ?? "")
return
}
self?.insertUser(user)
}
}
private func uploadImage(_ imageData:Data) {
StoreManager.shared.uploadProfilePic(data: imageData, fileName: user.imageName) { (result) in
switch result{
case .success(let url):
self.user.imageURL = url
self.createUser(self.user)
case .failure(let error):
print("Error: ",error.localizedDescription)
self.showAlert(message: error.localizedDescription)
}
}
}
// register User
#IBAction func register(_ sender: UIButton) {
if checkTextFields([email,password,fname,lname]) {
showAlert(message: "Some fields are missing")
}else{
spinner.show(in: self.view)
NetworkingService.shared.userExists(email: email.text!) { (response) in//checking user through email
self.spinner.dismiss()
guard response != nil else { //measn user exists
self.showAlert(message: "user account already exits we verify through email")
return
}
self.user = CUser(firstName: self.fname.text!, lastName: self.lname.text!, email: self.email.text!, password: self.password.text!, imageURL: "")
/*image url is nil becuase we not upload the image here wehn calling the upload image method
then after setting create user method here we only setting the data in user model and url nil*/
if let userImage = self.userProfile.image!.jpegData(compressionQuality: 0.2) {
self.uploadImage(userImage)
}else{
print("Error: Image cannot be compressed")
}
}
}
}
func checkTextFields(_ textfields:[UITextField]) -> Bool{
for textfield in textfields {
return textfield.text?.isEmpty == true ? true: false
}
return true
}
func gotOLogin(){
guard let loginController = self.storyboard?.instantiateViewController(withIdentifier: String(describing: LoginUserViewController.self)) as? LoginUserViewController else {return}
self.navigationController?.pushViewController(loginController, animated: true)
}
func showAlert(title:String = "Error", message:String){
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let ok = UIAlertAction(title: "ok", style:.default, handler: nil)
alertController.addAction(ok)
self.present(alertController, animated: true, completion: nil)
}
}
extension SignUpUserViewController : UIImagePickerControllerDelegate, UINavigationControllerDelegate{
func getPhoto(){
let actionSheet = UIAlertController(title: "Choose Image", message: nil, preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
actionSheet.addAction(UIAlertAction(title: "Take Photo", style: .default, handler: {
[weak self] _ in
self?.presentCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Choose Photo", style: .default, handler: { [weak self] _ in
self?.profilePhotoPicker()
}))
present(actionSheet, animated: true, completion: nil)
}
func presentCamera(){ //not allowed in simualtor to capture photo
imagePicker.sourceType = .camera
imagePicker.delegate = self
imagePicker.allowsEditing = true
present(imagePicker, animated: true, completion: nil)
}
func profilePhotoPicker(){
imagePicker.sourceType = .photoLibrary
imagePicker.delegate = self
imagePicker.allowsEditing = true
present(imagePicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { //called when select photo
print(info)
userProfile.image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
imagePicker.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
ProfileViewcontroler here is getProfilePic fun i already get all the necessary thing but issue is that in networking class download url is not working
class ProfileViewController: UIViewController {
var logoutUser = ["lgout"]
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var currentUserPic: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: String(describing: ProfileCell.self), bundle: .main), forCellReuseIdentifier: String(describing: ProfileCell.self))
}
func getProfilePic(){
guard let userEmail = UserDefaults.standard.value(forKey: "useremail") as? String else {
return}
let userEmailForPic = NetworkingService.emailForImage(emailAddress: userEmail)
let fileName = userEmailForPic + "_profile_image_jpg"
let patheStorage = "images/"+fileName//here i want to fetch the pic but in netwroking class downnload url not working
}
}
Firebase Realtime Database and Firebase Storage are separated services. You should be using downloadURL on a storage reference:
func downloadURL(for path: String, completion: #escaping (Result<URL, Error>) -> Void) {
let reference = Storage.storage().reference().child(path)
reference.downloadURL(completion: { url, error in
guard let url = url, error == nil else {
completion(.failure(StorageErrors.failedToGetDownloadUrl))
return
}
completion(.success(url))
})
}
Related
Here is the code
import UIKit
import Photos
import Firebase
import FirebaseStorage
class SignUpViewController: UIViewController {
#IBOutlet weak var userNameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var errorLabel: UILabel!
#IBOutlet weak var tapToChangeButton: UIButton!
#IBOutlet weak var profileImageView: UIImageView!
var imagePickerController = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
userNameTextField.backgroundColor = .clear
userNameTextField.layer.cornerRadius = 27
userNameTextField.layer.borderWidth = 1
userNameTextField.layer.borderColor = UIColor.systemGreen.cgColor
emailTextField.backgroundColor = .clear
emailTextField.layer.cornerRadius = 27
emailTextField.layer.borderWidth = 1
emailTextField.layer.borderColor = UIColor.systemGreen.cgColor
passwordTextField.backgroundColor = .clear
passwordTextField.layer.cornerRadius = 27
passwordTextField.layer.borderWidth = 1
passwordTextField.layer.borderColor = UIColor.systemGreen.cgColor
let imageTap = UITapGestureRecognizer(target: self, action: #selector(openImagePicker))
profileImageView.isUserInteractionEnabled = true
profileImageView.addGestureRecognizer(imageTap)
profileImageView.layer.cornerRadius = profileImageView.bounds.height / 2
profileImageView.clipsToBounds = true
tapToChangeButton.addTarget(self, action: #selector(openImagePicker), for: .touchUpInside)
imagePickerController = UIImagePickerController()
imagePickerController.allowsEditing = true
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
checkPermissions()
}
#objc func openImagePicker(_ sender:Any) {
// Open Image Picker
self.present(imagePickerController, animated: true, completion: nil)
}
func checkPermissions() {
if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.authorized {
PHPhotoLibrary.requestAuthorization({ (status:
PHAuthorizationStatus) -> Void in
()
})
}
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
} else {
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
}
}
func requestAuthorizationHandler(status: PHAuthorizationStatus) {
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
print("Have authorization")
} else {
print("Authorization declined")
}
}
After pressing the signUp button the uploadToCloud function should be performed but I don't know how to fetch the url from image picker
#IBAction func signupPressed(_ sender: UIButton) {
guard let username = self.userNameTextField.text, username.count > 3 else {
self.errorLabel.text = "Please enter a valid username"
return
}
guard let password = passwordTextField.text else {
self.errorLabel.text = "Please enter a valid password"
return
}
guard let email = emailTextField.text else {
self.errorLabel.text = "Please enter a valid email"
return
}
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
if let e = error {
self.errorLabel.text = e.localizedDescription
} else {
//Navigate to the ChatViewController
let db = Firestore.firestore()
db.collection("users").addDocument(data: ["username": username, "uid": authResult!.user.uid]) { (error) in
if let e = error {
// You may not want to show this error to the user but you should still show a "sanitised" error so that it doesn't leak information.
self.errorLabel.text = e.localizedDescription
} else {
self.performSegue(withIdentifier: "goToMap", sender: self)
}
}
}
}
}
func uploadToCloud(fileURL: URL) {
guard let uid = Auth.auth().currentUser?.uid else { return }
let storage = Storage.storage()
let data = Data()
let storageRef = storage.reference()
let localFile = fileURL
let photoRef = storageRef.child("users/\(uid)")
let uploadTask = photoRef.putFile(from: localFile, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
self.errorLabel.text = error?.localizedDescription
return
}
metadata.contentType = "image/jpg"
}
}
}
extension SignUpViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let pickedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
self.profileImageView.image = pickedImage
}
if let url = info[UIImagePickerController.InfoKey.imageURL] as? URL {
uploadToCloud(fileURL: url)
}
picker.dismiss(animated: true, completion: nil)
}
}
I don't understand your problem well, But I think the problem is your
uploadToCloud function.
This is another version of your function which is more easier to me
func uploadToCloud(with profileImage:UIImage){
let storage = Storage.storage().reference()
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let imageData = profileImage.pngData() else{
return
}
storage.child("users/\(uid)").putData(imageData, metadata: nil) { (StorageMetadata, error) in
guard StorageMetadata != nil else{
print("oops an error occured while data uploading")
return
}
print("Image sent")
}
}
I am not able to load the documents in chat application in Swift IOS using Firestore database, though able to successfully retrieve the data from the Firestore database, I have added the deinit method as well please assist further to resolve the error, I have added the complete view controller , please help me
Error
'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (47) must be equal to the number of rows contained in that section before the update (23), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Code
let kBannerAdUnitID = "ca-app-pub-3940256099942544/2934735716"
#objc(FCViewController)
class FCViewController: UIViewController, UITableViewDataSource, UITableViewDelegate,
UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
// Instance variables
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var sendButton: UIButton!
var ref : CollectionReference!
var ref2: DocumentReference!
var messages: [DocumentSnapshot]! = []
var msglength: NSNumber = 10
fileprivate var _refHandle: CollectionReference!
var storageRef: StorageReference!
var remoteConfig: RemoteConfig!
private let db = Firestore.firestore()
private var reference: CollectionReference?
private let storage = Storage.storage().reference()
// private var messages = [Constants.MessageFields]()
//snapshot private var messages: [Constants.MessageFields] = []
private var messageListener: ListenerRegistration?
// var db:Firestore!
#IBOutlet weak var banner: GADBannerView!
#IBOutlet weak var clientTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.clientTable.register(UITableViewCell.self, forCellReuseIdentifier: "tableViewCell")
// clientTable.delegate = self
//clientTable.dataSource = self
//db = Firestore.firestore()
ref = db.collection("messages").document("hello").collection("newmessages").document("2").collection("hellos").document("K").collection("messages")
ref2 = db.collection("messages").document("hello").collection("newmessages").document("2").collection("hellos").document("K").collection("messages").document()
configureDatabase()
configureStorage()
configureRemoteConfig()
fetchConfig()
loadAd()
}
deinit {
if let refhandle = _refHandle {
let listener = ref.addSnapshotListener { querySnapshot, error in
}
listener.remove()
}
}
func configureDatabase() {
db.collection("messages").document("hello").collection("newmessages").document("2").collection("hellos").document("K").collection("messages").addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
/* let name = documents.map { $0["name"]!}
let text = documents.map { $0["text"]!}
let photourl = documents.map { $0["photoUrl"]!}
print(name)
print(text)
print(photourl)*/
self.messages.append(contentsOf: documents)
// self.clientTable.insertRows(at: [IndexPath(row: self.messages.count-1, section: 0)], with: .automatic)
//self.clientTable.reloadData()
}
}
func configureStorage() {
storageRef = Storage.storage().reference()
}
func configureRemoteConfig() {
remoteConfig = RemoteConfig.remoteConfig()
let remoteConfigSettings = RemoteConfigSettings(developerModeEnabled: true)
remoteConfig.configSettings = remoteConfigSettings
}
func fetchConfig() {
var expirationDuration: Double = 3600
// If in developer mode cacheExpiration is set to 0 so each fetch will retrieve values from
// the server.
if self.remoteConfig.configSettings.isDeveloperModeEnabled {
expirationDuration = 0
}
remoteConfig.fetch(withExpirationDuration: expirationDuration) { [weak self] (status, error) in
if status == .success {
print("Config fetched!")
guard let strongSelf = self else { return }
strongSelf.remoteConfig.activateFetched()
let friendlyMsgLength = strongSelf.remoteConfig["friendly_msg_length"]
if friendlyMsgLength.source != .static {
strongSelf.msglength = friendlyMsgLength.numberValue!
print("Friendly msg length config: \(strongSelf.msglength)")
}
} else {
print("Config not fetched")
if let error = error {
print("Error \(error)")
}
}
}
}
#IBAction func didPressFreshConfig(_ sender: AnyObject) {
fetchConfig()
}
#IBAction func didSendMessage(_ sender: UIButton) {
_ = textFieldShouldReturn(textField)
}
#IBAction func didPressCrash(_ sender: AnyObject) {
print("Crash button pressed!")
Crashlytics.sharedInstance().crash()
}
func inviteFinished(withInvitations invitationIds: [String], error: Error?) {
if let error = error {
print("Failed: \(error.localizedDescription)")
} else {
print("Invitations sent")
}
}
func loadAd() {
self.banner.adUnitID = kBannerAdUnitID
self.banner.rootViewController = self
self.banner.load(GADRequest())
}
// UITableViewDataSource protocol methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue cell
let cell = self.clientTable .dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)
// Unpack message from Firebase DataSnapshot
let messageSnapshot: DocumentSnapshot! = self.messages[indexPath.row]
guard let message = messageSnapshot as? [String:String] else { return cell }
let name = message[Constants.MessageFields.name] ?? ""
if let imageURL = message[Constants.MessageFields.imageURL] {
if imageURL.hasPrefix("gs://") {
Storage.storage().reference(forURL: imageURL).getData(maxSize: INT64_MAX) {(data, error) in
if let error = error {
print("Error downloading: \(error)")
return
}
DispatchQueue.main.async {
cell.imageView?.image = UIImage.init(data: data!)
cell.setNeedsLayout()
}
}
} else if let URL = URL(string: imageURL), let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage.init(data: data)
}
cell.textLabel?.text = "sent by: \(name)"
} else {
let text = message[Constants.MessageFields.text] ?? ""
cell.textLabel?.text = name + ": " + text
cell.imageView?.image = UIImage(named: "ic_account_circle")
if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),
let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage(data: data)
}
}
return cell
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
guard let text = textField.text else { return true }
textField.text = ""
view.endEditing(true)
let data = [Constants.MessageFields.text: text]
sendMessage(withData: data)
return true
}
func sendMessage(withData data: [String: String]) {
var mdata = data
mdata[Constants.MessageFields.name] = Auth.auth().currentUser?.displayName
if let photoURL = Auth.auth().currentUser?.photoURL {
mdata[Constants.MessageFields.photoURL] = photoURL.absoluteString
}
// Push data to Firebase Database
self.ref.document().setData(mdata, merge: true) { (err) in
if let err = err {
print(err.localizedDescription)
}
print("Successfully set newest city data")
}
}
// MARK: - Image Picker
#IBAction func didTapAddPhoto(_ sender: AnyObject) {
let picker = UIImagePickerController()
picker.delegate = self
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.camera) {
picker.sourceType = .camera
} else {
picker.sourceType = .photoLibrary
}
present(picker, animated: true, completion:nil)
}
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion:nil)
guard let uid = Auth.auth().currentUser?.uid else { return }
// if it's a photo from the library, not an image from the camera
if #available(iOS 8.0, *), let referenceURL = info[.originalImage] as? URL {
let assets = PHAsset.fetchAssets(withALAssetURLs: [referenceURL], options: nil)
let asset = assets.firstObject
asset?.requestContentEditingInput(with: nil, completionHandler: { [weak self] (contentEditingInput, info) in
let imageFile = contentEditingInput?.fullSizeImageURL
let filePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000))/\((referenceURL as AnyObject).lastPathComponent!)"
guard let strongSelf = self else { return }
strongSelf.storageRef.child(filePath)
.putFile(from: imageFile!, metadata: nil) { (metadata, error) in
if let error = error {
let nsError = error as NSError
print("Error uploading: \(nsError.localizedDescription)")
return
}
strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
}
})
} else {
guard let image = info[.originalImage] as? UIImage else { return }
let imageData = image.jpegData(compressionQuality:0.8)
let imagePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000)).jpg"
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
self.storageRef.child(imagePath)
.putData(imageData!, metadata: metadata) { [weak self] (metadata, error) in
if let error = error {
print("Error uploading: \(error)")
return
}
guard let strongSelf = self else { return }
strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
}
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion:nil)
}
#IBAction func signOut(_ sender: UIButton) {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
dismiss(animated: true, completion: nil)
} catch let signOutError as NSError {
print ("Error signing out: \(signOutError.localizedDescription)")
}
}
func showAlert(withTitle title: String, message: String) {
DispatchQueue.main.async {
let alert = UIAlertController(title: title,
message: message, preferredStyle: .alert)
let dismissAction = UIAlertAction(title: "Dismiss", style: .destructive, handler: nil)
alert.addAction(dismissAction)
self.present(alert, animated: true, completion: nil)
}
}
}
Edit
perform this block of code on main thread
for doc in documents {
self.messages.append(doc)
self.clientTable.insertRows(at: [IndexPath(row: self.messages.count-1, section: 0)], with: .automatic)
}
This should work..
In my application I have pick the image using UIImagePickerController from photos and then compress and convert to base64 string. Finally I upload the base64 string to server. Here the server was not accept the base64 string and does not work, but in Android and Postman it works well. I couldn't find the mistake in my code.
Here I mention the UIImagePickerControllerDelegate:
// MARK: UIImagePickerControllerDelegate
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
selectedImage = selectedImage.resizeWithWidth(width: 700)!
picker.dismiss(animated: true, completion: nil)
DispatchQueue.main.async {
self.collPhotos.reloadData()
}
}
Here I mention the Base64 Conversion and post the parameter string to server:
func addImagesApiCAll() {
let imagedata1:UIImage = selectedImage as! UIImage
let compressData = UIImagePNGRepresentation(imagedata1)
let base64 = compressData?.base64EncodedString(options: .lineLength64Characters)
print("charCount",base64!.count)
if Reachability()!.isReachable {
let id = Singleton.sharedInstance.selectedCategory!
let parameterStr = "property_id=\(self.PropertyID)&photos=\(base64!)&lang_code=\(lanuguage_selection.value(forKey: "language") ?? "en")&base_id=\(id)&user_id=\(login_session.value(forKey: "UserId")!)"
Network.shared.POSTRequest(withParameterString: parameterStr, serviceURL: SAVE_PHOTO_LISTING, APIKEY: "SAVE_PHOTO_LISTING")
} else {
showInformation(title: "Network Error", message: "Please check your internet connection")
}
}
extension AddPhotoViewController: HTTP_POST_STRING_REQUEST_PROTOCOL {
func httpPostRequest(APIKEY: String, requestURL: String, responseDict: NSDictionary, errorDict: String) {
ListingActivityDelegate.hideActivity()
if APIKEY == "SAVE_PHOTO_LISTING"{
if errorDict.count == 0 {
print(responseDict)
let mod = RentYourSpaceModel(fromDictionary: responseDict as! [String : Any])
if mod.status! != 0 {
Singleton.sharedInstance.rentYourSpace = mod
if Singleton.sharedInstance.rentYourSpace.result[0].step5.productImage.count == 0{
imageFromResponse = "NO"
} else {
imageFromResponse = "YES"
}
}
self.showInformation(title: "Application", message: mod.message)
}
else {
}
}
}
}
Here I mention the code for Select Image from Camera or Gallery:
#IBAction func act_AddPhoto(_ sender: UIButton) {
let actionSheet = UIAlertController(title: "Home Stay", message: "Choose Image", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler: { _ in
self.openCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Photos", style: .default, handler: { _ in
self.openGallary()
}))
actionSheet.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil))
//If you want work actionsheet on ipad then you have to use popoverPresentationController to present the actionsheet, otherwise app will crash in iPad
switch UIDevice.current.userInterfaceIdiom {
case .pad:
actionSheet.popoverPresentationController?.sourceView = sender
actionSheet.popoverPresentationController?.sourceRect = sender.bounds
actionSheet.popoverPresentationController?.permittedArrowDirections = .up
default:
break
}
self.present(actionSheet, animated: true, completion: nil)
}
//MARK: - Open the camera
func openCamera() {
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.camera)){
imagePicker.sourceType = UIImagePickerControllerSourceType.camera
//If you dont want to edit the photo then you can set allowsEditing to false
imagePicker.allowsEditing = true
imagePicker.delegate = self
self.present(imagePicker, animated: true, completion: nil)
}
else{
let alert = UIAlertController(title: "Warning", message: "You don't have camera", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
//MARK: - Choose image from camera roll
func openGallary(){
imagePicker.sourceType = UIImagePickerControllerSourceType.photoLibrary
//If you dont want to edit the photo then you can set allowsEditing to false
imagePicker.allowsEditing = true
imagePicker.delegate = self
self.present(imagePicker, animated: true, completion: nil)
}
Here I give the POSTRequest Method:
//MARK:- Post request with parameter String.
func POSTRequest(withParameterString: String , serviceURL: String , APIKEY: String)
{
var RESPONSE_ERROR = String()
var RESPONSE_DATA = NSDictionary()
let Url = String(format: serviceURL)
guard let serviceUrl = URL(string: Url) else { return }
var request = URLRequest(url: serviceUrl)
let postString = withParameterString
// print(postString)
request.httpBody = postString.data(using: String.Encoding.utf8);
//request.addValue("application/json", forHTTPHeaderField: "content-type")
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(with: request, completionHandler: {
data, response, error in
if let response = response {
print(response)
}
if let resdata = data {
do {
// print(response)
let json = try JSONSerialization.jsonObject(with: resdata, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
//print(json)
if parseJSON.object(forKey: "status") as! NSInteger == 1 {
if error != nil {
RESPONSE_ERROR = (error?.localizedDescription)!
}
DispatchQueue.main.async {
RESPONSE_DATA = parseJSON
self.HTTP_POST_STRING_REQUEST_DELEGATE?.httpPostRequest(APIKEY: APIKEY, requestURL: serviceURL, responseDict: RESPONSE_DATA, errorDict: RESPONSE_ERROR)
}
} else {
DispatchQueue.main.async {
RESPONSE_DATA = parseJSON
self.HTTP_POST_STRING_REQUEST_DELEGATE?.httpPostRequest(APIKEY: APIKEY, requestURL: serviceURL, responseDict: RESPONSE_DATA, errorDict: RESPONSE_ERROR)
}
}
} else {
DispatchQueue.main.async {
RESPONSE_ERROR = "No Data"
self.HTTP_POST_STRING_REQUEST_DELEGATE?.httpPostRequest(APIKEY: APIKEY, requestURL: serviceURL, responseDict: RESPONSE_DATA, errorDict: RESPONSE_ERROR)
}
}
}catch {
DispatchQueue.main.async {
RESPONSE_ERROR = "Check your input datas"
self.HTTP_POST_STRING_REQUEST_DELEGATE?.httpPostRequest(APIKEY: APIKEY, requestURL: serviceURL, responseDict: RESPONSE_DATA, errorDict: RESPONSE_ERROR)
}
}
} else {
DispatchQueue.main.async {
RESPONSE_ERROR = (error?.localizedDescription)!
self.HTTP_POST_STRING_REQUEST_DELEGATE?.httpPostRequest(APIKEY: APIKEY, requestURL: serviceURL, responseDict: RESPONSE_DATA, errorDict: RESPONSE_ERROR)
}
}
})
task.resume()
}
It looks like you are trying to send base64 string as query string. It would be too long to be a query string. You need to share details for POSTRequest method. For more information.
Network.shared.POSTRequest(withParameterString: parameterStr, serviceURL: SAVE_PHOTO_LISTING, APIKEY: "SAVE_PHOTO_LISTING")
And you need to avoid using exclamation (!) in your application. Your code would crash in many points.
For example:
if Reachability()?.isReachable
You can use optional values to prevent crashes.
let parameters = ["property_id":self.PropertyID,
"photos":base64 ?? "",
"lang_code":lanuguage_selection.value(forKey: "language") ?? "en",
"base_id":id,
"user_id":login_session.value(forKey: "UserId") ?? ""];
Network.shared.POSTRequest(parameters: parameters, serviceURL: SAVE_PHOTO_LISTING, APIKEY: "SAVE_PHOTO_LISTING")
in method add json data:
if let jsonData = try? JSONSerialization.data(withJSONObject: parameters) {
request.httpBody?.append(jsonData)
}
Hi when posting a picture in my swift 3 and firebase app how do I add a comment to it like before the picture is actually posted? Like in Instagram and how do I allow other users to comment on the pictures that other people have posted as well? below is every code I have on posting
Post Cell
import UIKit
import Firebase
import FirebaseStorage
import FirebaseDatabase
import SwiftKeychainWrapper
class PostCell: UITableViewCell {
#IBOutlet weak var userImg: UIImageView!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var postImg: UIImageView!
#IBOutlet weak var likesLbl: UILabel!
var post: Post!
var userPostKey: FIRDatabaseReference!
let currentUser = KeychainWrapper.standard.string(forKey: "uid")
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configCell(post: Post, img: UIImage? = nil, userImg: UIImage? = nil) {
self.post = post
self.likesLbl.text = "\(post.likes)"
self.username.text = post.username
if img != nil {
self.postImg.image = img
} else {
let ref = FIRStorage.storage().reference(forURL: post.postImg)
ref.data(withMaxSize: 10 * 10000, completion: { (data, error) in
if error != nil {
print(error)
} else {
if let imgData = data {
if let img = UIImage(data: imgData){
self.postImg.image = img
}
}
}
})
}
if userImg != nil {
self.postImg.image = userImg
} else {
let ref = FIRStorage.storage().reference(forURL: post.userImg)
ref.data(withMaxSize: 100000000, completion: { (data, error) in
if error != nil {
print("couldnt load img")
} else {
if let imgData = data {
if let img = UIImage(data: imgData){
self.userImg.image = img
}
}
}
})
}
_ = FIRDatabase.database().reference().child("users").child(currentUser!).child("likes").child(post.postKey)
}
#IBAction func liked(_ sender: Any) {
let likeRef = FIRDatabase.database().reference().child("users").child(currentUser!).child("likes").child(post.postKey)
likeRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let _ = snapshot.value as? NSNull {
self.post.adjustLikes(addlike: true)
likeRef.setValue(true)
} else {
self.post.adjustLikes(addlike: false)
likeRef.removeValue()
}
})
}
}
FeedVC
import UIKit
import Firebase
import FirebaseDatabase
import FirebaseStorage
import SwiftKeychainWrapper
import CoreImage
class FeedVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var postBtn: UIButton!
var posts = [Post]()
var post: Post!
var imagePicker: UIImagePickerController!
var imageSelected = false
var selectedImage: UIImage!
var userImage: String!
var userName: String!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
FIRDatabase.database().reference().child("posts").observe(.value, with: {(snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
self.posts.removeAll()
for data in snapshot {
print(data)
if let postDict = data.value as? Dictionary<String, AnyObject> {
let key = data.key
let post = Post(postKey: key, postData: postDict)
self.posts.append(post)
}
}
}
self.tableView.reloadData()
})
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell") as? PostCell {
cell.configCell(post: post)
return cell
} else {
return PostCell()
}
}
override var preferredStatusBarStyle : UIStatusBarStyle {
return UIStatusBarStyle.lightContent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
selectedImage = image
imageSelected = true
} else {
print("A valid image wasnt selected")
}
imagePicker.dismiss(animated: true, completion: nil)
guard imageSelected == true else {
print("An image must be selected")
return
}
if let imgData = UIImageJPEGRepresentation(selectedImage, 0.2) {
let imgUid = NSUUID().uuidString
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
FIRStorage.storage().reference().child("post-pics").child(imgUid).put(imgData, metadata: metadata) { (metadata, error) in
if error != nil {
print("image did not save to firebase storage")
} else {
print("uploded to firebase storage")
let downloadURL = metadata?.downloadURL()?.absoluteString
if let url = downloadURL {
self.postToFirebase(imgUrl: url)
}
}
}
}
}
func postToFirebase(imgUrl: String) {
let userID = FIRAuth.auth()?.currentUser?.uid
FIRDatabase.database().reference().child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
let data = snapshot.value as! Dictionary<String, AnyObject>
let username = data["username"]
let userImg = data["userImg"]
let post: Dictionary<String, AnyObject> = [
"username": username as AnyObject,
"userImg": userImg as AnyObject,
"imageUrl": imgUrl as AnyObject,
"likes": 0 as AnyObject
]
let firebasePost = FIRDatabase.database().reference().child("posts").childByAutoId()
firebasePost.setValue(post)
self.imageSelected = false
self.tableView.reloadData()
}) { (error) in
print(error.localizedDescription)
}
}
#IBAction func postImageTapped(_ sender: AnyObject)
{
let alert = UIAlertController(title: "Choose Image", message: nil, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Camera", style: .default, handler: { _ in
self.openCamera()
}))
alert.addAction(UIAlertAction(title: "Gallery", style: .default, handler: { _ in
self.openGallary()
}))
alert.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
func openCamera()
{
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.camera))
{
imagePicker.sourceType = UIImagePickerControllerSourceType.camera
imagePicker.allowsEditing = true
self.present(imagePicker, animated: true, completion: nil)
}
else
{
let alert = UIAlertController(title: "Warning", message: "You don't have camera", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
func openGallary()
{
imagePicker.sourceType = UIImagePickerControllerSourceType.photoLibrary
imagePicker.allowsEditing = true
self.present(imagePicker, animated: true, completion: nil)
}
#IBAction func SignOutPressed(_ sender: AnyObject) {
try! FIRAuth.auth()?.signOut()
KeychainWrapper.standard.removeObject(forKey: "uid")
dismiss(animated: true, completion: nil)
}
}
Post
import Foundation
import Firebase
import FirebaseDatabase
class Post {
private var _username: String!
private var _userImg: String!
private var _postImg: String!
private var _likes: Int!
private var _postKey: String!
private var _postRef: FIRDatabaseReference!
var username: String {
return _username
}
var userImg: String {
return _userImg
}
var postImg: String {
get {
return _postImg
} set {
_postImg = newValue
}
}
var likes: Int {
return _likes
}
var postKey: String {
return _postKey
}
init(imgUrl: String, likes: Int, username: String, userImg: String) {
_likes = likes
_postImg = imgUrl
_username = username
_userImg = userImg
}
init(postKey: String, postData: Dictionary<String, AnyObject>) {
_postKey = postKey
if let username = postData["username"] as? String {
_username = username
}
if let userImg = postData["userImg"] as? String {
_userImg = userImg
}
if let postImage = postData["imageUrl"] as? String {
_postImg = postImage
}
if let likes = postData["likes"] as? Int {
_likes = likes
}
_postRef = FIRDatabase.database().reference().child("posts").child(_postKey)
}
func adjustLikes(addlike: Bool) {
if addlike {
_likes = likes + 1
} else {
_likes = likes - 1
}
_postRef.child("likes").setValue(_likes)
}
}
Hope this is enough information provided.
Still looking for answer? You could probably add some king of view to your post that would hold comments. Instagram only shows a few and then displays the comments in a separate viewController
When I post from a different device, the tableview I on the current device keeps the items that were previously there and then downloads the items again including the new post, can anyone help me to make it so that it only displays one of each item?
I think the issue is in my downloadFromFirebase().
Here is my code:
class DisplayVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var captionField: RoundTextField!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var myMainImg: roundImage!
var imageSelected = false
static var imageCache: NSCache<NSString,UIImage> = NSCache()
var posts = [Post]() // array for the posts
var imagePicker: UIImagePickerController!
#IBAction func addImageTapped(_ sender: Any) {
present(imagePicker, animated: true, completion: nil)
}
#IBAction func logoutPressed(_ sender: Any) {
//remove keychain and sign out of firebase
let keychainResult = KeychainWrapper.standard.removeObject(forKey: KEY_UID)
print("AA: ID removed from keychain: \(keychainResult)")
try! FIRAuth.auth()?.signOut()
performSegue(withIdentifier: "goToSignIn", sender: nil)
}
#IBAction func postBtnTapped(_ sender: Any) {
//does this exist? or is it null? if condition is not true then it is executed
guard let caption = captionField.text, caption != "" else {
let alert = UIAlertController(title: "Bad Caption", message: "Caption must be entered", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
guard let img = myMainImg.image, imageSelected == true else {
let alert = UIAlertController(title: "Bad Image", message: "Image must be choosen", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
if let imgData = UIImageJPEGRepresentation(img, 0.2){
let imgUid = NSUUID().uuidString
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
DataService.ds.REF_POST_IMAGES.child(imgUid).put(imgData, metadata: metadata, completion: { (metadata, error) in
if error != nil{
print("unable to upload image to Firebase storage")
}else{
print("GREAT SUCESS FOR IMAGE ON STORAGE")
let downloadURL = metadata?.downloadURL()?.absoluteString
if let url = downloadURL{
self.postToFireBase(imageURL: url)
}
}
})
}
}
func postToFireBase(imageURL: String){
let post: Dictionary<String, AnyObject> = ["caption": captionField.text as AnyObject,"imageURL":imageURL as AnyObject, "likes":0 as AnyObject,"userName": userName as AnyObject]
let firebasePost = DataService.ds.REF_POSTS.childByAutoId()
firebasePost.setValue(post)
captionField.text = ""
imageSelected = false
myMainImg.image = UIImage(named: "add-image")
posts.removeAll()
tableView.reloadData()
}
//cannot use the view did load for the guard method!
override func viewDidAppear(_ animated: Bool) {
}
override func viewDidLoad() {
super.viewDidLoad()
self.hideKeyboardWhenTappedAround()
tableView.delegate = self
tableView.dataSource = self
imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
posts.removeAll()
self.downloadFromFirebase()
}
func downloadFromFirebase(){
//"POSTS" Listener, initialize listener and it will work constantly
DataService.ds.REF_POSTS.observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot]{
for snap in snapshot{
print("SNAP: \(snap)") // make free objects by parsing the JSON
if let postDict = snap.value as? Dictionary<String, AnyObject>{
let key = snap.key
let post = Post(postKey: key, postData: postDict)
self.posts.append(post) //stick the post in the posts Array
}
}
}
self.tableView.reloadData()
})
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count //number of total posts
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell") as? PostCell{
if let img = DisplayVC.imageCache.object(forKey: post.imageURL as NSString){
cell.configureCell(post: post, image: img)
return cell
}else{
cell.configureCell(post: post, image: nil)
return cell
}
}else{
return PostCell()
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerEditedImage] as? UIImage{
myMainImg.image = image
}else{
print("AA: Valid image wasn't selected")
}
imagePicker.dismiss(animated: true, completion: nil)
imageSelected = true
}
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
}
Try this:
Include after
let post = Post(postKey: key, postData: postDict)
the following:
if !posts.contains(post) {
self.post.append(post)
}
Let me know if it works!