Can't add items to array - swift

I'm having some trouble with an array. I created an array called 'coins'
var coins = [Coin]()
then appended objects to it within a function
func getCoinData() {
AF.request("https://min-api.cryptocompare.com/data/top/mktcapfull?limit=10&tsym=USD", encoding: JSONEncoding.default).responseJSON { response in
if let json = response.result.value{
let responseDictionary = json as! [String : Any]
let data = responseDictionary["Data"] as! [Any]
for index in data {
let coin = index as! Dictionary<String, Any>
let coinInfo = coin["CoinInfo"] as! Dictionary<String, Any>
let displayInfo = coin["DISPLAY"] as! Dictionary<String, Any>
let usdDisplayInfo = displayInfo["USD"] as! Dictionary<String, Any>
let name = coinInfo["Name"]
let fullName = coinInfo["FullName"]
let imageUrl = coinInfo["ImageUrl"]
let price = usdDisplayInfo["PRICE"]
let marketCap = usdDisplayInfo["MKTCAP"]
let change24Hr = usdDisplayInfo["CHANGE24HOUR"]
let newCoin = Coin()
if let newCoinName = name, let newCoinFullName = fullName, let newCoinImageUrl = imageUrl, let newCoinPrice = price, let newCoinMarketCap = marketCap, let newCoinChange24hr = change24Hr {
let coinName = newCoinName
let coinFullName = newCoinFullName
let coinImageUrl = newCoinImageUrl
let coinPrice = newCoinPrice
let coinMarketCap = newCoinMarketCap
let coinChange24Hr = newCoinChange24hr
newCoin.name = "\(coinName)"
newCoin.fullName = "\(coinFullName)"
newCoin.imageURL = "\(coinImageUrl)"
newCoin.price = "\(coinPrice)"
newCoin.marketCap = "\(coinMarketCap)"
newCoin.change24Hr = "\(coinChange24Hr)"
self.coins.append(newCoin)
}
}
}
}
}
When i print 'self.coins.count' within the scope of the function i can see the count incrementing. Outside the function it's reading 0 items in the array.

Written for Swift 5
The problem is that you have a URL request which is Asynchronous. This means that the task is not waited for to complete.
In your problem, inside the function coins is printed after it has been assigned, after the URL request. However, when coins is printed outside the function, it is printed before it has been changed, as the URL request has not yet completed.
To solve this, you need to create a completion handler. A basic one is shown here:
// Our errors which could occur
enum SomeError: Error { case unknown }
// Function which is ASYNCHRONOUS
func someAsyncFunction(completion: #escaping (Result<Int, SomeError>) -> ()) {
// Temporary for this example
let success = true
let myNum = 3
// Return value if it is a success, otherwise return the error
if success {
completion(.success(myNum))
} else {
completion(.failure(.unknown))
}
}
// Call
someAsyncFunction { (result) in
print("Result: \(result)")
/* PRINT COINS HERE */
}
See a full guide on completion handlers using Result in Swift 5 at hackingwithswift.com.

Related

Parse Alamofire result swift

I'm very lost parsing the following response from an AF request – let json = result as! NSDictionary – in Swift:
{
errors = (
);
get = statistics;
parameters = {
country = germany;
};
response = (
{
cases = {
"1M_pop" = 14303;
active = 317167;
critical = 4179;
new = "+15161";
recovered = 863300;
total = 1200006;
};
continent = Europe;
country = Germany;
day = "2020-12-08";
deaths = {
"1M_pop" = 233;
new = "+380";
total = 19539;
};
population = 83900328;
tests = {
"1M_pop" = 347331;
total = 29141172;
};
time = "2020-12-08T09:15:08+00:00";
}
);
results = 1;
}
Any idea how to get the actual case numbers, i.e. for example the number of new cases?
So far I have tried the following (error throwing) approach:
if let responseDict = result as? NSDictionary {
if let data = responseDict.value(forKey: "response") as?
[NSDictionary] {
// Get case numbers
guard let cases = data[0]["cases"] else { return }
guard let casesPerOneMil = cases[0] as! Int else { return }
print(casesPerOneMil)
}
}
Basically don't use NS... collection types in Swift at all, use native types.
And don't use value(forKey, use key subscription.
And you have to conditional downcast Any to the expected concrete type.
There is another mistake: The object for cases is a dictionary, note the {} and you have to get the value for casesPerOneMil with key subscription, too
if let responseDict = result as? [String:Any],
let dataArray = responseDict["response"] as? [[String:Any]],
let firstDataItem = dataArray.first {
// Get case numbers
guard let cases = firstDataItem["cases"] as? [String:Any] else { return }
guard let casesPerOneMil = cases["1M_pop"] as? Int else { return }
print(casesPerOneMil)
}
}

