How to compare json array values? Can't understand the logic.
I need to show a price change of cryptocurrencies (increase or decrease) in tableView, while updating data from server https://api.coinmarketcap.com/v1/ticker/
As I understand I have to create two arrays: one for the old values and second for new ones, and function for comparison, where two arrays initially should be equal. What should I do next? Am I in right direction?
var currencies = [Cryptocurrency]()
var newCurrencies = [Cryptocurrency]()
I'm getting the array of data through NSNotificationCenter. Should I change something here?
NotificationCenter.default.addObserver(self, selector: #selector(getCurrencies), name: Notificator.getCurrencyNotification, object: nil)
#objc func getCurrencies(notification: Notification) {
if let receivedCurrencies = notification.object as? [Cryptocurrency] {
self.currencies = receivedCurrencies
AnimatableReload.reload(tableView: tableView, animationDirection: "up")
}
}
func showPriceChanges() {
currencies = newCurrencies
?
}
Related
I just wanna get all the records from the transaction variable and save it to an array.i tried and all I am getting is this constant error. please help me, I just wanna all models(records) to be saved on an array.
Type '()' cannot conform to 'View'
#State private var transactions: [Transactions] = [Transactions]()
ForEach(transactions, id: \.self) { transaction in
timexxx[0] = transaction.timexx ?? "0"
Text(timexxx[0] ?? "0")
}
enter image description here
Like what #multiverse has suggested
ForEach loop expects some sort of View but you are giving it or attempting to give an "array" (it only wants View)
Here is an updated code where you give the ForEach what it wants and you append to your timexxx array
ForEach(Array(transactions.enumerated()), id: \.offset) { (offset, transaction) in
Text(transaction.timexx ?? "\(offset)")
.onAppear {
timexxx[offset] = transaction.timexx ?? "\(offset)"
}
}
Update
for your question
"how do I do this with a simple "For" loop ? let's say I wanna do this operation in a simple class."
This is how it's done.
I removed the view Text.
for (i, transaction) in transactions.enumerated() {
timexxx[i] = transaction.timexx ?? "0"
}
Ok , so this is an error i faced as well, when i was learning SwiftUI( i am still a beginner), so now we need to understand , what does this error actually means, in this case the ForEach loop expects some sort of View but you are giving it or attempting to give an "array" (it only wants View)....
If you want values to be transferred to an array just simply create a function and do it .....
say you have a class and inside of which you do
#Published var song = [Song]()
then what you do is inside a function like loadData()
objects is the array whose elements you want transferred and most likely those elements belong to a Struct like Song here(if not its even simpler just use what ever type it has like Int, String etc.), this way all your elements will get transferred to song from objects
func loadData() {
song = objects.map {
artist in
Song(album: artist.album, artistImage: artist.artistImage)
}
}
Here i add the simplest possible way to transfer from one array to other
var objects = [1,2,3,4,5]
var song = [Int]()
func loadData() {
song = objects.map { $0 }
}
loadData()
print(song)
//[1,2,3,4,5]
Looking at the structure of my firebase database:
I am attempting to save the values for every key for every cryptocurrency into separate arrays.
For example, the 'coinAmount' key, I would like to get an array looking like ["1.0,"2.0"], and 'coinName' as ["Bitcoin","Ethereum"] etc for each of the 5 keys.
My attempt at this:
let index = NSIndexPath(item: 0, section: 0)
if portfolioCoinFullNameString.count > 0 {
print("PortfolioVC: Number of Coins in Portfolio > 0")
let coinRef = ref.child("\(portfolioCoinFullNameString[index.row])")
coinRef.observe(DataEventType.value) { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let key = snap.key
let value = snap.value
print("key = \(key) ,, value = \(value!)")
if key == "coinAmount" {
self.firebaseCoinAmountArray.append(Double(value as! String)!)
} else if key == "coinName" {
self.firebaseCoinNameArary.append(value as! String)
} else if key == "coinPrice" {
self.firebaseCoinPriceArray.append(Double(value as! String)!)
} else if key == "coinSymbol" {
self.firebaseCoinSymbolArray.append(value as! String)
} else if key == "coinTotalFiat" {
self.firebaseCoinTotalFiatArray.append(Double(value as! String)!)
} else {
print("PortfolioVC: Key does not match anything!")
}
}
}
} else {
print("PortfolioVC: Number of Coins in Portfolio !> 0")
}
This works for adding the first coin, but when i attempt to add the second one the 2 values are appended to the array so it contains 3, and if i add a third coin it append 3 values. Not sure why its looping through each coin?
EDIT:
I searched around StackOverflow and came across another method:
let index = NSIndexPath(item: 0, section: 0)
if portfolioCoinFullNameString.count > 0 {
print("PortfolioVC: Number of Coins in Portfolio > 0")
let coinRef = ref.child("\(portfolioCoinFullNameString[index.row])")
coinRef.observe(DataEventType.value) { (snapshot) in
let value = snapshot.value as? NSDictionary
let coinName = value?["coinName"] as? String ?? ""
self.firebaseCoinNameArary.append(coinName)
print("aaaaaa:\(self.firebaseCoinNameArary)")
}
} else {
print("PortfolioVC: Number of Coins in Portfolio !> 0")
}
But still have the issue of when adding any coin as second element in the array, the array contains 3x first coin added instead of just two entires of the first coin and second coin. I know i could do an if statement to check if the array already contains the name and if it does dont add it, but how would i do this with the number values as theres the possibility coins could have the same price.??
Edit2: Been thinking about it, would it work if i just observe the values and not for changes, save all of the to Arrays, populate the cells with them. Then if a user edits the 'coinAmount' value in the app, it updates in Firebase, but not the arrays. Then, upon user closing app/logging out, next time portfolio is shown it will re-pull the values from firebase which contains the updated numbers?
Edit3: Also, the nodes under portfolio called 'Bitcoin' etc, if i called them coin1, coin2, coin3 etc, how would i get that path and get the key and values for say coin3? Is there a way to just get the values for All the nodes under 'protfolio' in one go?
The idea is once I save all the firebase data to the arrays i will populate a tableview cell with all of the data.
Any help is appreciated :)
Thanks,
Jeremy
In order to do this don't use a for-loop use .childAdded
I will provide an example
database.ADD THE ENTIRE PATH.child("portfolio").observe(DataEventType.childadded) { (snapshot) in
let key = snapshot.key
let value = snapsnapshot.value as? NSDictionary
//Add everything that is in your firebase
let coinAmount = value?["coinAmount"] as? String
let cointName = value?["coinName"] as? String
//.... Added all the variables for the database
//use the variables above and append the array and it will work!
}
}
I have some user comments stored in a database (parse-server) that I would like to would like to display on my viewController's viewDidLoad(). I can easily pull the comment objects as follows:
super.viewDidLoad()
func query(){
let commentsQuery = PFQuery(className: "Comments")
commentsQuery.whereKey("objectId", equalTo: detailDisclosureKey)
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
if objects.count == 1{
for object in objects{
self.unOrderedComments.append(object)
}
}
}
}
}
This query dumps all of the of the comments in the unOrederedComments array. Each comment is added to the database with a createdAt property automatically being added relating the exact time of its creation. This property is a string with (as an example) the form: "2017-08-13T19:31:47.776Z" (the Z at the end is at the end of every string... not exactly sure why its there but its constant). Now, each new comment is added in order to the top of database and thus any queried result should be in order regardless. However, I would like to make sure of this by reordering it if necessary. My general thought process is to use .sorted, but I cannot figure out how to apply this to my situation
func orderComments(unOrderComments: [PFObject]) -> [PFObject]{
let orderedEventComments = unOrderedEventComments.sorted(by: { (<#PFObject#>, <#PFObject#>) -> Bool in
//code
})
}
This is the generic set up but I cannot, despite looking up several examples online figure out what to put in the <#PFObject#>'s and in the //code. I want to order them based on the "createdAt" property but this is not achieved via dot notation and instead requires PFObject["createdAt"] and using this notation keeps leading to error. I feel as so though I may need to set up a custom predicate but I do not know how to do this.
I was in the same situation, what I did was to first create an array of structs with the data I downloaded where I turned the string createdAt into a Date, then used this function:
dataArrayOrdered = unOrderedArray.sorted(by: { $0.date.compare($1.date) == .orderedAscending})
(.date being the stored Date inside my array of strcuts)
Try this code, notice that I assumed you have a variable name called ["Comments"] inside your Parse database, so replace if necessary. Also, I realised that createdAt it's in Date format, so there was no need to change it from String to Date, chek if it works the same for you, if it doesn't refer to this: Swift convert string to date.
struct Comment {
var date = Date()
var comment = String()
}
var unOrderedComments: [Comment] = []
var orderedComments = [Comment]()
override func viewDidLoad() {
super.viewDidLoad()
query()
}
func query(){
let commentsQuery = PFQuery(className: "Comments")
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
if objects.count >= 1{
for object in objects{
let newElement = Comment(date: object.createdAt!, comment: object["Comments"] as! String)
self.unOrderedComments.append(newElement)
print(self.unOrderedComments)
}
}
self.orderedComments = self.unOrderedComments.sorted(by: { $0.date.compare($1.date) == .orderedAscending})
print(self.orderedComments)
}
}
}
I need to put data in the array using a query, but it only works in the block. I searched here and found out that it's because the findObjectsInBackgroundWithBlock is asynchronous, but how can I make it synchronous then?
var cities = [String]()
func loadCityArray() {
let citiesVisited = PFQuery(className: "Trips")
citiesVisited.whereKey("userId", equalTo: (PFUser.currentUser()?.objectId)!)
citiesVisited.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
for object in objects {
let city = object["cityId"] as! String
let query = PFQuery(className: "Cities")
query.whereKey("objectId", equalTo: city)
query.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for object in objects {
self.cities.append(object["cityName"] as! String)
}
}
})
}
}
You do not want to make that synchonous on the main thread. That is strongly discouraged.
You rather want to store the things you need from the request in instance variables and tell the corresponding View Controller new values are present in the block.
edit:
Suppose you have an object waiting to use the data:
var chart : DataConsumer?
In the block where you get the data,
chart.useData(data)
edit 2:
the useData function should keep track of changes in the data set and make use of the information that new data arrived. For example, by displaying it.
Well I have been making a test app to continue my swift learning, today I came across a problem.
Basically I have a tableview and a detailview. For my tableview file I have some data that I am currently passing to the detailview, like the name that goes on the navigation bar, one image and some text, this data is stored in arrays on my tableview file, I use "prepareforsegue" to pass this information:
var names = ["name1","name2","name3"]
detailViewController.detailName = names[indexPath.row]
Then in my detailViewController I have variables set for that:
var detailName: String?
Then I use this for stuff, example: naming my navigation bar or setting an image in detailView:
navigationItem.title = detailName!
Now, what I dont get how to do is pass a whole array of information to a variable in my detailViewController. What I want to do is pass an array of images to use it on my detailView. I want to be able to iterate through the array of images with a button, I know how to set that up but I just need to know how to pass the array, right now I am just passing one of the values(one name, one image etc...)
Thanks for the help in advance.
It's not so different from what you have done.
Just add field for images in detailViewController, and then pass images using it. Images could be represented in [UIImage]. However, [String] can be also used for local filenames, and [NSURL] can be used for remote image urls.
code:
In DetailViewController:
var images: [UIImage]? // or var images: [UIImage] = []
In prepareForSegue:
detailViewController.images = YourImages
You seem to be asking for one of two things:
A UIImage array, which you can declare using var imageArray : [UIImage] = [] and then append whatever images you want to pass.
Alternatively, you can pass in a [AnyObject] array and cast its elements to a UIImage or String by doing
var objectArray : [AnyObject] = []
objectArray.append("test")
objectArray.append(UIImage(named: "test.png")!)
if let s = objectArray[0] as? String {
// Do something with the string
}
if let i = objectArray[1] as? UIImage {
// Do something with the image
}
if let s = objectArray[1] as? String {
// The above cast fails, this code block won't be executed
} else {
// ... but this one will
}
in the table view controller file:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get reference to the destination view controller
var detailVC = segue.destinationViewController as! DetailViewController
var detailImages: Array<UIImage> = []
detailImages.append(UIImage(named: "pup.png")!)
detailImages.append(UIImage(named: "cutepuppy.png")!)
detailVC.detailImages = detailImages;
}
and in the detail file:
var detailImages: Array<UIImage>?