Index related error in retrieving the data from Firestore database - ios

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..

Related

Custom Firebase cell not appearing in TableView

I am trying to populate a TableView with custom cells that have an image in them downloaded from Firebase. The custom cell is not appearing in the Tableview. I believe I configure the cells with an array named 'posts', that is full of 'TimeLinePost', however when I print 'posts.count' for the 'numberOfRows' func 0 appears, so something is not working somewhere. I may also be making a mistake in how I downloading the data. Any assistance where I am going wrong would be great thanks.
This is the code for the TableView and contains the 'TimeLinePost' Class -
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var table: UITableView?
var posts = [TimeLinePost]()
private let storage = Storage.storage().reference()
override func viewDidLoad() {
super.viewDidLoad()
self.table?.register(TableViewCell.nib(), forCellReuseIdentifier: TableViewCell.identifier)
table?.delegate = self
table?.dataSource = self
table?.reloadData()
}
#IBAction func unwindSegue(_ sender: UIStoryboardSegue){
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(posts.count)
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.identifier, for:indexPath) as! TableViewCell
cell.configure(with: posts[indexPath.row])
return cell
}
}
class TimeLinePost {
var image: String
init (image: String) {
self.image = image
}
}
This is the code for uploading the data -
struct MyKeys {
static let imagesFolder = "imagesFolder"
static let uid = "uid"
static let imagesURL = "imagesURL"
static let imagesCollection = "imagesCollection"
}
class uploadViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var imageDownloadUrl: String?
#IBOutlet weak var photoImageView: UIImageView!
var original: UIImage!
private let storage = Storage.storage().reference()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func choosePhoto() {
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary){
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .photoLibrary
navigationController?.present(picker, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
self.navigationController?.dismiss(animated: true, completion: nil)
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
photoImageView.image = image
original = image
}
}
#IBAction func uploadPhoto(_ sender: Any) {
guard let image = photoImageView.image,
let data = image.jpegData(compressionQuality: 1.0)
else {
print("Error")
return
}
let imageName = UUID().uuidString
let imageReference = Storage.storage().reference().child("images").child(imageName)
imageReference.putData(data, metadata: nil) { (metadata, error) in
guard error == nil else {
print("Failed to upload")
return
}
imageReference.downloadURL{ (url, error) in
if let error = error {
print("Error")
return
}
guard let url = url else {
print("Error")
return
}
let dataReference = Firestore.firestore().collection(MyKeys.imagesCollection).document()
let documentUid = dataReference.documentID
let urlString = url.absoluteString
let data = [
MyKeys.uid: documentUid,
MyKeys.imagesURL: urlString,
]
dataReference.setData(data) { (error) in
if let error = error {
print("Error:\(error)")
return
}
UserDefaults.standard.set(documentUid, forKey: MyKeys.uid)
}
}
}
}
}
And this is the code for my custom cell -
class TableViewCell: UITableViewCell {
#IBOutlet var imagePost: UIImageView!
static let identifier = "TableViewCell"
static func nib() -> UINib {
return UINib(nibName: "TableViewCell", bundle: nil)
}
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 configure(with posts: TimeLinePost) {
self.imagePost.image = UIImage(named: posts.image)
}
func downloadImage(){
guard let uid = UserDefaults.standard.value(forKey: MyKeys.uid) else {
print("Error1")
return
}
let query = Firestore.firestore().collection(MyKeys.imagesCollection).whereField(MyKeys.uid, isEqualTo: uid)
query.getDocuments { (snapshot, error) in
if let error = error {
print("Error2")
return
}
guard let snapshot = snapshot, let data = snapshot.documents.first?.data(), let urlString = data[MyKeys.imagesURL] as? String, let url = URL(string: urlString) else {
print("Error3")
return
}
let resource = ImageResource(downloadURL: url)
self.imagePost.kf.setImage(with: resource, completionHandler: { (result) in
switch result {
case .success(_):
print("Success")
return
case .failure(_):
print("Error4")
return
}
})
}
}
}

Firebase ServerValue.timestamp() continuously changes in firebase database

