I have 2 files. First - TransactionsViewController. Second - GetTransactions. When I open TransactionsViewController the table from that view loads faster than the date from GetTransactions. Therefore, it is displayed blank. How to fix it?
Here is the code of TransactionsViewController viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
GetTransactions().getTransactions()
clientsCount = GetTransactions.transactions.count
setupNavBar()
createTable()
refreshSetup()
}
Here is the code of GetTransactions:
class GetTransactions {
static var transactionsArr = [[String : Any]]()
static var transactions = [TransactionInfo]()
let URL_GET_TRANSACTIONS = "https://mashkov.dev/sites/default/BankApp/Transactions/Transactions.php"
func getTransactions(){
GetTransactions.transactionsArr.removeAll()
AF.request(URL_GET_TRANSACTIONS).responseJSON{ (response) in
if (response.value as? [[String : Any]]) != nil {
GetTransactions.transactionsArr = response.value as! [[String : Any]]
}
self.convertData()
print(GetTransactions.transactions)
}
}
func convertData() {
GetTransactions.transactions.removeAll()
for transaction in GetTransactions.transactionsArr {
let cl = TransactionInfo(id: transaction["id"] as! String,
payee: transaction["payee_account_id"] as! String,
sender: transaction["sender_account_id"] as! String,
transDate: transaction["trans_date"] as! String,
amount: transaction["amount"] as! String,
isSuccessfully: Bool((transaction["isSuccessfully"] as! String)) ?? true)
GetTransactions.transactions.append(cl)
}
}
}
What you need is a closure / block to be passed as an argument
class GetTransactions {
static var transactionsArr = [[String : Any]]()
static var transactions = [TransactionInfo]()
let URL_GET_TRANSACTIONS = "https://mashkov.dev/sites/default/BankApp/Transactions/Transactions.php"
func getTransactions(completion block: () -> ()){
GetTransactions.transactionsArr.removeAll()
AF.request(URL_GET_TRANSACTIONS).responseJSON{ (response) in
if (response.value as? [[String : Any]]) != nil {
GetTransactions.transactionsArr = response.value as! [[String : Any]]
}
self.convertData()
print(GetTransactions.transactions)
block()
}
}
func convertData() {
GetTransactions.transactions.removeAll()
for transaction in GetTransactions.transactionsArr {
let cl = TransactionInfo(id: transaction["id"] as! String,
payee: transaction["payee_account_id"] as! String,
sender: transaction["sender_account_id"] as! String,
transDate: transaction["trans_date"] as! String,
amount: transaction["amount"] as! String,
isSuccessfully: Bool((transaction["isSuccessfully"] as! String)) ?? true)
GetTransactions.transactions.append(cl)
}
}
}
And you call it using,
GetTransactions().getTransactions {
clientsCount = GetTransactions.transactions.count
//reload your tableView here
}
Related
I am having two ViewControllers, one called ComposingOverviewViewController and another one called CheapestViewController.
If the User presses a Button, he/ she ;) will be shown the CheapestViewController. If the user presses "Choose", cheapestBoolbecomes true. The selection made should be stored in Firebase, with the bool set as "true", so it can be checked afterwards, if the selection should be shown or not. Even when I check it while saving the data to firebase, Xcode tells me that the value is actually "true". But in Firebase it gets stored as "false".
I have to say, that to actually show something in CheapestViewController, the User has first to press the Button check Values. Since I am aware, that as soon as the View switches back again from CheapestViewControllerto ComposingOverviewViewControllerthe generated Selection gets lost. So as soon as the User presses "Save" in the ComposingOverviewController the selection gets generated again.
Maybe it has to do something with this.
I deleted as much unnecessary code as possible.
Hope that's okay for everyone!
But I actually don't have any clue.
Maybe someone can help. :)
class ComposingOVerviewViewController: UIViewController {
#IBOutlet weak var cheapestImageView: UIImageView!
var individualimageViewBool = false
var cheapestimageviewBool = false
var efficientImageViewBool = false
var bestImageViewBool = false
var overAllBestImageViewBool = false
var DollarJPerfor:Double?
var projectCreated:String?
let uid = User.current.uid
var indivCompValue = [WeighScore]()
var projCompValue = [ProjCompValue]()
var projectBudget:Int = 0
var projectDifficulty:Double = 0
var projectCreativity:Double = 0
var finalCheapest: [WeighScore] = []
var cheapestBool:Bool?
var freelancerArray = [WeighScore]()
var jobAmountNeeded = [JobNeeded]()
override func viewDidLoad() {
super.viewDidLoad()
UserService.projectjobamount(for: User.current, projectCreated: projectCreated ?? "no Value") { (jobNeeded) in
self.jobAmountNeeded = jobNeeded
}
UserService.individualComposingValues(for: User.current) { (indivCompValue) in
self.freelancerArray = indivCompValue
}
projectSpecification(for: User.current, projectCreated: projectCreated ?? "no Value")
imageHighlights()
}
// MARK: - Highlighten the Images when Team selected
func imageHighlights() {
if individualimageViewBool == true {
individualImageView.image = UIImage(named: "Individual-1")
} else {
individualImageView.image = UIImage(named: "Individual") }
if cheapestimageviewBool == true {
cheapestImageView.image = UIImage(named: "cheapesthigh")
cheapestBool = true
}
else { cheapestImageView.image = UIImage(named: "Cheapest") }
if efficientImageViewBool == true {
efficientImageView.image = UIImage(named: "Efficient-1")
}
else { efficientImageView.image = UIImage(named: "Efficient") }
if bestImageViewBool == true {
bestImageView.image = UIImage(named: "Besthighlight") }
else {bestImageView.image = UIImage(named: "Best") }
if overAllBestImageViewBool == true {
overAllBestImageView.image = UIImage(named: "absolutbesthigh") }
else {overAllBestImageView.image = UIImage(named: "absolutbest") }
}
func getCheapest(member: [WeighScore], neededValue: [JobNeeded]) {
var tempArray1: [WeighScore] = []
for jobAmount in jobAmountNeeded {
let neededValue = jobAmount.jobAmount
tempArray1 = freelancerArray.filter { $0.memberJob == jobAmount.jobName}
let amountInArray = tempArray1.count
if neededValue == 0 { return
} else if neededValue == amountInArray{
tempArray1.sort(by: {$0.normalPrice! < $1.normalPrice!})
for value in tempArray1 {
print(value.memberName as Any)
print(tempArray1.count)
finalBest.append(value) }
} else {
tempArray1.sort(by: {$0.normalPrice! < $1.normalPrice!})
let deletedValue = tempArray1.count - neededValue
print("deleted Value: ", deletedValue)
tempArray1.remove(0..<deletedValue)
for value in tempArray1 {
print(value.memberName as Any)
print(tempArray1.count)
finalCheapest.append(value)
}
}
}
}
// MARK: - Button Function to check Values only
#IBAction func checkValues(_ sender: Any) {
getBest(member: freelancerArray, neededValue: jobAmountNeeded, budgetLevel: projectBudget)
getAbsolutBest(member: freelancerArray, neededValue: jobAmountNeeded)
getMostEfficient(member: freelancerArray, neededValue: jobAmountNeeded)
getCheapest(member: freelancerArray, neededValue: jobAmountNeeded)
}
// MARK: - Save the Team/ Teams
#IBAction func saveAction(_ sender: Any) {
print(cheapestBool)
getCheapest(member: freelancerArray, neededValue: jobAmountNeeded)
for value in finalCheapest {
let finalCheapest = "finalCheapest"
PostService.proposedTeams(projectCreated: projectCreated ?? "No Value", teamType: finalCheapest, memberJob: value.memberJob ?? "no value", memberName: value.memberName ?? "no Value", memberID: value.memberID ?? "no Value", ajp: value.ajp ?? 999, crea: value.crea ?? 999, ijr: value.ijr ?? 999, qpa: value.qpa ?? 999, coj: value.coj ?? 999, los: value.los ?? 999, iracc: value.iracc ?? 999, dph: value.dph ?? 999, normalPrice: value.normalPrice ?? 999, sumFreelanceRating: value.sumFreelanceRating ?? 999, priceJPerfScore: value.priceJPerfScore ?? 999, teamSelected: cheapestBool ?? false)
}
}
// MARK: - Pass data to specific ViewController of Team Alternatives
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "teamToCheapest" {
let destVC = segue.destination as! CheapestViewController
print(sentcheapestTeam.count)
destVC.finalCheapest = finalCheapest
}
}
// MARK: - Go to specific Teams
#IBAction func cheapest(_ sender: Any) {
performSegue(withIdentifier: "teamToCheapest", sender: self)
}
}
// MARK: - Extensions
extension Array {
mutating func remove(_ range: Range<Int>) -> Array {
let values = Array(self[range])
self.removeSubrange(range)
return values
}
}
class CheapestViewController: UIViewController {
// MARK: - Properties
#IBOutlet weak var tableView: UITableView!
// var VC = ComposingOVerviewViewController()
var finalCheapest = [WeighScore]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "cheapestToOverview" {
let destVC = segue.destination as! ComposingOVerviewViewController
destVC.cheapestBool = true
}
}
#IBAction func chooseCheapest(_ sender: Any) {
print(finalCheapest.count)
for value in finalCheapest {
print(value.memberJob)
}
performSegue(withIdentifier: "cheapestToOverview", sender: self)
}
}
class WeighScore {
var key: String?
let memberJob: String?
let memberName: String?
let memberID:String?
let ajp: Double?
let crea:Double?
let ijr:Double?
let qpa:Double?
let coj:Double?
let los:Double?
let iracc:Double?
let dph:Double?
let normalPrice:Double?
var sumFreelanceRating:Double?
let priceJPerfScore:Double?
init(ajp: Double, crea:Double, ijr:Double, qpa:Double, coj:Double, los:Double, iracc:Double, dph:Double, sumFreelanceRating:Double, normalPrice:Double, memberJob: String, memberName: String, priceJPerfScore:Double, memberID:String) {
self.ajp = ajp
self.crea = crea
self.ijr = ijr
self.qpa = qpa
self.coj = coj
self.los = los
self.iracc = iracc
self.dph = dph
self.sumFreelanceRating = sumFreelanceRating
self.normalPrice = normalPrice
self.memberName = memberName
self.memberJob = memberJob
self.priceJPerfScore = priceJPerfScore
self.memberID = memberID
}
var dictValue: [String: Any] {
return ["memberName" : memberName,
"memberJob" : memberJob,
"ajp" : ajp,
"crea" : crea,
"ijr" : ijr,
"qpa" : qpa,
"coj" : coj,
"los" : los,
"iracc" : iracc,
"dph" : dph,
"normalPrice": normalPrice,
"sumFreelanceRating" : sumFreelanceRating,
"priceJPerfScore": priceJPerfScore,
"memberID": memberID,
]
}
init?(snapshot: DataSnapshot) {
guard let dict = snapshot.value as? [String: Any],
let memberJob = dict["memberJob"] as? String,
let memberName = dict["memberName"] as? String,
let ajp = dict["ajp"] as? Double,
let crea = dict["crea"] as? Double,
let ijr = dict["ijr"] as? Double,
let qpa = dict["qpa"] as? Double,
let coj = dict["coj"] as? Double,
let los = dict["los"] as? Double,
let iracc = dict["iracc"] as? Double,
let dph = dict["dph"] as? Double,
let normalPrice = dict["normalPrice"] as? Double,
let sumFreelanceRating = dict["sumFreelanceRating"] as? Double,
let priceJPerfScore = dict["priceJPerfScore"] as? Double,
let memberID = dict["memberID"] as? String
else {return nil}
self.memberJob = memberJob
self.memberName = memberName
self.ajp = ajp
self.crea = crea
self.ijr = ijr
self.qpa = qpa
self.coj = coj
self.los = los
self.iracc = iracc
self.dph = dph
self.normalPrice = normalPrice
self.sumFreelanceRating = sumFreelanceRating
self.priceJPerfScore = priceJPerfScore
self.memberID = memberID
}
}
class TeamSetting {
var key: String?
let memberJob: String?
let memberName: String?
let memberID:String?
let ajp: Double?
let crea:Double?
let ijr:Double?
let qpa:Double?
let coj:Double?
let los:Double?
let iracc:Double?
let dph:Double?
let normalPrice:Double?
var sumFreelanceRating:Double?
let priceJPerfScore:Double?
var teamSelected: Bool?
init(ajp: Double, crea:Double, ijr:Double, qpa:Double, coj:Double, los:Double, iracc:Double, dph:Double, sumFreelanceRating:Double, normalPrice:Double, memberJob: String, memberName: String, priceJPerfScore:Double, memberID:String, teamSelected: Bool) {
self.ajp = ajp
self.crea = crea
self.ijr = ijr
self.qpa = qpa
self.coj = coj
self.los = los
self.iracc = iracc
self.dph = dph
self.sumFreelanceRating = sumFreelanceRating
self.normalPrice = normalPrice
self.memberName = memberName
self.memberJob = memberJob
self.priceJPerfScore = priceJPerfScore
self.memberID = memberID
self.teamSelected = teamSelected
}
var dictValue: [String: Any] {
return ["memberName" : memberName,
"memberJob" : memberJob,
"ajp" : ajp,
"crea" : crea,
"ijr" : ijr,
"qpa" : qpa,
"coj" : coj,
"los" : los,
"iracc" : iracc,
"dph" : dph,
"normalPrice": normalPrice,
"sumFreelanceRating" : sumFreelanceRating,
"priceJPerfScore": priceJPerfScore,
"memberID": memberID,
"teamSelected": teamSelected]
}
init?(snapshot: DataSnapshot) {
guard let dict = snapshot.value as? [String: Any],
let memberJob = dict["memberJob"] as? String,
let memberName = dict["memberName"] as? String,
let ajp = dict["ajp"] as? Double,
let crea = dict["crea"] as? Double,
let ijr = dict["ijr"] as? Double,
let qpa = dict["qpa"] as? Double,
let coj = dict["coj"] as? Double,
let los = dict["los"] as? Double,
let iracc = dict["iracc"] as? Double,
let dph = dict["dph"] as? Double,
let normalPrice = dict["normalPrice"] as? Double,
let sumFreelanceRating = dict["sumFreelanceRating"] as? Double,
let priceJPerfScore = dict["priceJPerfScore"] as? Double,
let memberID = dict["memberID"] as? String,
let teamSelected = dict["teamSelected"] as? Bool
else {return nil}
self.memberJob = memberJob
self.memberName = memberName
self.ajp = ajp
self.crea = crea
self.ijr = ijr
self.qpa = qpa
self.coj = coj
self.los = los
self.iracc = iracc
self.dph = dph
self.normalPrice = normalPrice
self.sumFreelanceRating = sumFreelanceRating
self.priceJPerfScore = priceJPerfScore
self.memberID = memberID
self.teamSelected = teamSelected
}
}
There's no firebase code in the question so answering this question
Swift Firebase doesn't store Bool?
is not possible.
There's a lot of code in the question but it appears this is a Master->Detail setup (?) with the master viewController being ComposingOVerviewViewController and the detail being CheapestViewController. If so one issue appears to be that the detail viewController is spawning a new master?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "cheapestToOverview" {
let destVC = segue.destination as! ComposingOVerviewViewController
destVC.cheapestBool = true
}
}
This fails (Non-nominal type 'Any' cannot be extended)
extension Any {
func literal() -> String {
if let booleanValue = (self as? Bool) {
return String(format: (booleanValue ? "true" : "false"))
}
else
if let intValue = (self as? Int) {
return String(format: "%d", intValue)
}
else
if let floatValue = (self as? Float) {
return String(format: "%f", floatValue)
}
else
if let doubleValue = (self as? Double) {
return String(format: "%f", doubleValue)
}
else
{
return String(format: "<%#>", self)
}
}
}
as I would like to use it in a dictionary (self) to xml string factory like
extension Dictionary {
// Return an XML string from the dictionary
func xmlString(withElement element: String, isFirstElement: Bool) -> String {
var xml = String.init()
if isFirstElement { xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n") }
xml.append(String(format: "<%#>\n", element))
for node in self.keys {
let value = self[node]
if let array: Array<Any> = (value as? Array<Any>) {
xml.append(array.xmlString(withElement: node as! String, isFirstElemenet: false))
}
else
if let dict: Dictionary<AnyHashable,Any> = (value as? Dictionary<AnyHashable,Any>) {
xml.append(dict.xmlString(withElement: node as! String, isFirstElement: false))
}
else
{
xml.append(String(format: "<%#>", node as! CVarArg))
xml.append((value as Any).literal
xml.append(String(format: "</%#>\n", node as! CVarArg))
}
}
xml.append(String(format: "</%#>\n", element))
return xml.replacingOccurrences(of: "&", with: "&", options: .literal, range: nil)
}
}
I was trying to reduce the code somehow, as the above snippet is repeated a few times in a prototype I'm building but this is not the way to do it (a working copy with the snippet replicated works but ugly?).
Basically I want to generate a literal for an Any value - previously fetched from a dictionary.
It seems like you can't add extensions to Any. You do have some other options though - either make it a function toLiteral(value: Any) -> String, or what is probably a neater solution; use the description: String attribute which is present on all types that conform to CustomStringConvertible, which includes String, Int, Bool, and Float - your code would be simplified down to just xml.append(value.description). You then just have make a simple implementation for any other types that you might get.
Ok, finally got this working. First the preliminaries: each of your objects needs to have a dictionary() method to marshal itself. Note: "k.###" are struct static constants - i.e., k.name is "name", etc. I have two objects, a PlayItem and a PlayList:
class PlayItem : NSObject {
var name : String = k.item
var link : URL = URL.init(string: "http://")!
var time : TimeInterval
var rank : Int
var rect : NSRect
var label: Bool
var hover: Bool
var alpha: Float
var trans: Int
var temp : String {
get {
return link.absoluteString
}
set (value) {
link = URL.init(string: value)!
}
}
func dictionary() -> Dictionary<String,Any> {
var dict = Dictionary<String,Any>()
dict[k.name] = name
dict[k.link] = link.absoluteString
dict[k.time] = time
dict[k.rank] = rank
dict[k.rect] = NSStringFromRect(rect)
dict[k.label] = label ? 1 : 0
dict[k.hover] = hover ? 1 : 0
dict[k.alpha] = alpha
dict[k.trans] = trans
return dict
}
}
class PlayList : NSObject {
var name : String = k.list
var list : Array <PlayItem> = Array()
func dictionary() -> Dictionary<String,Any> {
var dict = Dictionary<String,Any>()
var items: [Any] = Array()
for item in list {
items.append(item.dictionary())
}
dict[k.name] = name
dict[k.list] = items
return dict
}
}
Note any value so marshal has to be those legal types for a dictionary; it helps to have aliases so in the PlayItem a "temp" is the string version for the link url, and its getter/setter would translate.
When needed, like the writeRowsWith drag-n-drop tableview handler, I do this:
func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
if tableView == playlistTableView {
let objects: [PlayList] = playlistArrayController.arrangedObjects as! [PlayList]
var items: [PlayList] = [PlayList]()
var promises = [String]()
for index in rowIndexes {
let item = objects[index]
let dict = item.dictionary()
let promise = dict.xmlString(withElement: item.className, isFirstElement: true)
promises.append(promise)
items.append(item)
}
let data = NSKeyedArchiver.archivedData(withRootObject: items)
pboard.setPropertyList(data, forType: PlayList.className())
pboard.setPropertyList(promises, forType:NSFilesPromisePboardType)
pboard.writeObjects(promises as [NSPasteboardWriting])
}
else
{
let objects: [PlayItem] = playitemArrayController.arrangedObjects as! [PlayItem]
var items: [PlayItem] = [PlayItem]()
var promises = [String]()
for index in rowIndexes {
let item = objects[index]
let dict = item.dictionary()
let promise = dict.xmlString(withElement: item.className, isFirstElement: true)
promises.append(promise)
items.append(item)
}
let data = NSKeyedArchiver.archivedData(withRootObject: items)
pboard.setPropertyList(data, forType: PlayList.className())
pboard.setPropertyList(promises, forType:NSFilesPromisePboardType)
pboard.writeObjects(promises as [NSPasteboardWriting])
}
return true
}
What makes this happen are these xmlString extensions and the toLiteral function - as you cannot extend "Any":
func toLiteral(_ value: Any) -> String {
if let booleanValue = (value as? Bool) {
return String(format: (booleanValue ? "1" : "0"))
}
else
if let intValue = (value as? Int) {
return String(format: "%d", intValue)
}
else
if let floatValue = (value as? Float) {
return String(format: "%f", floatValue)
}
else
if let doubleValue = (value as? Double) {
return String(format: "%f", doubleValue)
}
else
if let stringValue = (value as? String) {
return stringValue
}
else
if let dictValue: Dictionary<AnyHashable,Any> = (value as? Dictionary<AnyHashable,Any>)
{
return dictValue.xmlString(withElement: "Dictionary", isFirstElement: false)
}
else
{
return ((value as AnyObject).description)
}
}
extension Array {
func xmlString(withElement element: String, isFirstElemenet: Bool) -> String {
var xml = String.init()
xml.append(String(format: "<%#>\n", element))
self.forEach { (value) in
if let array: Array<Any> = (value as? Array<Any>) {
xml.append(array.xmlString(withElement: "Array", isFirstElemenet: false))
}
else
if let dict: Dictionary<AnyHashable,Any> = (value as? Dictionary<AnyHashable,Any>) {
xml.append(dict.xmlString(withElement: "Dictionary", isFirstElement: false))
}
else
{
xml.append(toLiteral(value))
}
}
xml.append(String(format: "<%#>\n", element))
return xml
}
}
extension Dictionary {
// Return an XML string from the dictionary
func xmlString(withElement element: String, isFirstElement: Bool) -> String {
var xml = String.init()
if isFirstElement { xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n") }
xml.append(String(format: "<%#>\n", element))
for node in self.keys {
let value = self[node]
if let array: Array<Any> = (value as? Array<Any>) {
xml.append(array.xmlString(withElement: node as! String, isFirstElemenet: false))
}
else
if let dict: Dictionary<AnyHashable,Any> = (value as? Dictionary<AnyHashable,Any>) {
xml.append(dict.xmlString(withElement: node as! String, isFirstElement: false))
}
else
{
xml.append(String(format: "<%#>", node as! CVarArg))
xml.append(toLiteral(value as Any))
xml.append(String(format: "</%#>\n", node as! CVarArg))
}
}
xml.append(String(format: "</%#>\n", element))
return xml
}
func xmlHTMLString(withElement element: String, isFirstElement: Bool) -> String {
let xml = self.xmlString(withElement: element, isFirstElement: isFirstElement)
return xml.replacingOccurrences(of: "&", with: "&", options: .literal, range: nil)
}
}
This continues another's solution, the toLiteral() suggestion above, in hopes it helps others.
Enjoy.
This would be a super noob question but I have no idea how to convert URL to data and data to UIimage. The text labels are work really beautifully but the UI images are not working
I figured out there are some codes to convert them but don't know how to use them. could you help me out.
override func viewDidLoad() {
super.viewDidLoad()
let manager = AFHTTPSessionManager()
let url = mainURL + "sample.php"
manager.get(url, parameters: nil, progress: nil, success: { (task, res) in
guard let json = res as? [String: Any] else {
print ("not [String: Any]]")
return
}
if let array = json["data"] as? [Any] {
for i in 0 ..< array.count {
if let row = array [ i ] as? [String: Any] {
let model = Model(t: row ["title"] as! String,
n: row ["user"] as! String,
d: row ["regdate"] as! String,
imgUrl: row ["img"] as! String)
self.models.append(model)
}
self.tableView.reloadData()
}
}
}) { (task, error) in
print("error = \(error)")
}
self.tableView.estimatedRowHeight = 500
}
Model
class Model : NSObject {
//?
let data = try? Data(contetsOf: url!)
let img = UIImage(data: data!)
let imgView = UIImageView(image: img)
var title : String
var name : String
var date : String
var image : UIImage!
init(t: String, n: String, d: String, imgUrl: String){
title = t
name = n
date = d
image = UIImage(named: imgUrl)
}
and the outlets.
mainCell.titleLabel.text = m.title //labels work fine
mainCell.nameLabel.text = m.name
mainCell.mainImgView.image = m.image
Try this
Just store image url in model class not image. Update your model class with below code
class Model : NSObject {
var title : String
var name : String
var date : String
var image : String
init(t: String, n: String, d: String, imgUrl: String){
title = t
name = n
date = d
image = imgUrl
}
}
// past code inside cellforRowAt
// download image from image URL
let url = URL(string: image)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
mainCell.mainImgView.image = UIImage(data: data!)
}
}).resume()
I need to initialize an object, and pass it through a prepareforsegue to another class.
Last line of the code below throws "Contextual type 'FPChat!.Type' cannot be used with dictionary literal"
if (segue.identifier == "chatmessages") {
let vc = segue.destination as! FPChatMessageViewController
//vc.currentChat = fPChat
}
}
fPchat = FPChat?
// Start the Chat
#IBAction func Chat(_ sender: UIButton) {
// Create a new entry in chats. This variable is passed with prepareforsegue
let chatRef = ref.child("chats").childByAutoId()
let chatId = chatRef.key
//fPchat = FPChat?
let fPchat = FPChat.currentChat(currentChatID: chatId)
Below chat class:
import Firebase
class FPChat {
var chatID = ""
var chatDate: Date!
var text = ""
var messages: [FPChatMessage]!
var author: FPUser!
var mine = true
// Calling FPChat.currentChat(id) I have back the FPChat object
static func currentChat(currentChatID: String) -> FPChat {
return FPChat(chatID: currentChatID)
}
private init(chatID: String) {
self.chatID = chatID
}
init(snapshot: DataSnapshot, andMessages messages: [FPChatMessage]) {
guard let value = snapshot.value as? [String: Any] else { return }
self.chatID = snapshot.key
if let text = value["text"] as? String {
self.text = text
}
guard let timestamp = value["timestamp"] as? Double else { return }
self.chatDate = Date(timeIntervalSince1970: (timestamp / 1_000.0))
guard let author = value["author"] as? [String: String] else { return }
self.author = FPUser(dictionary: author)
self.messages = messages
self.mine = self.author.userID == Auth.auth().currentUser?.uid
}
}
What I am doing wrong?
I am having a hard time trying to figure out, how I can change/update a specific part of my firebase database through swift. To give you an example of how my firebase database is structured, here you have a photo:
I am trying to update the likesForPost +1 everytime someone hits the like button that I have in my tableViewController. The important part is that every likesForPost should not be updates, just the one where the button is. I hope you understand my situation and that you can help me :-)
My struct
struct Sweet {
let key: String!
let content: String!
let addedByUser: String!
let profilePhoto: String!
var likesForPost: String!
let itemRef: FIRDatabaseReference?
init (content: String, addedByUser: String, profilePhoto: String!, likesForPost: String!, key: String = "") {
self.key = key
self.content = content
self.addedByUser = addedByUser
self.profilePhoto = profilePhoto
self.likesForPost = likesForPost
self.itemRef = nil
}
init (snapshot: FIRDataSnapshot) {
key = snapshot.key
itemRef = snapshot.ref
if let theFeedContent = snapshot.value!["content"] as? String {
content = theFeedContent
} else {
content = ""
}
if let feedUser = snapshot.value!["addedByUser"] as? String {
addedByUser = feedUser
} else {
addedByUser = ""
}
if let feedPhoto = snapshot.value!["profilePhoto"] as? String! {
profilePhoto = feedPhoto
} else {
profilePhoto = ""
}
if let feedLikes = snapshot.value!["likesForPost"] as? String! {
likesForPost = feedLikes
} else {
likesForPost = "0"
}
}
func toAnyObject() -> AnyObject {
return ["content":content, "addedByUser":addedByUser, "profilePhoto":profilePhoto!, "likesForPost":likesForPost]
}
}
My UITableViewController
import UIKit
import FirebaseDatabase
import FirebaseAuth
import FBSDKCoreKit
class feedTableViewController: UITableViewController {
#IBOutlet weak var loadingSpinner: UIActivityIndicatorView!
var facebookProfileUrl = ""
var dbRef: FIRDatabaseReference!
var updates = [Sweet]()
override func viewDidLoad() {
super.viewDidLoad()
loadingSpinner.startAnimating()
dbRef = FIRDatabase.database().reference().child("feed-items")
startObersvingDB()
}
func startObersvingDB() {
dbRef.observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
var newUpdates = [Sweet]()
for update in snapshot.children {
let updateObject = Sweet(snapshot: update as! FIRDataSnapshot)
newUpdates.append(updateObject)
}
self.updates = newUpdates
self.tableView.reloadData()
}) { (error: NSError) in
print(error.description)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func addToFeed(sender: AnyObject) {
let feedAlert = UIAlertController(title: "New update", message: "Enter your update", preferredStyle: .Alert)
feedAlert.addTextFieldWithConfigurationHandler { (textField:UITextField) in
textField.placeholder = "Your update"
}
feedAlert.addAction(UIAlertAction(title: "Send", style: .Default, handler: { (action:UIAlertAction) in
if let feedContent = feedAlert.textFields?.first?.text {
if let user = FIRAuth.auth()?.currentUser {
let name = user.displayName
//let photoUrl = user.photoURL
let accessToken = FBSDKAccessToken.currentAccessToken()
if(accessToken != nil) //should be != nil
{
let req = FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"id"], tokenString: accessToken.tokenString, version: nil, HTTPMethod: "GET")
req.startWithCompletionHandler({ (connection, result, error : NSError!) -> Void in
if(error == nil)
{
let userId: String! = result.valueForKey("id") as? String!
let userID = userId
self.facebookProfileUrl = "http://graph.facebook.com/\(userID)/picture?type=large"
let likes = "0"
let feed = Sweet(content: feedContent, addedByUser: name!, profilePhoto: self.facebookProfileUrl, likesForPost: likes)
let feedRef = self.dbRef.child(feedContent.lowercaseString)
feedRef.setValue(feed.toAnyObject())
}
else
{
print("error \(error)")
}
})
}
// LAV FEEDCONTENT OM TIL OGSÅ AT MODTAGE PROFIL BILLEDE URL I STRING OG GIV SÅ facebookProfileUrl STRING LIGE HERUNDER I feed
} else {
// No user is signed in.
}
}
}))
self.presentViewController(feedAlert, animated: true, completion: nil)
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return updates.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:updateTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! updateTableViewCell
let update = updates[indexPath.row]
//cell.textLabel?.text = update.content
//cell.detailTextLabel?.text = update.addedByUser
cell.nameLabel.text = update.addedByUser
cell.updateLabel.text = update.content
cell.likesLabel.text = "\(update.likesForPost) hi-fives"
if update.profilePhoto! != "" {
if let url = NSURL(string: update.profilePhoto!) {
if let data = NSData(contentsOfURL: url) {
cell.picView.image = UIImage(data: data)
cell.picView.layer.cornerRadius = cell.picView.frame.size.width/2
cell.picView.clipsToBounds = true
}
}
} else {
print("Empty facebookProfileUrl")
}
loadingSpinner.stopAnimating()
return cell
}
}
Modify your struct to include one more variable (lets say let path : String!)that will include the value of the node key retrieved from your DB(megaTest or test).
Your Struct
struct Sweet {
let key: String!
let content: String!
let addedByUser: String!
let profilePhoto: String!
var likesForPost: String!
let itemRef: FIRDatabaseReference?
let path : String!
init (content: String, addedByUser: String, profilePhoto: String!, likesForPost: String!, key: String = "",dataPath : String!) {
self.key = key
self.content = content
self.addedByUser = addedByUser
self.profilePhoto = profilePhoto
self.likesForPost = likesForPost
self.itemRef = nil
self.path = dataPath
}
init (snapshot: FIRDataSnapshot) {
key = snapshot.key
itemRef = snapshot.ref
path = key
if let theFeedContent = snapshot.value!["content"] as? String {
content = theFeedContent
} else {
content = ""
}
if let feedUser = snapshot.value!["addedByUser"] as? String {
addedByUser = feedUser
} else {
addedByUser = ""
}
if let feedPhoto = snapshot.value!["profilePhoto"] as? String! {
profilePhoto = feedPhoto
} else {
profilePhoto = ""
}
if let feedLikes = snapshot.value!["likesForPost"] as? String! {
likesForPost = feedLikes
} else {
likesForPost = "0"
}
}
func toAnyObject() -> AnyObject {
return ["content":content, "addedByUser":addedByUser, "profilePhoto":profilePhoto!, "likesForPost":likesForPost,"pathInTheDB" : path]
}
}
In cellForIndexPath just add this
cell. pathDB = self.structArray![indexPath.row].path
Modify your customCell class like this
class customTableViewCell : UITableViewCell{
var pathDB : String! //megaTest or test
#IBAction func likeBtn(sender : UIButton!){
//Update like's
}
}
For updating the value you can use either runTransactionBlock:-
FIRDatabase.database().reference().child(pathDB).child("likesForPost").runTransactionBlock({ (likes: FIRMutableData) -> FIRTransactionResult in
// Set value and report transaction success
likes.value = likes.value as! Int + 1
return FIRTransactionResult.successWithValue(likes)
}) { (err, bl, snap) in
if let error = error {
print(error.localizedDescription)
}
}
Or observe that node with .observeSingleEventOfType, retrieve the snap and then update
let parentRef = FIRDatabase.database().reference().child(pathDB).child("likesForPost")
parentRef.observeSingleEventOfType(.Value,withBlock : {(snap) in
if let nOfLikes = snap.value as? Int{
parentRef.setValue(nOfLikes+1)
}
})