The following Swift code has to run twice in order to display the JSON data in a label. On the first run the label simply remains blank. It seems to me that the issue could be something along the lines of the JSON part of the code runs last, but I can't figure out why. I have included the code below. I'm still a rookie, so be gentle :D
class ViewController: UIViewController {
#IBOutlet weak var labelDegrees: UILabel!
#IBOutlet weak var labelSpeed: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Set logo in nav bar
navigationItem.titleView = UIImageView(image: UIImage(named: "logo"))
// Global save values
var appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
var context: NSManagedObjectContext = appDel.managedObjectContext!
// JSON Fetching
let urlPath = "http://api.openweathermap.org/data/2.5/weather?lat=55.564120&lon=12.568605"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if (error != nil) {
println(error)
}
else {
// Delete old entries in CoreData
var request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
for result in results! {
context.deleteObject(result as NSManagedObject)
context.save(nil)
}
// Start fetching JSON
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var item = jsonResult["wind"] as NSDictionary
var degrees:Float = item["deg"] as Float
var speed:Float = item["speed"] as Float
// Start saving JSON
var newItem = NSEntityDescription.insertNewObjectForEntityForName("WindData", inManagedObjectContext: context) as NSManagedObject
var speedValue:Float = speed as Float
var degreesValue:Float = degrees as Float
newItem.setValue(speedValue, forKey: "speed")
newItem.setValue(degreesValue, forKey: "degrees")
context.save(nil)
}
})
task.resume()
// Start fetching from CoreData
var request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
if results!.count > 0 {
for result in results as [NSManagedObject] {
let degrees:Float = result.valueForKey("degrees")! as Float
let speed:Float = result.valueForKey("speed")! as Float
if speed > 6.0 {
self.labelDegrees.text = "Go kitesurf: \(speed) m/s"
}
else {
self.labelDegrees.text = "Stay in: \(speed) m/s"
}
}
}
else {
println("No data")
}
}
You are actually fetching the data on another thread with dataTaskWithURL.So you need to write // Start fetching from CoreData code in dataTaskWithURL.As dataTaskWithURL will not immediately executed so // Start fetching from CoreData code will get called first while dataTaskWithURL is fetching the data.So replece the code with below
class ViewController: UIViewController {
#IBOutlet weak var labelDegrees: UILabel!
#IBOutlet weak var labelSpeed: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Set logo in nav bar
navigationItem.titleView = UIImageView(image: UIImage(named: "logo"))
// Global save values
var appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
var context: NSManagedObjectContext = appDel.managedObjectContext!
// JSON Fetching
let urlPath = "http://api.openweathermap.org/data/2.5/weather?lat=55.564120&lon=12.568605"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
dispatch_async(dispatch_get_main_queue(),{
if (error != nil) {
println(error)
}
else {
// Delete old entries in CoreData
var request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
for result in results! {
context.deleteObject(result as NSManagedObject)
context.save(nil)
}
// Start fetching JSON
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var item = jsonResult["wind"] as NSDictionary
var degrees:Float = item["deg"] as Float
var speed:Float = item["speed"] as Float
// Start saving JSON
var newItem = NSEntityDescription.insertNewObjectForEntityForName("WindData", inManagedObjectContext: context) as NSManagedObject
var speedValue:Float = speed as Float
var degreesValue:Float = degrees as Float
newItem.setValue(speedValue, forKey: "speed")
newItem.setValue(degreesValue, forKey: "degrees")
context.save(nil)
}
// Start fetching from CoreData
var request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
if results!.count > 0 {
for result in results as [NSManagedObject] {
let degrees:Float = result.valueForKey("degrees")! as Float
let speed:Float = result.valueForKey("speed")! as Float
if speed > 6.0 {
self.labelDegrees.text = "Go kitesurf: \(speed) m/s"
}
else {
self.labelDegrees.text = "Stay in: \(speed) m/s"
}
}
}
else {
println("No data")
}
});
})
task.resume()
}
Related
I have run into a problem where I can save and load into and from CoreData in Swift for my iOS app, but I run into a problem where I have tried to guard for duplicate entries, but it does not seem to work. can anyone tell me where I went wrong? Thanks!
My ViewController class:
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet weak var headerLabel:UILabel!
#IBOutlet weak var myTableView: UITableView!
var lenders = [LenderData]()
var lendersTemp = [LenderData]()
override func viewDidLoad() {
super.viewDidLoad()
self.myTableView.rowHeight = 90
myTableView.delegate = self
myTableView.dataSource = self
let fetchRequest: NSFetchRequest<LenderData> = LenderData.fetchRequest()
do {
let lenders = try PersistenceService.context.fetch(fetchRequest)
self.lenders = lenders
} catch {
// Who cares....
}
downloadJSON {
for tempLender in self.lendersTemp {
if !self.lenders.contains(where: {$0.id == tempLender.id}) {
self.lenders.append(tempLender)
}
}
self.lendersTemp.removeAll()
PersistenceService.saveContext()
self.myTableView.reloadData()
}
}
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://api.kivaws.org/v1/loans/newest.json")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("JSON not downloaded")
} else {
if let content = data {
do {
let myJSONData = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
var imageID:Int64 = -1
var country:String = "N/A"
var latLongPair:String = "0.000000 0.000000"
var town:String = "N/A"
if let loans = myJSONData["loans"] as? NSArray {
for i in 0...loans.count-1 {
if let lender = loans[i] as? NSDictionary {
if let imageData = lender["image"] as? NSDictionary { imageID = imageData["id"] as! Int64 }
if let countryData = lender["location"] as? NSDictionary {
country = countryData["country"] as! String
town = countryData["town"] as! String
if let geo = countryData["geo"] as? NSDictionary {
latLongPair = geo["pairs"] as! String
}
}
let newLender = LenderData(context: PersistenceService.context)
newLender.id = lender["id"] as! Int64
newLender.name = lender["name"] as? String
newLender.image_id = imageID
newLender.activity = lender["activity"] as? String
newLender.use = lender["use"] as? String
newLender.loan_amount = lender["loan_amount"] as! Int32
newLender.funded_amount = lender["funded_amount"] as! Int32
newLender.country = country
newLender.town = town
newLender.geo_pairs = latLongPair
self.lendersTemp.append(newLender)
}
}
}
DispatchQueue.main.async {
completed()
}
} catch {
print("Error occured \(error)")
}
}
}
}
task.resume()
}
}
EDIT
Added the part of the code where I populate the lendersTemp array
I quote matt on this one from the comments:
So... You are appending to self.lendersTemp on a background thread but reading it on the main thread. Instead, get rid of it and just pass the data right thru the completed function.
Which is exactly what I did. And this worked
I am trying to display a Json result (temperature and Humidity) on my view controller (respectively temperatureDisp and humidityDisp), but it does not seem to work.
class HomeVC: UIViewController {
#IBOutlet var usernameLabel: UILabel!
#IBOutlet var temperatureDisp: UILabel!
#IBOutlet var humidityDisp: UILabel!
#IBAction func logoutTapped(sender: AnyObject) {
let appDomain = NSBundle.mainBundle().bundleIdentifier
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain!)
self.performSegueWithIdentifier("goto_login", sender: self)
}
override func viewDidAppear(animated: Bool) {
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
let isLoggedIn:Int = prefs.integerForKey("ISLOGGEDIN") as Int
if (isLoggedIn != 1) {
self.performSegueWithIdentifier("goto_login", sender: self)
} else {
self.usernameLabel.text = prefs.valueForKey("USERNAME") as! NSString as String
}
}
override func viewDidLoad() {
super.viewDidLoad()
var url2 : String = "http://admin:xxxxxxx#xxxxxx/xxxxx.fr/metrics2.php"
var request2 : NSMutableURLRequest = NSMutableURLRequest()
request2.URL = NSURL(string: url2)
request2.HTTPMethod = "GET"
NSURLConnection.sendAsynchronousRequest(request2, queue: NSOperationQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult : NSArray! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: error) as! NSArray
if (jsonResult != nil) {
println(jsonResult)
} else {
println("There is a problem")
}
var temperature = jsonResult[0].valueForKey("temperature") as! String
var humidity = jsonResult[0].valueForKey("humidite") as! String
println(temperature)
println(humidity)
self.humidityDisp.text = temperature
})
}
}}
That is how the variable jsonResult looks :
(
{
Id = 117;
date = "2015-04-06";
humidite = "45.3";
login = raspberrypi;
luminosite = "\U00e9teinte";
temperature = "18.4";
time = "16:25:21";
}
)
I'm not sure what you mean by "it does not seem to work", but from your code I can assume at least that you are not getting the values you expect from your JSON result, but more likely your app is crashing like crazy. If you want to write applications in Swift you absolutely must understand optionals and learn how to properly work with them. Read The Swift Programming Language—and thoroughly. As your code is now, by force unwrapping using as! and using implicitly unwrapped types (those followed by !) you are ignoring the entire concept of optionals and opening yourself up to crashes.
So, assuming that there is no network or parsing errors, and assuming that the JSON string you're parsing has an array as its root object, the following should work. I've taken the liberty of typing and unwrapping your variables appropriately, as well as cleaning up some of the cruft.
class HomeVC: UIViewController {
#IBOutlet var usernameLabel: UILabel!
#IBOutlet var temperatureDisp: UILabel!
#IBOutlet var humidityDisp: UILabel!
#IBAction func logoutTapped(sender: AnyObject) {
if let appDomain = NSBundle.mainBundle().bundleIdentifier {
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain)
}
self.performSegueWithIdentifier("goto_login", sender: self)
}
override func viewDidAppear(animated: Bool) {
let prefs = NSUserDefaults.standardUserDefaults()
let isLoggedIn = prefs.boolForKey("ISLOGGEDIN")
if isLoggedIn {
self.performSegueWithIdentifier("goto_login", sender: self)
} else {
if let username = prefs.stringForKey("USERNAME") {
self.usernameLabel.text = username
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
var url2 = "http://admin:xxxxxxx#xxxxxx/xxxxx.fr/metrics2.php"
var request2 = NSMutableURLRequest()
request2.URL = NSURL(string: url2)
request2.HTTPMethod = "GET"
NSURLConnection.sendAsynchronousRequest(request2, queue: NSOperationQueue()) { (response: NSURLResponse!,data: NSData!,error: NSError!) in
var parseError:NSError?
if let data = data, let jsonResults = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &parseError) as? NSArray {
println( jsonResults )
if let result = jsonResults.firstObject as? [String : AnyObject] {
if let humidity = result["humidite"] as? String {
self.humidityDisp.text = humidity
}
if let temperature = result["temperature"] as? String {
self.temperatureDisp.text = temperature
}
}
} else {
println("There is a problem: \(parseError)" )
}
}
}
}
I’m now stuck little bit. First, my code is as follows,
import UIKit
import CoreData
class ViewController: UIViewController {
var eTitles : [String] = []
var jTitles : [String] = []
var pCategories : [String] = []
var imgPaths : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "http://localhost:8888/post.php")
let allData = NSData(contentsOfURL: url!)
let allJsonData : AnyObject! = NSJSONSerialization.JSONObjectWithData(allData!, options: NSJSONReadingOptions(0), error: nil)
if let json = allJsonData as? Array<AnyObject>{
//println(json)
for index in 0...json.count-1{
let post : AnyObject? = json[index]
//println(post)
let collection = post! as Dictionary<String, AnyObject>
//println(collection)
//println(collection["Eng_Title"])
var eTitle : AnyObject? = collection["Eng_Title"]
var jTitle : AnyObject? = collection["Jam_Title"]
var pCategory : AnyObject? = collection["Category"]
var imgPath : AnyObject? = collection["Category_Img"]
eTitles.append(eTitle as String)
jTitles.append(jTitle as String)
pCategories.append(pCategory as String)
imgPaths.append(imgPath as String)
}
}
println(eTitles)
println(jTitles)
println(pCategories)
println(imgPaths)
for var i = 0; i < pCategories.count; i++
{
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let managedContext = appDelegate.managedObjectContext!
let entity = NSEntityDescription.entityForName("Category", inManagedObjectContext: managedContext)!
let category = Category(entity: entity, insertIntoManagedObjectContext:managedContext)
category.title = pCategories[i]
category.path = fetchImg(imgPaths[i])
appDelegate.saveContext()
let en = NSEntityDescription.entityForName("Post", inManagedObjectContext: managedContext)!
let post = Post(entity: en, insertIntoManagedObjectContext:managedContext)
post.jtitle = jTitles[i]
post.etitle = eTitles[i]
post.category = category
appDelegate.saveContext()
}
}
func fetchImg(path : String) -> String{
var urlWebView = NSURL(string: path)!
println(urlWebView)
var requestWebView = NSURLRequest(URL: urlWebView)
var saveP : String = ""
NSURLConnection.sendAsynchronousRequest(requestWebView, queue: NSOperationQueue.mainQueue(), completionHandler: {
response, data, error in
if error != nil {
println("There was an error")
}
else {
// let musicFile = (data: data)
var documentsDirectory:String?
var paths:[AnyObject] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
if paths.count > 0 {
documentsDirectory = paths[0] as? String
var savePath = documentsDirectory! + "/" + NSUUID().UUIDString + ".jpg"
println(savePath)
saveP = savePath
NSFileManager.defaultManager().createFileAtPath(savePath, contents: data, attributes: nil)
}
}
})
return saveP
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Everytime I run this app, it stops at this line :
NSURLConnection.sendAsynchronousRequest(requestWebView, queue: NSOperationQueue.mainQueue(), completionHandler: {
(What I’m trying to do here is to parse Json data and put it into array, and then save it into CoreData. The problem is fetchImg() function.
I’m trying to pass paths, which come from Json data, to this function, and the function fetch real images from web, create a path and save the data onto device, and return the path to which images actually are saved.)
Any advice?
Sorry for my poor Eng explanation!!!
Thanks!
How can I have my app run my code every time the app is viewed and not just every time it is opened? Right now I have to exit it, and open it again to run the code.
My app pulls data via JSON and displays it in labels. All of my code is in viewDidLoad.
Is the answer in putting all of the code in viewDidAppear?
Thanks in advance
Here is the whole code:
//
// ViewController.swift
// KitesurfioApp
//
// Created by Lasse Kristensen on 24/10/14.
// Copyright (c) 2014 Lasse Kristensen. All rights reserved.
//
import UIKit
import CoreData
class ViewController: UIViewController {
#IBOutlet weak var labelSpeed: UILabel!
override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "backgroundNotification:", name: UIApplicationWillEnterForegroundNotification, object: nil);
// Set logo in nav bar
navigationItem.titleView = UIImageView(image: UIImage(named: "logo"))
refresh();
}
func backgroundNotification(noftification:NSNotification){
refresh();
println("Refreshed after background")
}
func refresh() {
// Global save values
var appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
var context: NSManagedObjectContext = appDel.managedObjectContext!
// JSON Fetching
let urlPath = "http://api.openweathermap.org/data/2.5/weather?lat=55.564120&lon=12.568605"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
dispatch_async(dispatch_get_main_queue(),{
if (error != nil) {
println(error)
}
else {
// Delete old entries in CoreData
var request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
for result in results! {
context.deleteObject(result as NSManagedObject)
context.save(nil)
}
// Start fetching JSON
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var item = jsonResult["wind"] as NSDictionary
var degrees:Float = item["deg"] as Float
var speed:Float = item["speed"] as Float
// Start saving JSON
var newItem = NSEntityDescription.insertNewObjectForEntityForName("WindData", inManagedObjectContext: context) as NSManagedObject
var speedValue:Float = speed as Float
var degreesValue:Float = degrees as Float
newItem.setValue(speedValue, forKey: "speed")
newItem.setValue(degreesValue, forKey: "degrees")
context.save(nil)
}
// Start fetching from CoreData
var request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
if results!.count > 0 {
for result in results as [NSManagedObject] {
let degrees:Float = result.valueForKey("degrees")! as Float
let speed:Float = result.valueForKey("speed")! as Float
if speed > 6.0 {
self.labelSpeed.text = "Go kitesurf: \(speed) m/s \(degrees)"
}
else {
self.labelSpeed.text = "Stay in: \(speed) m/s \(degrees)"
}
}
}
else {
println("No data")
}
});
})
task.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You need to add UIApplicationWillEnterForegroundNotification and refresh your screen there.Put all code from viewDidLoad to helper function like refreh and call it when viewDidLoad or you come in foreground from background or whenever you want to refresh screen.
override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "backgoundNofification:", name: UIApplicationWillEnterForegroundNotification, object: nil);
//put all code of your viewDidLoad to refresh
refresh();
}
func backgoundNofification(noftification:NSNotification){
refresh();
}
func refresh() {
//here is your all code to fetch data and all
}
I am attempting to pull out and print two values from CoreData. One is speed, the other is degrees. However things don't go as planned. First up, here's part of the code:
// Start fetching from CoreData
request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
results = context.executeFetchRequest(request, error: nil)
if results!.count > 0 {
for result: AnyObject in results! {
println(result)
println(result.speed)
println(result.degrees)
}
}
else {
println("No data")
}
When I simply print results I get the values:
<NSManagedObject: 0x7f944a734e80> (entity: WindData; id: 0xd000000000740000 <x-coredata://762EB8C2-DDCF-43F5-8DFC-FAB9A29532E1/WindData/p29> ; data: {
degrees = 190;
speed = 8; })
println(result.speed) writes in the consol: 2.34181e-38
and println(result.degrees) won't allow me to compile and tells me that AnyObject does not have a member named degrees (although it clearly does)
Sorry for any rookie mistakes, this is my first time posting on Stackoverflow.
Here is the full code:
//
// ViewController.swift
// KitesurfioApp
//
// Created by Lasse Kristensen on 24/10/14.
// Copyright (c) 2014 Lasse Kristensen. All rights reserved.
//
import UIKit
import CoreData
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Set logo in nav bar
navigationItem.titleView = UIImageView(image: UIImage(named: "logo"))
// Global save values
var appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
var context: NSManagedObjectContext = appDel.managedObjectContext!
// JSON Fetching
let urlPath = "http://api.openweathermap.org/data/2.5/weather?lat=55.564120&lon=12.568605"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if (error != nil) {
println(error)
}
else {
// Delete old entries in CoreData
var request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
for result in results! {
context.deleteObject(result as NSManagedObject)
context.save(nil)
}
// Start fetching JSON
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var item = jsonResult["wind"] as NSDictionary
var degrees:Int = item["deg"] as NSInteger
var speed:Float = item["speed"] as Float
// Start saving JSON
var newItem = NSEntityDescription.insertNewObjectForEntityForName("WindData", inManagedObjectContext: context) as NSManagedObject
var speedValue:NSNumber = speed as NSNumber
var degreesValue:NSNumber = degrees as NSNumber
newItem.setValue(speedValue, forKey: "speed")
newItem.setValue(degreesValue, forKey: "degrees")
context.save(nil)
}
})
task.resume()
// Start fetching from CoreData
var request = NSFetchRequest(entityName: "WindData")
request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
if results!.count > 0 {
for result: AnyObject in results! as [WindData] {
println(result)
println(result.speed)
}
}
else {
println("No data")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You need to cast the AnyObject to your class which have properties degree etc.AnyObject does not know about your class properties you need to downcast the array [AnyObject] to your class array
for result in results! as [NSManagedObject] {
println(result)
println(result.valueForKey("speed")!)
println(result.valueForKey("degrees")!)
}