I am Trying to add an instance of ServerValue.timestamp() into my firebase database, when i run the app , the time stamps continuously increase here is the code, im not sure how to stop the timestamp from increasing in firebase
here is my custom class and my tableview class
class Story
{
var text: String = ""
var timestamp: String = ""
let ref: DatabaseReference!
init(text: String) {
self.text = text
ref = Database.database().reference().child("People").child("HomeFeed").child("Posts").childByAutoId()
}
init(snapshot: DataSnapshot)
{
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
text = value["Post"] as! String
ref.updateChildValues(["timestamp":ServerValue.timestamp()])
let id = ref.key
Database.database().reference().child("People").child("HomeFeed").child("Posts").child("\(id)").child("timestamp").observeSingleEvent(of: .value) { (snapshot) in
let dope = snapshot.value as! Double
let x = dope / 1000
let date = NSDate(timeIntervalSince1970: x)
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .medium
DispatchQueue.main.async {
self.timestamp = formatter.string(from: date as Date)
self.timestamp = "\(value["timestamp"])"
}
}
}
}
func save() {
ref.setValue(toDictionary())
}
func toDictionary() -> [String : Any]
{
return [
"Post" : text,
"timestamp" : timestamp
]
}
}
here is the tableview class
class TableViewController: UIViewController,UITableViewDataSource, UITableViewDelegate {
let databaseRef = Database.database().reference()
#IBOutlet weak var tableView: UITableView!
var rub: StorageReference!
#IBAction func createpost(_ sender: Any) {
self.databaseRef.child("ProfileInfo").child(Auth.auth().currentUser!.uid).child("ProfilePic").observe(DataEventType.value) { (snapshot) in
let profpic = snapshot.value as? String
self.databaseRef.child("ProfileInfo").child(Auth.auth().currentUser!.uid).child("Full Name").observe(DataEventType.value) { (snapshot) in }
let fullname = snapshot.value as? String
if profpic == nil && fullname == nil {
let alert = UIAlertController(title: "Need to create profile", message: nil, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "To create profile", style: UIAlertActionStyle.default, handler: { action in self.performSegue(withIdentifier: "ToCreateprof", sender: nil)}))
alert.addAction(UIAlertAction(title: "Dissmiss", style: UIAlertActionStyle.default, handler: nil))
// show the alert
self.present(alert, animated: true, completion: nil)
}else {
self.performSegue(withIdentifier: "ToPost", sender: nil)
}
} //if no prof pic and name, no posting
}
#IBAction func toCreateorprofile(_ sender: Any) {
self.databaseRef.child("ProfileInfo").child(Auth.auth().currentUser!.uid).child("ProfilePic").observe(DataEventType.value) { (snapshot) in
let profpic = snapshot.value as? String
self.databaseRef.child("ProfileInfo").child(Auth.auth().currentUser!.uid).child("Full Name").observe(DataEventType.value) { (snapshot) in }
let fullname = snapshot.value as? String
if profpic != nil && fullname != nil {
self.performSegue(withIdentifier: "olduser", sender: nil)
}else {
self.performSegue(withIdentifier: "ToCreateprof", sender: nil)
}
}
}
let storiesRef = Database.database().reference().child("People").child("HomeFeed").child("Posts")
var stories = [Story]()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// download stories
storiesRef.observe(.value, with: { (snapshot) in
self.stories.removeAll()
for child in snapshot.children {
let childSnapshot = child as! DataSnapshot
let story = Story(snapshot: childSnapshot)
self.stories.insert(story, at: 0)
}
self.tableView.reloadData()
})
}
#objc func handleRefresh(_ refreshControl: UIRefreshControl) {
self.tableView.reloadData()
refreshControl.endRefreshing()
}
lazy var refreshControl: UIRefreshControl = {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action:
#selector(TableViewController.handleRefresh(_:)),
for: UIControlEvents.valueChanged)
refreshControl.tintColor = UIColor.purple
return refreshControl
}()
override func viewDidLoad()
{
super.viewDidLoad()
self.tableView.reloadData()
self.tableView.addSubview(self.refreshControl)
tableView.delegate = self
tableView.dataSource = self
self.tableView.estimatedRowHeight = 92.0
self.tableView.rowHeight = UITableViewAutomaticDimension
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return stories.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Story Cell", for: indexPath) as! StoryTableviewcell
let story = stories[indexPath.row]
cell.story = story
self.databaseRef.child("ProfileInfo").child(Auth.auth().currentUser!.uid).child("Full Name").observe(.value) { (snapshot) in
let name = snapshot.value as? String
if name != nil {
cell.fullnamepost.text = name
}
}
rub = Storage.storage().reference().storage.reference(forURL:"gs://people-3b93c.appspot.com").child("ProfilePic").child(Auth.auth().currentUser!.uid)
if rub != nil {
// Create a UIImage, add it to the array
rub.downloadURL(completion: { (url, error) in
if error != nil {
print(error?.localizedDescription as Any)
return
}
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error as Any)
return
}
guard let imageData = UIImage(data: data!) else { return }
DispatchQueue.main.async {
cell.profimage.image = imageData
}
}).resume()
})
}
return cell
}
}