How to work with Firebase Asynchronously? Database reading giving odd results

I have written the following function to search through my Firebase database and I have also looked into using debug statements and tested with breakpoints to see this function is pulling the correct data and it is. But when I return the array at the end, the array is empty. As far as I understand this is due to the asynchronous nature of firebase. The function is getting to the end before the data is being added to the array. How do I fix this so it can work as intended, I want to return an array of items which I can then use for other functions.
static func SearchPostsByTags(tags: [String]) -> [Post]{
var result = [Post]()
let dbref = FIRDatabase.database().reference().child("posts")
dbref.observeSingleEvent(of: .value, with: { snap in
let comps = snap.value as! [String : AnyObject]
for(_, value) in comps {
let rawTags = value["tags"] as? NSArray
let compTags = rawTags as? [String]
if compTags != nil {
for cTag in compTags! {
for tag in tags {
if (tag == cTag) {
let foundPost = Post()
foundPost.postID = value["postID"] as! String
foundPost.title = value["title"] as! String
result.append(foundPost)
}
}
}
}
}
})
return result
}
}
You are returning your array before the async call ends. You should fill your array inside the async call and call then another method, which provides the results.
static func SearchPostsByTags(tags: [String]) {
let dbref = FIRDatabase.database().reference().child("posts")
dbref.observeSingleEvent(of: .value, with: { snap in
let comps = snap.value as! [String : AnyObject]
var result = [Post]()
for(_, value) in comps {
let rawTags = value["tags"] as? NSArray
let compTags = rawTags as? [String]
if compTags != nil {
for cTag in compTags! {
for tag in tags {
if (tag == cTag) {
let foundPost = Post()
foundPost.postID = value["postID"] as! String
foundPost.title = value["title"] as! String
result.append(foundPost)
}
}
}
}
}
// Call some func to deliver the finished result array
// You can also work with completion handlers - if you want to try have a look at callbacks / completion handler section of apples documentation
provideTheFinishedArr(result)
})
}

Calling method with error: Expected argument (Variable) ->()

I am trying to call a method with a completion handler, but I can't seem to call it without this error. I am confused on what its asking for.
Here is the method I can calling:
func fillFromFile(completionBlock: #escaping ([Asset_Content]) -> ()) {
let url = "URLSTRING"
LoadJSONFile(from: url) { (result) in
// The code inside this block would be called when LoadJSONFile is completed. this could happen very quickly, or could take a long time
//.map is an easier way to transform/iterate over an array
var newContentArray = [Asset_Content]()
for json in result{
let category = json["BIGCATEGORY"] as? String
let diagnosis = json["DIAGNOSIS"] as? String
let perspective = json["PERSPECTIVE"] as? String
let name = json["NAME"] as? String
let title = json["Title"] as? String
let UnparsedTags = json["TAGS"] as? String
let filename = json["FILENAME"] as? String
let tagArray = UnparsedTags?.characters.split(separator: ",")
for tag in tagArray!{
if(!self.ListOfTags.contains(String(tag))){
self.ListOfTags.append(String(tag))
}
}
let asset = Asset_Content(category!, diagnosis!, perspective!, name!, title!, filename!)
// This is a return to the map closure. We are still in the LoadJSONFile completion block
newContentArray.append(asset)
}
print("return count ", newContentArray.count)
// This is the point at which the passed completion block is called.
completionBlock(newContentArray)
}
}

How to add an Array<String> to a Dictionary<String, AnyObject> in Swift 2

