I have 2 classes.
In first class I have a label and a button and in second class I have JsonPars function
I need to write a data from second class to the label in first class.
Here is my code:
import UIKit
import CoreLocation
//main class
class ViewController: UIViewController{
#IBOutlet weak var labl: UILabel!
#IBAction func btn(sender: AnyObject) {
Json().Pars()
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
and second class:
import Foundation
//Json-class
class Json {
func Pars() {
let url = NSURL(string: "http://my.url.com")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if (error != nil){
println(error.localizedDescription)
}
var err: NSError?
let parseObj: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: &err)
if let json = parseObj as? NSDictionary{
if let response = json["response"] as? NSDictionary{
if let obj = response["Object"] as? NSDictionary{
if let data = obj["data"] as? NSString {
println(data)//work's and i have a data
ViewController().labl.text = data //fatal error: unexpectedly found nil while unwrapping an Optional value
}
}
}
}
})
task.resume()
}
}
Sorry fo my English
The problem is you're creating a new instance of ViewController when you call ViewController(). Since you haven't presented the view controller it's outlets haven't been set and are nil. Therefore when you try to access labl (an implicitly unwrapped optional), it's equal to nil and your app crashes.
To fix this, perhaps pars() (it's convention to use lower case for methods) could return the data, which you would then have access to in your ViewController class.
Sorry I'm in class on my Lenovo so I can't test this, but try:
import Foundation
import UIKit
I normally do that just in case I need it at some point. Also if that fails maybe try sending data to the viewcontroller then applying it to label.text.
EDIT: You also have this:
})
above task.resume, please review that and see if that doesn't fix it.
Related
I'm really new into swift & currently learning API by doing a project that shows list of games from rawg.io referring to the website's doc. I created GameFeed.swift & GameDetail.swift to pull name, release date, and rating from it and working fine in my console.
GameFeed.swift :
struct GameFeed: Codable {
let results:[GameDetail]
}
GameDetail.swift :
struct GameDetail: Codable {
let name:String
let released:String
let rating:Double
}
Now i'm trying to put the results to a simple UIlabel like gameName.text, gameReleased.text & gameRating.text from ViewController.swift so it will be show in Main.Storyboard
i did research on google about how to show it to these UIlabel by using DispatchQueue.main.async but when i'm declaring it, it receiving error :
Value of type 'GameFeed' has no member 'name'
same error messages also happened to released & rating. This is my ViewController.Swift :
class ViewController: UIViewController {
#IBOutlet weak var gameName: UILabel!
#IBOutlet weak var gameReleased: UILabel!
#IBOutlet weak var gameRating: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Defining API Site
let urlString = "https://api.rawg.io/api/games"
let url = URL(string: urlString)
guard url != nil else {
return
}
// Calling API
let session = URLSession.shared
let dataTask = session.dataTask(with: url!){
(data, response, error) in
if error == nil && data != nil {
let decoder = JSONDecoder()
do {
let gameFeed = try decoder.decode(GameFeed.self, from: data!)
print(gameFeed)
DispatchQueue.main.async {
self.gameName.text = gameFeed.name
self.gameReleased.text = gameFeed.released
self.gameRating.text = gameFeed.rating
}
}
catch {
print("Error Parsing JSON")
}
}
}
dataTask.resume()
}
}
What should i do to make it possible to parse the data to labels?
The GameFeed contains an Array of GameDetails. But you are trying to set a single GameDetail on those labels. You should first pull out a single GameDetail from that array, then assign it in a way you like.
DispatchQueue.main.async {
let gameDetail = gameFeed.results.first // <- This will return the first one
self.gameName.text = gameDetail?.name
self.gameReleased.text = gameDetail?.released
self.gameRating.text = gameDetail?.rating
}
I am trying to retrieve certain data from my Firebase Database - the profile image. As you can see, this is from a UITableViewCell. I have an #IBOutlet for my imageView I want to cover.
As the view awakens, you can see that I go through, and make sure that I can get the information. I know how to retrieve data from Firebase, but not photo URLs, and then convert to the photo itself.
I'm not sure why it isn't working. I am getting an error, and will show it below. There is a possibility it is because of the URL unwrapping stuff, or as if the Firebase isn't formatted correctly, which I think it is, though.
Error Message : Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
import UIKit
import FirebaseAuth
import FirebaseDatabase
import Firebase
class ProfileCellControler: UITableViewCell {
#IBOutlet var name : UILabel!
#IBOutlet var rating : UILabel!
#IBOutlet var imageViewPro : UIImageView!
var databaseRefer : DatabaseReference!
var databaseHandle : DatabaseHandle!
override func awakeFromNib() {
super.awakeFromNib()
var urlString = ""
let urll = URL(string: urlString)!
databaseRefer = Database.database().reference()
let userID = Auth.auth().currentUser!.uid
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Profile Name").observe(.value, with: { (data) in
print(String((data.value as? String)!))
self.name.text = "\(String((data.value as? String)!))"
print("Done")
})
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Stars").observe(.value, with: { (data) in
print(String((data.value as? String)!))
if ((String((data.value as? String)!)) == "N/A") {
self.rating.text = "No Rating"
} else {
self.rating.text = "\(String((data.value as? String)!)) ★"
}
print("Done")
})
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Profile Image").observe(.value, with: { (data) in
print(String((data.value as? String)!))
print("Done \(String((data.value as? String)!))")
urlString = (String((data.value as? String)!))
})
ImageService.downloadImage(withURL: urll) { (image) in
self.imageViewPro.image = image
}
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
The string for the URL is found nil because you are creating the call to download the image for your url before the urll has been initialized with a value from the database in:
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Profile Image").observe(.value, with: { (data) in
print(String((data.value as? String)!))
print("Done \(String((data.value as? String)!))")
urlString = (String((data.value as? String)!))
})
observe(.value, with: ) Is an asynchronous operation thus
ImageService.downloadImage(withURL: urll) { (image) in
self.imageViewPro.image = image
}
Is being called before observe(.value, with:) is resolved. I would recommend moving the callback for the download URL inside of the completion for .observe(:value, :with) or using grand central dispatch to control the flow better.
As a side note, I highly recommend SDWebImage for handling your image downloading needs as it is configurable with a default image for situations such as this when the image fails to load.
Import KingFisher to make your life easier and then..
Download string representation of image from Firebase asynchronically.
Assign downloaded image to imageView with .kf.setImage method.
I am trying to implement loading data from Parse server after tapping a button on postVC to load data and navigate to guestVC. It was working fine and at some point began to crash the app...not sure why? I am getting the fatal error: unexpectedly found nil while unwrapping an Optional value...Any and all direction or help would be greatly appreciated. Thanks!
import UIKit
import Parse
var postuuid = [String]()
class postVC: UITableViewController {
//postVC button click function
//clicked username button from post
#IBAction func usernameBtn_click(sender: AnyObject) {
let i = sender.layer.valueForKey("index") as! NSIndexPath
let cell = tableView.cellForRowAtIndexPath(i) as! postCell
// if user tapped on himself go home, else go guest
if cell.usernameBtn.titleLabel?.text == PFUser.currentUser()?.username {
let home = self.storyboard?.instantiateViewControllerWithIdentifier("homeVC") as! homeVC
self.navigationController?.pushViewController(home, animated: true)
} else {
let guest = self.storyboard?.instantiateViewControllerWithIdentifier("guestVC") as! guestVC
self.navigationController?.pushViewController(guest, animated: true)
}
}
// guestVC relevant code
import UIKit
import Parse
var guestname = [String]()
class guestVC: UICollectionViewController {
var uuidArray = [String]()
var picArray = [PFFile]()
// posts loading function
func loadPosts() {
let query = PFQuery(className: "posts")
// app keeps crashing in line below when I try to load data to guestVC
query.whereKey("username", equalTo: guestname.last!)
query.limit = self.page
query.findObjectsInBackgroundWithBlock( { (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
self.uuidArray.removeAll(keepCapacity: false)
self.picArray.removeAll(keepCapacity: false)
for object in objects! {
self.uuidArray.append(object.valueForKey("uuid") as! String)
self.picArray.append(object.valueForKey("pic") as! PFFile)
}
self.collectionView?.reloadData()
} else {
print(error!.localizedDescription)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
// code here will be executed as the main queue
})
})
}
You use a lot of exclamation marks to force unwrap optional values in your code, it's a bad habit.
For example, you can unwrap guestname.last safely by:
guard let lastItem = guestname.last else {
// do something else
return
}
query.whereKey("username", equalTo: lastItem)
Before adding or appending, check dictionary key has valid value or not. Check if 'uuid' or 'pic' key has value in dictionary or not. If it has then add/append.
I know how to to retrieve an image file from Parse and store it in an UIImageView but how do you retrieve a video(or movie) file from Parse.com and store it in a UIView to be played (using AVPlayer) ?
Import the following in your file:
import Parse
import AVFoundation
import AVKit
add this before your class definition so that you can access it everywhere as global variables:
public var audioPlayer = AVPlayer()
public var songNumber = Int()
add this to your class definition: AVAudioPlayerDelegate
Set arrays to store the data:
var idArray = [String]()
var nameArray = [String]()
Your viewDidLoad + getSongs function:
override func viewDidLoad() {
super.viewDidLoad()
var objectQuery = PFQuery(className: "Songs")
objectQuery {
(objectsArray: [PFObject]?, error: NSError?) -> Void in
var objectIDs = objectsArray!
print(objectIDs)
for i in 0...objectIDs.count-1 {
self.iDArray.append(objectIDs[i].valueForKey("objectId") as! String)
self.nameArray.append(objectIDs[i].valueForKey("songName") as! String)
self.tableView.reloadData()
}
}
}
func getSongs () {
var songQuery = PFQuery(className: "Songs")
songQuery.getObjectInBackgroundWithId(idArray[songNumber], block: {
(object: PFObject?, error : NSError?) -> Void in
if let audioFile = object?["songFile"] as? PFFile {
let audioFileUrlString: String = audioFile.url
let audioFileUrl = NSURL(string: audioFileUrlString)!
audioPlayer = AVPlayer(URL: audioFileUrl)
audioPlayer.play()
}
})
}
After this code, you can get your getSongs function where you want, or if its a TableView then you can set it in a cell with indexPath.row
As the question is a bit broad Im giving a broad answer as I'm not sure how you want to set it and where you want to play it/under what trigger?
Here is a related question I referenced to get you started as well as the docs in Parse.
Even though the question may seem unrelated, the answer and the context is in this question/answer: Could not cast value of type PFFile to NSURL This link shows you how to query and convert so that you can play a music file.
You should also check out the Parse docs: File Queries Parse Docs
I have two swift files in my project. One is called loadApi.swift and the other one is the default ViewController.swift
I intend to include all the functionalities of getting and parsing Json data in the loadApi.swift. Bellow is the code
import Foundation
var arr: Array = [String]()
func loadApi(){
var url = "http://api.wordnik.com:80/v4/word.json/love/definitions?limit=200&includeRelated=true&sourceDictionaries=ahd&useCanonical=true&includeTags=false&api_key=a2a73e7b926c924fad7001ca3111acd55af2ffabf50eb4ae5"
let session = NSURLSession.sharedSession()
let wordnik = NSURL(string: url)
//println(wordnik)
var task = session.dataTaskWithURL(wordnik!){
(data, response, error)-> Void in
if error != nil {
println("Internet error! Check your Internet Connection")
} else{
var error : NSError?
var vocabData = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &error) as NSArray
if error != nil{
println("Parse Json error!")
}else{
for items in vocabData{
let def = items["text"] as String
arr.append(def)
}
}
}
}
task.resume()
}
well, everything works fine and I can get and parse the Json data from the url and than append them into the "arr" Array. However, when I try to call the "arr" Array in the default ViewController.swift, I got this error saying "missing parameter #2 in call". Bellow is my code in the ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
loadApi()
println(arr)
//error saying "missing parameter #2 in call"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
How can I access the "arr" Array that contains all the strings I got from the Json data?
Changing the declaration of the array to var arr = [String]() should do the trick.