Filtered view is leading to incorrect detail view when tapped on, but correct detail view when swiped

I think this is my last question before I can finally 1.0 release this thing. Here's my last remaining "can't launch yet" issue: When I filter by state, the resulting rows are not opening the appropriate detail view.
For example, if I search for TX, I see the seven Texas bonuses. If I tap on the 3rd one, I end up in AK3 (which is the 3rd item in the list if it isn't being filtered). The weird part, is that my swipe action DOES know that I am on TX3. Only when tapping to go to the detail does it jump to the wrong bonus.
Here is the full UITableViewController page:
import UIKit
import os.log
import Foundation
class BonusListViewController: UITableViewController {
var bonuses = [JsonFile.JsonBonuses]()
var bonus: JsonFile.JsonBonuses?
var filteredBonuses = [JsonFile.JsonBonuses]()
var detailViewController: BonusDetailViewController? = nil
var riderNumToH:String = UserDefaults.standard.string(forKey: Constants.RiderData().riderNumToH) ?? "000"
var pillionNumToH:String = UserDefaults.standard.string(forKey: Constants.RiderData().pillionNumToH) ?? "000"
var emailDestinationToH:String = UserDefaults.standard.string(forKey: Constants.RallyData().emailDestinationToH) ?? "photos#tourofhonor.com"
struct Constants {
struct RiderData {
let riderNumToH = "riderNumToH"
let pillionNumToH = "pillionNumToH"
}
struct RallyData {
let emailDestinationToH = "emailDestinationToH"
}
}
let defaults = UserDefaults.standard
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
// MARK: Search Support
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Enter two letter state"
navigationItem.searchController = searchController
definesPresentationContext = true
// MARK: Settings Data Struct
struct Constants {
struct RiderData {
let riderNumToH = "riderNumToH"
let pillionNumToH = "pillionNumToH"
}
struct RallyData {
let emailDestinationToH = "emailDestinationToH"
}
}
//MARK: Load the bonuses
print("About to call loadBonuses")
loadBonuses { [weak self] bonuses in
self?.bonuses = bonuses ?? []
DispatchQueue.main.async {
self?.tableView.reloadData()
}
print("loadBonuses called")
}
// MARK: Set Rider Defaults to Initial Values.
let defaults = UserDefaults.standard
print("Setting initial defaults")
if riderNumToH == "" {
print("riderNumToH is blank")
defaults.set("000", forKey: Constants.RiderData().riderNumToH)
} else if riderNumToH == "000" {
print("riderNumToH is 000")
} else {
print("riderNumToH is custom")
}
if pillionNumToH == "" {
print("pillionNumToH is blank")
defaults.set("000", forKey: Constants.RiderData().pillionNumToH)
} else if pillionNumToH == "000" {
print("pillionNumToH is 000")
} else {
print("pillionNumToH is custom")
}
if emailDestinationToH == "" {
print("emailDestinationToH is blank")
defaults.set("photos#tourofhonor.com", forKey: Constants.RallyData().emailDestinationToH)
} else if emailDestinationToH == "photos#tourofhonor.com" {
print("emailDestinationToH is set to default")
} else {
print("emailDestinationToH has been customized")
}
}
// MARK: - Table View Configuration
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering() {
print("Showing \(filteredBonuses.count) Filtered Results")
return filteredBonuses.count
}
print("Found \(bonuses.count) rows in section.")
return bonuses.count
}
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
{
let clearAction = UIContextualAction(style: .normal, title: "Clear Data") { (contextAction: UIContextualAction, sourceView: UIView, completionHandler: (Bool) -> Void) in
print("Clear Action Tapped")
// Delete the created images
let bonus: JsonFile.JsonBonuses
if self.isFiltering() {
bonus = self.filteredBonuses[indexPath.row]
} else {
bonus = self.bonuses[indexPath.row]
}
print("Selected Bonus is \(bonus.bonusCode)")
let fileNameToDeletePri = "\(bonus.bonusCode)_1.jpg"
let fileNameToDeleteOpt = "\(bonus.bonusCode)_2.jpg"
var filePathPri = ""
var filePathOpt = ""
// Find documents directory on device
let dirs : [String] = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true)
if dirs.count > 0 {
let dir = dirs[0] //documents directory
filePathPri = dir.appendingFormat("/" + fileNameToDeletePri)
filePathOpt = dir.appendingFormat("/" + fileNameToDeleteOpt)
print("Local path = \(filePathPri)")
print("Local path = \(filePathOpt)")
} else {
print("Could not find local directory to store file")
return
}
do {
let fileManager = FileManager.default
// Check if primary file exists
if fileManager.fileExists(atPath: filePathPri) {
// Delete file
try fileManager.removeItem(atPath: filePathPri)
} else {
print("Primary image does not exist")
}
// Check if optional file exists
if fileManager.fileExists(atPath: filePathOpt) {
// Delete file
try fileManager.removeItem(atPath: filePathOpt)
} else {
print("Optional image does not exist")
}
}
catch let error as NSError {
print("An error took place: \(error)")
}
tableView.reloadData()
completionHandler(true)
}
clearAction.backgroundColor = .blue
let swipeConfig = UISwipeActionsConfiguration(actions: [clearAction])
return swipeConfig
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "BonusListViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? BonusListViewCell else {
fatalError("The dequeued cell is not an instance of BonusListViewCell.")
}
let bonus: JsonFile.JsonBonuses
if self.isFiltering() {
bonus = self.filteredBonuses[indexPath.row]
} else {
bonus = self.bonuses[indexPath.row]
}
// Set Primary Image
let documentsUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let imgUrl = documentsUrl.appendingPathComponent(bonus.bonusCode + "_1.jpg")
if(FileManager.default.fileExists(atPath:imgUrl.path))
{
do
{
let data = try Data.init(contentsOf:imgUrl)
cell.primaryImage.image = UIImage.init(data:data)
}
catch {
print(error)
}
}
else
{
cell.primaryImage.image = #imageLiteral(resourceName: "DefaultImage")
}
cell.nameLabel.text = bonus.name
cell.bonusCodeLabel.text = bonus.bonusCode.localizedUppercase
cell.categoryLabel.text = bonus.category
cell.valueLabel.text = "\(bonus.value)"
cell.cityLabel.text = "\(bonus.city.capitalized),"
cell.stateLabel.text = bonus.state.localizedUppercase
return cell
}
// MARK: Functions
// MARK: - Fetch JSON from ToH webserver
func downloadJSON(completed: #escaping ([JsonFile.JsonBonuses]?) -> ()) {
let url = URL(string: "http://tourofhonor.com/BonusData.json")!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error == nil, let data = data {
do {
let posts = try JSONDecoder().decode(JsonFile.self, from: data)
completed(posts.bonuses)
self.defaults.set(posts.meta.version, forKey: "jsonVersion")
print("URLSession did not fail")
print("JSON Version Set to \(posts.meta.version)")
} catch {
print("Can't decode JSON: \(error)")
}
} else {
print("downloadJSON completed")
completed(nil)
}
}.resume()
}
func saveBonuses(_ bonuses: [JsonFile.JsonBonuses], to url: URL) {
try? FileManager.default.removeItem(at: url)
do {
let data = try JSONEncoder().encode(bonuses)
try data.write(to: url)
print("saveBonuses successful")
} catch {
print("Error saving bonuses to file:", error)
}
}
func loadBonusesFromFile(_ url: URL) -> [JsonFile.JsonBonuses]? {
do {
let data = try Data(contentsOf: url)
let bonuses = try JSONDecoder().decode([JsonFile.JsonBonuses].self, from: data)
print("loadBonusesFromFile successful")
return bonuses
} catch {
print("Error loading bonuses from file:", error)
return nil
}
}
func loadBonuses(completion: #escaping ([JsonFile.JsonBonuses]?) -> Void) {
let localBonusesURL = try! FileManager.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("BonusData.json")
downloadJSON { bonuses in
if let bonuses = bonuses {
completion(bonuses)
self.saveBonuses(bonuses, to: localBonusesURL)
} else {
print("versions did not match")
completion(self.loadBonusesFromFile(localBonusesURL))
}
}
}
func searchBarIsEmpty() -> Bool {
// Returns true if the text is empty or nil
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredBonuses = bonuses.filter({( bonus: JsonFile.JsonBonuses) -> Bool in
return bonus.state.localizedCaseInsensitiveContains(searchText)
})
tableView.reloadData()
}
func isFiltering() -> Bool {
return searchController.isActive && !searchBarIsEmpty()
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? BonusDetailViewController {
destination.bonus = bonuses[(tableView.indexPathForSelectedRow?.row)!]
}
}
}
extension BonusListViewController: UISearchResultsUpdating {
// MARK: - UISearchResultsUpdating Delegate
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
}
The part of the code that should be filtering the view is identical in both the cellForRowAt and the swipeAction methods:
let bonus: JsonFile.JsonBonuses
if self.isFiltering() {
bonus = self.filteredBonuses[indexPath.row]
} else {
bonus = self.bonuses[indexPath.row]
}
Originally the cellForRowAt did not have the self entries, but I added those to see if it would resolve it, and it didn't.
I found the cause of the issue. Near the bottom of the UITableViewController I had the following:
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? BonusDetailViewController {
destination.bonus = bonuses[(tableView.indexPathForSelectedRow?.row)!]
}
}
This is where the issue was. I fixed it by adding the same if/else logic I used in my cellForRowAt and SwipeAction:
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? BonusDetailViewController {
if self.isFiltering() {
destination.bonus = filteredBonuses[(tableView.indexPathForSelectedRow?.row)!]
} else {
destination.bonus = bonuses[(tableView.indexPathForSelectedRow?.row)!]
}
}
}

