how can I use a variable from another function swift? - ios

I have a variable by the name of email in the contact picker function. I am trying to use that variable in the IBAction function for the MFMailComposeViewController. I want to apply it to toRecipient. How would I go about using a variable from another function?
import UIKit
import Contacts
import ContactsUI
import MessageUI
class ViewController: UIViewController, CNContactPickerDelegate, MFMailComposeViewControllerDelegate, UITextFieldDelegate {
//Message Setup
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var companyTextField: UITextField!
#IBOutlet weak var lblDetails: UILabel!
#IBAction func btnSelectEmployee(_ sender: Any) {
let entityType = CNEntityType.contacts
let authStatus = CNContactStore.authorizationStatus(for: entityType)
if authStatus == CNAuthorizationStatus.notDetermined {
let contactStore = CNContactStore.init()
contactStore.requestAccess(for: entityType, completionHandler: { (success, nil) in
if success {
self.openContacts()
}
else {
print("Not Authorized")
}
})
}
else if authStatus == CNAuthorizationStatus.authorized {
self.openContacts()
}
}
func openContacts() {
let contactPicker = CNContactPickerViewController.init()
contactPicker.delegate = self
self.present(contactPicker, animated: true, completion: nil)
}
func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
picker.dismiss(animated: true) {
}
}
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
//When user select any contact
let fullName = "\(contact.givenName) \(contact.familyName)"
var email = "Not Available"
if !contact.emailAddresses.isEmpty {
let emailString = (((contact.emailAddresses[0] as AnyObject).value(forKey: "labelValuePair") as AnyObject).value(forKey: "value"))
email = emailString! as! String
self.lblDetails.text = "\(fullName)\n \(email)"
}
}
//Mail View
#IBAction func sendAction(_ sender: Any) {
let mailVC = MFMailComposeViewController()
mailVC.mailComposeDelegate = self
mailVC.setSubject("Hello. You have a visitor in the lobby.")
let mailContent = "\(nameTextField.text!) from \(companyTextField.text!) is here to see you."
mailVC.setMessageBody(mailContent, isHTML: false)
let toRecipient = "somebody5555555#gmail.com"
mailVC.setToRecipients([toRecipient])
self.present(mailVC, animated: true) {
self.nameTextField.text = ""
self.companyTextField.text = ""
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
nameTextField.resignFirstResponder()
companyTextField.resignFirstResponder()
return true
}
}

Define the var outside the function
var email = "Not Available"
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
//When user select any contact
let fullName = "\(contact.givenName) \(contact.familyName)"
if !contact.emailAddresses.isEmpty {
let emailString = (((contact.emailAddresses[0] as AnyObject).value(forKey: "labelValuePair") as AnyObject).value(forKey: "value"))
email = emailString! as! String
self.lblDetails.text = "\(fullName)\n \(email)"
}
}
Now you can use the variable inside your class wherever you want.
To get more basics, read documentation Apple Documentation

Related

User variable is nil when creating a user with Firebase