RESOLVED! See the solution at the bottom of this post
I'm trying to create a JSON object to use for my backend using Alamofire. I am able to add a key with a String value but can't seem to be able to add a value of Array to AnyObject. I though it would be very straight forward but I haven't been able to find a solution.
func someFunction(btn: UIButton){
var someDictionary = Dictionary<String, AnyObject>()
let someArray = [textField[1].text,textField[2].text,textField[3].text]
someDictionary["answer"] = textField[0].text
someDictionary["options"] = someArray as? Array // <---- Can't be assigned to AnyObject
let url = "http://localhost:3000/api/question"
Alamofire.request(.POST, url, parameters: someDictionary).responseJSON { response in
if let JSON = response.result.value as? Dictionary<String, AnyObject>{
}
}
}
Solution: Removed as? Array and created loop to append initialized Array
func someFunction(btn: UIButton){
var someDictionary = Dictionary<String, AnyObject>()
var SomeArray = [String]()
for i in 1...3{ //created loop to append the textField text
SomeArray.append(textField[i].text!)
}
someDictionary["answer"] = textField[0].text
someDictionary["options"] = SomeArray // Removed "as? Array"
let url = "http://localhost:3000/api/question"
Alamofire.request(.POST, url, parameters: someDictionary).responseJSON { response in
if let JSON = response.result.value as? Dictionary<String, AnyObject>{
print("JSON Response From Server-->\(JSON)")
}
}
}
Clean your project and run again. Your code is working for me and I can assign
func someFunction(btn: UIButton){
var someDictionary = Dictionary<String, AnyObject>()
let someArray = ["SomeString","SomeString","SomeString"]
someDictionary["answer"] = textFields[0].text
someDictionary["options"] = someArray // now you can assign
let url = "http://localhost:3000/api/question"
Alamofire.request(.POST, url, parameters: someDictionary).responseJSON { response in
if let JSON = response.result.value as? Dictionary<String, AnyObject>{
}
}
}

Cannot convert value of type 'String?!' to expected argument type 'Notifications'

I am trying to check the id of a record before I put it into the array, using xcode swift
here is the code. But, i get the following error
Notifications.swift:50:46: Cannot convert value of type 'String?!' to expected argument type 'Notifications'
on this line
*if (readRecordCoreData(result["MessageID"])==false)*
Please can some one help to explain this error
import CoreData
struct Notifications{
var NotifyID = [NSManagedObject]()
let MessageDesc: String
let Messageid: String
init(MessageDesc: String, Messageid:String) {
self.MessageDesc = MessageDesc
self.Messageid = Messageid
// self.MessageDate = MessageDate
}
static func MessagesWithJSON(results: NSArray) -> [Notifications] {
// Create an empty array of Albums to append to from this list
var Notification = [Notifications]()
// Store the results in our table data array
if results.count>0 {
for result in results {
//get fields from json
let Messageid = result["MessageID"] as! String
let MessageDesc = result["MessageDesc"] as? String
let newMessages = Notifications(MessageDesc: MessageDesc!, Messageid:Messageid)
//check with id's from core data
if (readRecordCoreData(result["MessageID"])==false)
{
Notification.append(newMessages)
}
}
}
return Notification
}
//check id
func readRecordCoreData(Jsonid: String) -> Bool {
var idStaus = false
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
//2
let fetchRequest = NSFetchRequest(entityName: "ItemLog")
//3
do {
let resultsCD = try! managedContext.executeFetchRequest(fetchRequest)
if (resultsCD.count > 0) {
for i in 0 ..< resultsCD.count {
let match = resultsCD[i] as! NSManagedObject
let id = match.valueForKey("notificationID") as! String
if (Jsonid as String! == id)
{
idStaus = true
}
else{
idStaus = false
}
}
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
return idStaus
}
One of your methods is static and the other one is not :
func readRecordCoreData(Jsonid: String) -> Bool
static func MessagesWithJSON(results: NSArray) -> [Notifications]
Depending on what you want to accomplish you could declare both static, none, or replace
//check with id's from core data
if (readRecordCoreData(result["MessageID"])==false)
{
Notification.append(newMessages)
}
By
//check with id's from core data
if (Notifications.readRecordCoreData(Messageid)==false)
{
Notification.append(newMessages)
}
Not sure if the code will work past compilation however as there are many readability issues