Using swipe action to delete files in Swift 4

I have the following function in my app to allow for a Swipe Action to clear the related images (not to remove the row, just some locally stored files). When try it, its using the default "XXX_1.jpg" value, rather than the bonusName that it should be using.
Here is my swipeAction code:
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
{
let clearAction = UIContextualAction(style: .normal, title: "Clear Data") { (contextAction: UIContextualAction, sourceView: UIView, completionHandler: (Bool) -> Void) in
print("Clear Action Tapped")
// Delete the created images
let fileNameToDeletePri = "\(self.bonus?.bonusCode ?? "XXX")_1.jpg"
let fileNameToDeleteOpt = "\(self.bonus?.bonusCode ?? "XXX")_2.jpg"
var filePathPri = ""
var filePathOpt = ""
// Find documents directory on device
let dirs : [String] = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true)
if dirs.count > 0 {
let dir = dirs[0] //documents directory
filePathPri = dir.appendingFormat("/" + fileNameToDeletePri)
filePathOpt = dir.appendingFormat("/" + fileNameToDeleteOpt)
print("Local path = \(filePathPri)")
print("Local path = \(filePathOpt)")
} else {
print("Could not find local directory to store file")
return
}
do {
let fileManager = FileManager.default
// Check if primary file exists
if fileManager.fileExists(atPath: filePathPri) {
// Delete file
try fileManager.removeItem(atPath: filePathPri)
} else {
print("Primary image does not exist")
}
// Check if optional file exists
if fileManager.fileExists(atPath: filePathOpt) {
// Delete file
try fileManager.removeItem(atPath: filePathOpt)
} else {
print("Optional image does not exist")
}
}
catch let error as NSError {
print("An error took place: \(error)")
}
completionHandler(true)
}
clearAction.backgroundColor = .blue
let swipeConfig = UISwipeActionsConfiguration(actions: [clearAction])
return swipeConfig
}
Oddly, I had initially added the above code in my unwindAction on my UITableDetailView and it worked, but decided I didn't want to delete the images anytime someone hit cancel, so I moved it to be a swipe action within my UITableView. I suspect it isn't figuring out which row I'm on and therefore defaulting to the XXX option.
EDIT: Here is the full ViewController, so you can see the above in context:
import UIKit
import os.log
import Foundation
class BonusListViewController: UITableViewController {
var bonuses = [JsonFile.JsonBonuses]()
var bonus: JsonFile.JsonBonuses?
var filteredBonuses = [JsonFile.JsonBonuses]()
var detailViewController: BonusDetailViewController? = nil
var riderNumToH:String = UserDefaults.standard.string(forKey: Constants.RiderData().riderNumToH)!
var pillionNumToH:String = UserDefaults.standard.string(forKey: Constants.RiderData().pillionNumToH)!
var emailDestinationToH:String = UserDefaults.standard.string(forKey: Constants.RallyData().emailDestinationToH)!
struct Constants {
struct RiderData {
let riderNumToH = "riderNumToH"
let pillionNumToH = "pillionNumToH"
}
struct RallyData {
let emailDestinationToH = "emailDestinationToH"
}
}
let defaults = UserDefaults.standard
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
// MARK: Search Support
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Enter two letter state to filter"
navigationItem.searchController = searchController
definesPresentationContext = true
// MARK: Settings Data Struct
struct Constants {
struct RiderData {
let riderNumToH = "riderNumToH"
let pillionNumToH = "pillionNumToH"
}
struct RallyData {
let emailDestinationToH = "emailDestinationToH"
}
}
//MARK: Load the bonuses
print("About to call loadBonuses")
loadBonuses { [weak self] bonuses in
self?.bonuses = bonuses ?? []
DispatchQueue.main.async {
self?.tableView.reloadData()
}
print("loadBonuses called")
}
// MARK: Set Rider Defaults to Initial Values.
let defaults = UserDefaults.standard
print("Setting initial defaults")
if riderNumToH == "" {
print("riderNumToH is blank")
defaults.set("000", forKey: Constants.RiderData().riderNumToH)
} else if riderNumToH == "000" {
print("riderNumToH is 000")
} else {
print("riderNumToH is custom")
}
if pillionNumToH == "" {
print("pillionNumToH is blank")
defaults.set("000", forKey: Constants.RiderData().pillionNumToH)
} else if pillionNumToH == "000" {
print("pillionNumToH is 000")
} else {
print("pillionNumToH is custom")
}
if emailDestinationToH == "" {
print("emailDestinationToH is blank")
defaults.set("photos#tourofhonor.com", forKey: Constants.RallyData().emailDestinationToH)
} else if emailDestinationToH == "photos#tourofhonor.com" {
print("emailDestinationToH is set to default")
} else {
print("emailDestinationToH has been customized")
}
}
// MARK: - Table View Configuration
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering() {
print("Showing \(filteredBonuses.count) Filtered Results")
return filteredBonuses.count
}
print("Found \(bonuses.count) rows in section.")
return bonuses.count
}
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
{
let clearAction = UIContextualAction(style: .normal, title: "Clear Data") { (contextAction: UIContextualAction, sourceView: UIView, completionHandler: (Bool) -> Void) in
print("Clear Action Tapped")
// Delete the created images
print("Selected Bonus is \(self.bonus?.bonusCode ?? "XXX")")
let fileNameToDeletePri = "\(self.bonus?.bonusCode ?? "XXX")_1.jpg"
let fileNameToDeleteOpt = "\(self.bonus?.bonusCode ?? "XXX")_2.jpg"
var filePathPri = ""
var filePathOpt = ""
// Find documents directory on device
let dirs : [String] = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true)
if dirs.count > 0 {
let dir = dirs[0] //documents directory
filePathPri = dir.appendingFormat("/" + fileNameToDeletePri)
filePathOpt = dir.appendingFormat("/" + fileNameToDeleteOpt)
print("Local path = \(filePathPri)")
print("Local path = \(filePathOpt)")
} else {
print("Could not find local directory to store file")
return
}
do {
let fileManager = FileManager.default
// Check if primary file exists
if fileManager.fileExists(atPath: filePathPri) {
// Delete file
try fileManager.removeItem(atPath: filePathPri)
} else {
print("Primary image does not exist")
}
// Check if optional file exists
if fileManager.fileExists(atPath: filePathOpt) {
// Delete file
try fileManager.removeItem(atPath: filePathOpt)
} else {
print("Optional image does not exist")
}
}
catch let error as NSError {
print("An error took place: \(error)")
}
completionHandler(true)
}
clearAction.backgroundColor = .blue
let swipeConfig = UISwipeActionsConfiguration(actions: [clearAction])
return swipeConfig
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "BonusListViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? BonusListViewCell else {
fatalError("The dequeued cell is not an instance of BonusListViewCell.")
}
// let bonus = bonuses[indexPath.row]
let bonus: JsonFile.JsonBonuses
if isFiltering() {
bonus = filteredBonuses[indexPath.row]
} else {
bonus = bonuses[indexPath.row]
}
// Set Primary Image
let documentsUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let imgUrl = documentsUrl.appendingPathComponent(bonus.bonusCode + "_1.jpg")
if(FileManager.default.fileExists(atPath:imgUrl.path))
{
do
{
let data = try Data.init(contentsOf:imgUrl)
cell.primaryImage.image = UIImage.init(data:data)
}
catch {
print(error)
}
}
else
{
cell.primaryImage.image = #imageLiteral(resourceName: "DefaultImage")
}
//let urlString = "http://tourofhonor.com/appimages/"+(bonus.imageName)
//let url = URL(string: urlString)
//cell.primaryImage.downloadedFrom(url: url!)
cell.nameLabel.text = bonus.name.capitalized
cell.bonusCodeLabel.text = bonus.bonusCode.localizedUppercase
cell.categoryLabel.text = bonus.category
cell.valueLabel.text = "\(bonus.value)"
cell.cityLabel.text = "\(bonus.city.capitalized),"
cell.stateLabel.text = bonus.state.localizedUppercase
return cell
}
// MARK: Functions
// MARK: - Fetch JSON from ToH webserver
func downloadJSON(completed: #escaping ([JsonFile.JsonBonuses]?) -> ()) {
let url = URL(string: "http://tourofhonor.com/BonusData.json")!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error == nil, let data = data {
do {
let posts = try JSONDecoder().decode(JsonFile.self, from: data)
completed(posts.bonuses)
self.defaults.set(posts.meta.version, forKey: "jsonVersion")
print("URLSession did not fail")
print("JSON Version Set to \(posts.meta.version)")
} catch {
print("Can't decode JSON: \(error)")
}
} else {
print("downloadJSON completed")
completed(nil)
}
}.resume()
}
func saveBonuses(_ bonuses: [JsonFile.JsonBonuses], to url: URL) {
try? FileManager.default.removeItem(at: url)
do {
let data = try JSONEncoder().encode(bonuses)
try data.write(to: url)
print("saveBonuses successful")
} catch {
print("Error saving bonuses to file:", error)
}
}
func loadBonusesFromFile(_ url: URL) -> [JsonFile.JsonBonuses]? {
do {
let data = try Data(contentsOf: url)
let bonuses = try JSONDecoder().decode([JsonFile.JsonBonuses].self, from: data)
print("loadBonusesFromFile successful")
return bonuses
} catch {
print("Error loading bonuses from file:", error)
return nil
}
}
func loadBonuses(completion: #escaping ([JsonFile.JsonBonuses]?) -> Void) {
let localBonusesURL = try! FileManager.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("BonusData.json")
downloadJSON { bonuses in
if let bonuses = bonuses {
completion(bonuses)
self.saveBonuses(bonuses, to: localBonusesURL)
} else {
print("versions did not match")
completion(self.loadBonusesFromFile(localBonusesURL))
}
}
}
func searchBarIsEmpty() -> Bool {
// Returns true if the text is empty or nil
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredBonuses = bonuses.filter({( bonus: JsonFile.JsonBonuses) -> Bool in
return bonus.state.localizedCaseInsensitiveContains(searchText)
})
tableView.reloadData()
}
func isFiltering() -> Bool {
return searchController.isActive && !searchBarIsEmpty()
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? BonusDetailViewController {
destination.bonus = bonuses[(tableView.indexPathForSelectedRow?.row)!]
}
}
}
extension BonusListViewController: UISearchResultsUpdating {
// MARK: - UISearchResultsUpdating Delegate
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
}
You are creating a new variable with the name bonus so the class variable (self.bonus) is left out to be empty.
You can remove self.bonus in tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? and replace it with the same implementation in forRowAt
let bonus: JsonFile.JsonBonuses
if isFiltering() {
bonus = filteredBonuses[indexPath.row]
} else {
bonus = bonuses[indexPath.row]
}
In this case you will be sure that the bonus is correct and ignore the class variable.

how do I add a comment to it like in Instagram and allow other users to comment on the pictures as well

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

Resources