I am following a tutorial and cannot seem to register my user as the user variable in the Firebase .createUser method appears to be nil. Therefore, when I unwrap it, I get an error.
I have read through a lot of the documentation as well as checked many other questions similar to mine but nothing seems to work
import UIKit
import Firebase
import SwiftKeychainWrapper
class ViewController: UIViewController {
#IBOutlet weak var userImgView: UIImageView!
#IBOutlet weak var usernameField: UITextField!
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
var imagePicker: UIImagePickerController!
var selectedImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
if let _ = KeychainWrapper.standard.string(forKey: "uid") {
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setupUser(userUid: String) {
if let imageData = self.userImgView.image!.jpegData(compressionQuality: 0.2) {
let imgUid = NSUUID().uuidString
let metaData = StorageMetadata()
Storage.storage().reference().child(imgUid).putData(imageData, metadata: metaData) { (metadata, error) in
let downloadURL = metadata
let userData = [
"username": self.usernameField.text!,
"userImg": downloadURL!
] as [String : Any]
Database.database().reference().child("users").child(userUid).setValue(userData)
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
}
#IBAction func signInPressed(_ sender: Any) {
if let email = emailField.text, let password = passwordField.text {
Auth.auth().signIn(withEmail: email, password: password) { user, error in
if error != nil && !(self.usernameField.text?.isEmpty)! {
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
self.performSegue(withIdentifier: "toFeed", sender: nil)
let userID = (user?.user.uid)!
self.setupUser(userUid: userID)
KeychainWrapper.standard.set(userID, forKey: "uid")
}
} else {
if let userID = (user?.user.uid) {
KeychainWrapper.standard.set((userID), forKey: "uid")
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
}
}
}
#IBAction func getPhoto (_ sender: AnyObject) {
present(imagePicker, animated: true, completion: nil)
}
}
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
internal func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
userImgView.image = image
} else {
print("image wasnt selected")
}
imagePicker.dismiss(animated: true, completion: nil)
}
}
The error I am getting is one the "let userID = (user?.user.uid)!". It is
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
The completion block for createUser(withEmail:,password:) gets called with either a AuthResult.user or an error. That why, as Joshua commented, you should check if error is nil before accessing any of the user properties.
From the auth quickstart for Swift:
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
strongSelf.hideSpinner {
guard let user = authResult?.user, error == nil else {
strongSelf.showMessagePrompt(error!.localizedDescription)
return
}
print("\(user.email!) created")
strongSelf.navigationController?.popViewController(animated: true)
}
}

Getting error `Do you want to add protocol stubs? [duplicate]

This question already has answers here:
Xcode 8 says "Do you want to add a stub?" How do I answer?
(3 answers)
Closed 4 years ago.
I added the ImageView Protocol. What can be done to remove the error
Do you want to add protocol stubs?
CardsViewController
import UIKit
protocol ImageViewProtocol{
func sendImageToViewController(theImage: UIImage)
}
class CardsViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, ImageViewProtocol {
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var locationTextField: UITextField!
#IBOutlet weak var imageView: UIImageView!
#IBAction func goToViewController2Action(_ sender: Any)
{
let viewcontroller2 = storyboard?.instantiateViewController(withIdentifier: "viewController2") as! ViewController2
viewcontroller2.delegate = self
self.navigationController?.pushViewController(viewcontroller2, animated: true)
}
func chooseImagePickerAction(source: UIImagePickerController.SourceType) {
if UIImagePickerController.isSourceTypeAvailable(source) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.allowsEditing = true
imagePicker.sourceType = source
self.present(imagePicker, animated: true, completion: nil)
}
}
#IBAction func saveButtonPressed(_ sender: UIBarButtonItem) {
if nameTextField.text == "" || locationTextField.text == "" || textField.text == "" {
print("Not all fields are filled")
} else {
if let context = (UIApplication.shared.delegate as? AppDelegate)?.coreDataStack.persistentContainer.viewContext {
let card = Card(context: context)
card.name = nameTextField.text
card.location = locationTextField.text
card.number = textField.text
if let image = imageView.image {
card.image = image.pngData()
}
do {
try context.save()
print("Cохранение удалось!")
} catch let error as NSError {
print("Не удалось сохранить данные \(error), \(error.userInfo)")
}
}
performSegue(withIdentifier: "unwindSegueFromNewCard", sender: self)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
imageView.image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
dismiss(animated: true, completion: nil)
}
}
ViewController2
import UIKit
class ViewController2: UIViewController {
var filter : CIFilter!
var delegate: ImageViewProtocol!
#IBOutlet weak var select: UISegmentedControl!
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var barcodeImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
barcodeImageView.image = UIImage(named: "photo")
}
#IBAction func saveButtonAction(_ sender: Any) {
if textField.text == "" {
print("Not all fields are filled")
} else {
delegate.sendImageToViewController(theImage: barcodeImageView.image!)
self.navigationController?.popViewController(animated: true)
}
performSegue(withIdentifier: "unwindSegueFromViewController", sender: sender)
}
#IBAction func tappedEnter(_ sender: Any) {
if textField.text?.isEmpty ?? true {
return
} else {
if let texttxt = textField.text {
let data = texttxt.data(using: .ascii, allowLossyConversion: false)
if select.selectedSegmentIndex == 0
{
filter = CIFilter(name: "CICode128BarcodeGenerator")
} else {
filter = CIFilter(name: "CIQRCodeGenerator")
}
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 5, y: 5)
let image = UIImage(ciImage: filter.outputImage!.transformed(by: transform))
barcodeImageView.image = image
}
}
}
}
This error comes because you implemented protocol (ImageViewProtcol) but you haven't add required methods of your protocol (in your case sendImageToViewController(theImage: UIImage)). All methods of your protocol are required by default. If you want to change it, you can look here.
It's the same as when you're implementing UITableViewDataSource, you also need to add required methods like number of items etc.
To fix this, add this method to your CardsViewController:
func sendImageToViewController(theImage: UIImage) {
// do something with image
}

Issues Uploading Images and posts to Firebase 5 Database

I am currently trying to upload a photo URL and post caption onto my firebase database. The photos are currently being saved on firebase storage which is fine, however I would like for it to also appear on the firebase database.
I repeatedly find myself dealing with this error "Value of type 'StorageMetadata' has no member 'downloadURL'"
I understand that in firebase 5 to get the url from storage you need to call downloadURL on storage reference, not metadata. I have tried multiple ways and examples, but it all leads to errors.screenshot of error
#IBOutlet weak var photo: UIImageView!
#IBOutlet weak var captionTextView: UITextView!
#IBOutlet weak var removeButton: UIBarButtonItem!
#IBOutlet weak var shareButton: UIButton!
var selectedImage: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleSelectPhoto)); photo.addGestureRecognizer(tapGesture)
photo.isUserInteractionEnabled = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
handlePost()
}
func handlePost() {
if selectedImage != nil {
self.shareButton.isEnabled = true
self.removeButton.isEnabled = true
self.shareButton.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
}else{
self.shareButton.isEnabled = false
self.shareButton.backgroundColor = .lightGray
self.removeButton.isEnabled = false
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
#objc func handleSelectPhoto() {
let pickerController = UIImagePickerController()
pickerController.delegate = self
present(pickerController, animated: true, completion: nil)
}
#IBAction func shareButton_TouchUpInside(_ sender: Any) {
view.endEditing(true)
let hud = JGProgressHUD(style: .dark)
hud.textLabel.text = "Loading"
hud.show(in: self.view)
hud.dismiss(afterDelay: 3.0)
if let profileImg = self.selectedImage, let imageData = UIImageJPEGRepresentation(profileImg, 0.1) {
let photoIdString = NSUUID().uuidString
let storageRef = Storage.storage().reference(forURL: ".......").child("posts").child(photoIdString)
storageRef.putData(imageData, metadata: nil, completion: { (metadata, error) in
if error != nil {
return
}
//let photoUrl = url?.absoluteString
let photoUrl = metadata?.downloadURL()?.absoluteString
self.sendDataToDatabase(photoUrl: photoUrl!)
}
)}
}
#IBAction func remove_TouchUpInside(_ sender: Any) {
clean()
handlePost()
}
func sendDataToDatabase(photoUrl: String) {
let ref = Database.database().reference()
let postsReference = ref.child("posts")
let newPostId = postsReference.childByAutoId().key
let newPostReference = postsReference.child(newPostId)
newPostReference.setValue(["photoUrl": photoUrl, "caption": captionTextView.text!], withCompletionBlock: {
(error, ref) in
if error != nil {
ProgressHUD.showError(error!.localizedDescription)
return
}
ProgressHUD.showSuccess("Success")
self.clean()
self.tabBarController?.selectedIndex = 0 // switches user back to selected tabbar 0 = first 1 = second etc.
})
}
func clean() {
self.captionTextView.text = ""
self.photo.image = UIImage(named:"placeholder-photo")
self.selectedImage = nil
}
}
extension CameraViewController: UIImagePickerControllerDelegate,
UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String : Any]) {
print("did finish picking media")
if let image = info["UIImagePickerControllerOriginalImage"]
as? UIImage {
selectedImage = image
photo.image = image
}
// profileImage.image = infoPhoto
dismiss(animated: true, completion: nil)
}
}
import UIKit
import ProgressHUD
import FirebaseStorage
import FirebaseDatabase
class CameraViewController: UIViewController {
#IBOutlet weak var photo: UIImageView!
#IBOutlet weak var shareButton: UIButton!
#IBOutlet weak var captionTextView: UITextView!
#IBOutlet weak var removeButton: UIBarButtonItem!
var selectedImage: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleSelectPhoto))
photo.addGestureRecognizer(tapGesture)
photo.isUserInteractionEnabled = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
handlePost()
}
func handlePost(){
if selectedImage != nil {
self.shareButton.isEnabled = true
self.removeButton.isEnabled = true
self.shareButton.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
} else {
self.shareButton.isEnabled = false
self.removeButton.isEnabled = false
self.shareButton.backgroundColor = .lightGray
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
#objc func handleSelectPhoto(){
let pickerController = UIImagePickerController()
pickerController.delegate = self
present(pickerController, animated: true, completion: nil)
}
// Share photos to the storage database
#IBAction func shareButton_TouchUpInside(_ sender: Any) {
view.endEditing(true)
ProgressHUD.show("Please wait...", interaction: false)
if let profileImg = self.selectedImage, let photoData = profileImg.jpegData(compressionQuality: 0.1) {
let photoIdString = NSUUID().uuidString
print(photoIdString)
let storageRef = Storage.storage().reference(forURL:Config.STORAGE_ROOT_REF).child("Posts").child(photoIdString)
storageRef.putData(photoData, metadata: nil, completion: { (metadata, error) in
if error != nil {
ProgressHUD.showError(error!.localizedDescription)
return
}
let photoUrl = metadata?.downloadURL()?.absoluteString
self.sendDataToDatabase(photoUrl: photoUrl!)
})
} else {
ProgressHUD.showError("Profile image can't be empty")
}
}
#IBAction func remove_touchUpInside(_ sender: Any) {
clean()
handlePost()
}
func sendDataToDatabase(photoUrl:String){
let ref = Database.database().reference()
let postsReference = ref.child("posts")
let newPostId = postsReference.childByAutoId().key
let newPostReference = postsReference.child(newPostId)
newPostReference.setValue(["photoUrl": photoUrl, "caption": captionTextView.text!], withCompletionBlock: {
(error, ref) in
if error != nil{
ProgressHUD.showError(error!.localizedDescription)
return
}
ProgressHUD.showSuccess("Success")
self.clean()
self.tabBarController?.selectedIndex = 0
})
}
func clean(){
self.captionTextView.text = ""
self.photo.image = UIImage(named: "placeholder-img")
self.shareButton = nil
}
}
extension CameraViewController : UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
print("did Finish picking media")
if let chosenImage = info[.originalImage] as? UIImage {
selectedImage = chosenImage
photo.image = chosenImage
}
dismiss(animated: true, completion: nil)
}
}
Use Above code, it will successfully upload the image and post that to the Firebase Database

Add elements to search history?

I have a model - Movies.
and two controllers - first for search movie by title, second - for display result with poster, title and year.
Now i need to create some history search on my third controller
(searchHistoryController - TableView) where displayed all movies, and when i tapped on cell with movie's title show movie info.
How I can build it?
I tried create array in my model. And write resutl in it, but each time when i use search it rewrite array, not add new element.
Maybe use realm
Need some help:)
Movie.swift
import Foundation
import UIKit
import Alamofire
import AlamofireImage
protocol MovieDelegate {
func updateMovieInfo()
}
class Movie {
private let omdbUrl = "http://www.omdbapi.com/?"
var title: String?
var filmYear: String?
var poster: String?
var delegete: MovieDelegate!
var historyMovie = [Movie]()
func getMovieInfo(title: String, completion: #escaping ()->()){
let params = ["t": title]
Alamofire.request(omdbUrl, method: .get, parameters: params).validate(statusCode: 200..<300).validate(contentType: ["application/json"]).responseJSON { (response) in
switch response.result {
case .success(let JSON):
let response = JSON as! NSDictionary
let status = response["Response"] as! String
if status == "True" {
self.title = (response["Title"] as! String)
self.filmYear = (response["Year"] as! String)
self.poster = (response["Year"] as! String)
// self.delegete.updateMovieInfo()
completion()
} else {
self.title = (response["Error"] as! String)
completion()
}
case .failure(let error):
print (error)
}
}
}
}
SearchVC
import UIKit
class SearchViewController: UIViewController {
var movie = Movie()
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
#IBOutlet weak var searchTextField: UITextField!
#IBOutlet weak var searchButton: UIButton!
#IBAction func searchButtonTapped(_ sender: UIButton) {
activityIndicator.startAnimating()
DispatchQueue.main.async {
self.movie.getMovieInfo(title: self.searchTextField.text!, completion: {
self.activityIndicator.stopAnimating()
self.performSegue(withIdentifier: "movieInfo", sender: self)
})
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! DetailInfoViewController
secondVC.movieTitle = movie.title!
}
}
DetailVC
class DetailInfoViewController: UIViewController, MovieDelegate {
#IBAction func showHistory(_ sender: UIButton) {
performSegue(withIdentifier: "showHistory", sender: self)
}
#IBOutlet weak var posterImageView: UIImageView!
#IBOutlet weak var filmNameLabel: UILabel!
#IBOutlet weak var filmYearLabel: UILabel!
var movie = Movie()
var movieTitle = ""
override func viewDidLoad() {
super.viewDidLoad()
self.movie.getMovieInfo(title: movieTitle ) {
self.updateMovieInfo()
}
self.movie.delegete = self
}
func updateMovieInfo() {
getPoster(link: movie.poster)
filmNameLabel.text = movie.title
filmYearLabel.text = movie.filmYear
}
func getPoster(link: String?) {
if link != nil {
guard let url = URL(string: link!) else { return }
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.async {
self.posterImageView.image = UIImage(data: data)
}
}
} } else {
self.posterImageView.image = #imageLiteral(resourceName: "Image")
}
}
}
First of all, movieHistory should not be part of your Movie class, but part of your SearchViewController class.
Second of all, unless you want to persist your data, you don't need Realm for this.
Just save the movies in SearchViewController into an array once the search button has been tapped and send it to your other view controller in the segue. Like so
#IBAction func searchButtonTapped(_ sender: UIButton) {
activityIndicator.startAnimating()
DispatchQueue.main.async {
self.movie.getMovieInfo(title: self.searchTextField.text!, completion: {
self.activityIndicator.stopAnimating()
movieHistory.append(movie)
self.performSegue(withIdentifier: "movieInfo", sender: movieHistory)
})
}
}
Also, modify prepare(for segue:...) like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! DetailInfoViewController
secondVC.movieTitle = movie.title!
secondVC.movieHistory = movieHistory
}
In detailVC override prepare(for segue:...) as well and send movieHistory to searchHistoryController the same way it is done in the previous VC.

ios Firebase - segue data in a "details post" Controller

Here is my situation:
I have a controller "Feed" which list multiple posts via a table ( a title and image) from Firebase.
On touch of a button, it bring to a "Feed Details" controller, where I would like the data ( title image and caption) from the post clicked previously (parent) being display. ( see screenshot 2)
At the moment nothing is being fetch when I arrive to the feed details controllers ...
How is it possible to fetch the details from the item click previously ??
Currently this is my feed controller:
//
// FeedVC.swift
// MobileAppDemo
//
// Created by Mikko Hilpinen on 31.10.2016.
// Copyright © 2016 Mikkomario. All rights reserved.
//
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import SwiftKeychainWrapper
import SwiftyJSON
class FeedVC: UIViewController, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate
{
#IBOutlet weak var addImageView: UIImageView!
#IBOutlet weak var feedTableView: UITableView!
#IBOutlet weak var titleInputView: InputTextView!
#IBOutlet weak var captionInputView: InputTextView!
private var posts = [Post]()
private var imagePicker = UIImagePickerController()
private var imageSelected = false
private var readPosts: ObserveTask?
override func viewDidLoad()
{
super.viewDidLoad()
imagePicker.delegate = self
imagePicker.allowsEditing = true
feedTableView.dataSource = self
feedTableView.rowHeight = UITableViewAutomaticDimension
feedTableView.estimatedRowHeight = 320
readPosts = Post.observeList(from: Post.parentReference.queryOrdered(byChild: Post.PROPERTY_CREATED))
{
posts in
self.posts = posts.reversed()
self.feedTableView.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
{
if let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as? MessageCell
{
let post = posts[indexPath.row]
cell.configureCell(tableView: tableView, post: post)
return cell
}
else
{
fatalError()
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let image = info[UIImagePickerControllerEditedImage] as? UIImage
{
addImageView.image = image
imageSelected = true
}
picker.dismiss(animated: true, completion: nil)
}
#IBAction func selectImagePressed(_ sender: AnyObject)
{
present(imagePicker, animated: true, completion: nil)
}
#IBAction func postButtonPressed(_ sender: AnyObject)
{
guard let caption = captionInputView.text, !caption.isEmpty else
{
// TODO: Inform the user
print("POST: Caption must be entered")
return
}
guard let title = titleInputView.text, !title.isEmpty else
{
// TODO: Inform the user
print("POST: title must be entered")
return
}
guard let image = addImageView.image, imageSelected else
{
print("POST: Image must be selected")
return
}
guard let currentUserId = User.currentUserId else
{
print("POST: Can't post before logging in")
return
}
imageSelected = false
addImageView.image = UIImage(named: "add-image")
captionInputView.text = nil
titleInputView.text = nil
// Uploads the image
if let imageData = UIImageJPEGRepresentation(image, 0.2)
{
let imageUid = NSUUID().uuidString
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
Storage.REF_POST_IMAGES.child(imageUid).put(imageData, metadata: metadata)
{
(metadata, error) in
if let error = error
{
print("STORAGE: Failed to upload image to storage \(error)")
}
if let downloadURL = metadata?.downloadURL()?.absoluteString
{
// Caches the image for faster display
Storage.imageCache.setObject(image, forKey: downloadURL as NSString)
print("STORAGE: Successfully uploaded image to storage")
_ = Post.post(caption: caption, title: title, imageUrl: downloadURL, creatorId: currentUserId)
}
}
}
}
#IBAction func signOutButtonPressed(_ sender: AnyObject)
{
// Doesn't listen to posts anymore
readPosts?.stop()
try! FIRAuth.auth()?.signOut()
User.currentUserId = nil
dismiss(animated: true, completion: nil)
}
}
I'm using the code for the feeddetails - Which of course doesn't feet the data
My database structure look like below:
How is it possible to fetch the data in that details view ?? If anybody could explain me the process, it will be really fantastic !!
Thank you for all your time and help !!
----- EDIT: --------
Thanks to Retterdesdialogs, I've update the code and the App start, but when i click on the button linkbutton nothin happen and the App crash:
The Console display:
Could not cast value of type 'UIViewController' (0x1087f6758) to 'MobileAppDemo.FeedDetailsController' (0x104bc34a0).
I'm having now for code:
feedVC.swift ( there all the post are listed):
//
// FeedVC.swift
// MobileAppDemo
//
// Created by Mikko Hilpinen on 31.10.2016.
// Copyright © 2016 Mikkomario. All rights reserved.
//
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import SwiftKeychainWrapper
import SwiftyJSON
class FeedVC: UIViewController, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate
{
#IBOutlet weak var addImageView: UIImageView!
#IBOutlet weak var feedTableView: UITableView!
#IBOutlet weak var titleInputView: InputTextView!
#IBOutlet weak var linkbutton: UIButton!
#IBOutlet weak var captionInputView: InputTextView!
private var posts = [Post]()
private var imagePicker = UIImagePickerController()
private var imageSelected = false
private var readPosts: ObserveTask?
override func viewDidLoad()
{
super.viewDidLoad()
imagePicker.delegate = self
imagePicker.allowsEditing = true
feedTableView.dataSource = self
feedTableView.rowHeight = UITableViewAutomaticDimension
feedTableView.estimatedRowHeight = 320
readPosts = Post.observeList(from: Post.parentReference.queryOrdered(byChild: Post.PROPERTY_CREATED))
{
posts in
self.posts = posts.reversed()
self.feedTableView.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
// here you need to add
{
if let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as? MessageCell
{
let post = posts[indexPath.row]
cell.configureCell(tableView: tableView, post: post)
cell.linkbutton.tag = indexPath.row
cell.linkbutton.addTarget(self, action: #selector(FeedVC.toFeedDetailAction(_:)), for: .touchUpInside)
return cell
}
else
{
fatalError()
}
}
func toFeedDetailAction(_ sender: UIButton) {
let FeedDetailsController = self.storyboard?.instantiateViewController(withIdentifier: "FeedDetailsIdentifier") as! FeedDetailsController
FeedDetailsController.post = posts[sender.tag]
self.navigationController?.pushViewController(FeedDetailsController, animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let image = info[UIImagePickerControllerEditedImage] as? UIImage
{
addImageView.image = image
imageSelected = true
}
picker.dismiss(animated: true, completion: nil)
}
#IBAction func selectImagePressed(_ sender: AnyObject)
{
present(imagePicker, animated: true, completion: nil)
}
#IBAction func postButtonPressed(_ sender: AnyObject)
{
guard let caption = captionInputView.text, !caption.isEmpty else
{
// TODO: Inform the user
print("POST: Caption must be entered")
return
}
guard let title = titleInputView.text, !title.isEmpty else
{
// TODO: Inform the user
print("POST: title must be entered")
return
}
guard let image = addImageView.image, imageSelected else
{
print("POST: Image must be selected")
return
}
guard let currentUserId = User.currentUserId else
{
print("POST: Can't post before logging in")
return
}
imageSelected = false
addImageView.image = UIImage(named: "add-image")
captionInputView.text = nil
titleInputView.text = nil
// Uploads the image
if let imageData = UIImageJPEGRepresentation(image, 0.2)
{
let imageUid = NSUUID().uuidString
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
Storage.REF_POST_IMAGES.child(imageUid).put(imageData, metadata: metadata)
{
(metadata, error) in
if let error = error
{
print("STORAGE: Failed to upload image to storage \(error)")
}
if let downloadURL = metadata?.downloadURL()?.absoluteString
{
// Caches the image for faster display
Storage.imageCache.setObject(image, forKey: downloadURL as NSString)
print("STORAGE: Successfully uploaded image to storage")
_ = Post.post(caption: caption, title: title, imageUrl: downloadURL, creatorId: currentUserId)
}
}
}
}
#IBAction func signOutButtonPressed(_ sender: AnyObject)
{
// Doesn't listen to posts anymore
readPosts?.stop()
try! FIRAuth.auth()?.signOut()
User.currentUserId = nil
dismiss(animated: true, completion: nil)
}
}
and the FeedDetails.swift ( where the Detail are listed once you have clicked the button, button has class: linkbutton from the viewVC Controller:
//
// FeedDetailsController.swift
// MobileAppDemo
//
// Created by Mikko Hilpinen on 27.10.2016.
// Copyright © 2016 Mikkomario. All rights reserved.
//
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
import Firebase
import FirebaseAuth
import SwiftKeychainWrapper
fileprivate struct RegisterInfo
{
let email: String
let password: String
}
class FeedDetailsController: UIViewController
{
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
var post: Post!
override func viewDidLoad()
{
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool)
{
if User.currentUserId != nil
{
print("AUTH: USING EXISTING KEYCHAIN")
User.startTrackingCurrentUser()
performSegue(withIdentifier: "ToFeed", sender: nil)
}
else
{
print("AUTH: NO EXSTING KEYCHAIN")
}
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
print("AUTH: Preparing for segue \(segue.identifier)")
if let registrationVC = segue.destination as? RegisterVC
{
print("AUTH: Found registration VC")
if let info = sender as? RegisterInfo
{
print("AUTH: Sending email (\(info.email)) and password (\(info.password.characters.count) chars) information: ")
registrationVC.setBaseInfo(email: info.email, password: info.password)
}
}
}
#IBAction func signInButtonPressed(_ sender: UIButton)
{
if let email = emailField.text, let password = passwordField.text
{
FIRAuth.auth()?.signIn(withEmail: email, password: password)
{
(user, error) in
if let error = error
{
// TODO: Handle other errors here as well
switch FIRAuthErrorCode(rawValue: error._code)!
{
case .errorCodeUserNotFound:
print("AUTH: USER NOT FOUND -> CREATING NEW USER")
print("AUTH: Sending email \(email) and password \(password.characters.count) characters")
self.performSegue(withIdentifier: "RegisterUser", sender: RegisterInfo(email: email, password: password))
default: print("AUTH: ERROR IN EMAIL LOGIN \(error)") // TODO: Inform user
}
}
else
{
print("AUTH: EMAIL AUTH SUCCESSFUL")
User.currentUserId = user?.uid
User.startTrackingCurrentUser()
self.performSegue(withIdentifier: "ToFeed", sender: nil)
}
}
}
// TODO: Inform user that the field contents are missing
}
#IBAction func facebookButtonPressed(_ sender: UIButton)
{
// (Already logged in to FB)
if let fbAccessToken = FBSDKAccessToken.current()
{
print("AUTH: Already logged in to FB")
firebaseAuth(with: FIRFacebookAuthProvider.credential(withAccessToken: fbAccessToken.tokenString))
}
else
{
let facebookLogin = FBSDKLoginManager()
facebookLogin.logIn(withReadPermissions: ["public_profile", "email"], from: self)
{
(result, error) in
if let error = error
{
print("AUTH: UNABLE TO AUTHENTICATE WITH FACEBOOK")
print("AUTH: \(error)")
}
else if let result = result
{
if result.isCancelled
{
print("AUTH: USER CANCELLED FACEBOOK AUTH")
}
else
{
print("AUTH: FACEBOOK AUTH SUCCESS")
let credential = FIRFacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
self.firebaseAuth(with: credential)
}
}
}
}
}
fileprivate func firebaseAuth(with credential: FIRAuthCredential)
{
if FIRAuth.auth() == nil
{
print("AUTH: NO AUTH SERVICE AVAILABLE")
}
FIRAuth.auth()?.signIn(with: credential)
{
(user, error) in
if let error = error
{
print("AUTH: UNABLE TO AUTHENTICATE TO FIREBASE")
print("AUTH: \(error)")
}
else
{
if let user = user
{
print("AUTH: SUCCESSFULLY AUTHENTICATED WITH FIREBASE")
// Updates current user data
var userName = "User"
var image: UIImage?
if let retrievedName = user.displayName
{
userName = retrievedName
}
if let retrievedImageUrl = user.photoURL
{
if let data = try? Data(contentsOf: retrievedImageUrl)
{
image = UIImage(data: data)
}
}
User.post(uid: user.uid, provider: user.providerID, userName: userName, image: image)
{
user in
User.currentUser = user
User.startTrackingCurrentUser()
self.performSegue(withIdentifier: "ToFeed", sender: nil)
}
}
}
}
}
}
1) you need to be able to access the button through IBOutlet or programmatically
2) set the button to the cells indexPath.row and add an action to it
let post = posts[indexPath.row]
cell.configureCell(tableView: tableView, post: post)
// here you need to add
cell.yourButton.tag = indexPath.row
cell.yourButton.addTarget(self, action: #selector(FeedViewController.toFeedDetailAction(_:)), for: .touchUpInside)
return cell
3) when you then press the button you can identify your feed, because you have the button tag, so you know the feed from your Posts-Array
func toFeedDetailAction(_ sender: UIButton) {
let feedDetailsViewController = self.storyboard?.instantiateViewController(withIdentifier: "FeedDetailsIdentifier") as! FeedDetailsViewController
feedDetailsViewController.post = posts[sender.tag]
self.navigationController?.pushViewController(feedDetailsViewController, animated: true)
}
4) in your FeedDetailsViewController add
var post: Post!
5) in FeedDetailsViewController do what ever you want with the post
Hope this helps :)

Resources