Setting text of label in cell after request in Swift - ios

I'm working on an app in Swift that makes a request to a server pulls back JSON and parses it into a schedule. I have table view controller with 7 cells and a label in each. I am aiming to change the text of each label after the request.
#IBOutlet weak var firstPeriod: UILabel!
#IBOutlet weak var secondPeriod: UILabel!
#IBOutlet weak var thirdPeriod: UILabel!
#IBOutlet weak var fourthPeriod: UILabel!
#IBOutlet weak var fifthPeriod: UILabel!
#IBOutlet weak var sixthPeriod: UILabel!
#IBOutlet weak var seventhPeriod: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let dayUrl = NSURL(string: "http://localhost:3000/day")
func setSchedLayout() {
var firstPeriodClass = String(defaults.dictionaryForKey("todaySchedule")!["first"]!["name"]!)
self.firstPeriod.text = firstPeriodClass
print(firstPeriodClass)
}
let task = NSURLSession.sharedSession().dataTaskWithURL(dayUrl!) {(data, response, error) in
day = NSString(data: data!, encoding: NSUTF8StringEncoding)!.stringByReplacingOccurrencesOfString("\"", withString: "").stringByReplacingOccurrencesOfString(" ", withString: "").stringByReplacingOccurrencesOfString("D", withString: "d")
var todaySchedule = defaults.dictionaryForKey("FullWeekSchedule")![day]
defaults.setObject(todaySchedule, forKey: "todaySchedule")
setSchedLayout()
}
task.resume()
}
This all happens inside my viewDidLoad()
I already have some data in my UserDefaults.
When my setSchedLayout() function calls at the end of my request, it doesn't change the text value for about 2 minutes. Then it gives me an error that says I'm trying to
modify the autolayout engine from a background thread
And when it does change the text it gives it an optional string. What's going on?

Related

Persisting Firebase Realtime Database creates null value in snapshot

So my application was working fine; I persisted the database locally using:
Database.database().isPersistenceEnabled = true
I didn't change anything but then all of a sudden it started returning a null snapshot. When I comment out that line, everything is working fine again and the label in the interface is displayed correctly, and this value is synced correctly across multiple devices. Here is a code snippet from my viewDidLoad:
import UIKit
import Firebase
import FirebaseDatabase
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var returnedName: UILabel!
#IBOutlet weak var scannedURLTextField: UITextField!
#IBOutlet weak var reentries: UILabel!
#IBOutlet weak var visitors: UILabel!
#IBOutlet weak var ticketType: UILabel!
#IBOutlet weak var statusImage: UIImageView!
#IBOutlet weak var entryOrReentrySwitch: UISwitch!
var qrDecodedURL : String!
var ticketNumber : String!
var eventID : String!
var numberOfAttendees: String!
var urlDictionary = [String : Any]()
var attendeeRef: DatabaseReference!
var ticketRef : DatabaseReference!
var entries : DatabaseReference!
var entriesRef : DatabaseReference!
var reentriesRef : DatabaseReference!
var entriesInt : Int!
var reentriesInt : Int!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
Database.database().isPersistenceEnabled = true
attendeeRef = Database.database().reference(withPath: "attendees")
ticketRef = Database.database().reference(withPath: "tickets")
entriesRef = Database.database().reference(withPath: "entries")
reentriesRef = Database.database().reference(withPath: "reentries")
self.scannedURLTextField .becomeFirstResponder()
scannedURLTextField.inputView=UIView()//create dummy view to supress keyboard
// Incresase size of switch
entryOrReentrySwitch.transform = CGAffineTransform(scaleX: 1.5, y: 1.5);
//monitor for changes in entries
entriesRef.child("entries").observe(.value, with: { (snapshot) in
print(snapshot);
self.visitors.text = "Visitors: \(snapshot.value as! String)"
let tempValue = snapshot.value as! String
self.entriesInt = Int(tempValue)
})
//Monitor for changes in reentries
reentriesRef.child("reentries").observe(.value, with: { (snapshot) in
// print(snapshot);
self.reentries.text = "Reentries: \(snapshot.value as! String)"
})
statusImage.image = UIImage.init(named: "scanQR.png")
}
The error generated is:
Could not cast value of type 'NSNull' (0x1fb2f3270) to 'NSString' (0x1fb2fcab8).
2019-10-12 18:05:54.061687-0700 VIM Tickets[621:97014] Could not cast value of type 'NSNull' (0x1fb2f3270) to 'NSString' (0x1fb2fcab8).
It fails on the self.visitors.text = "Visitors: \(snapshot.value as! String)" in the monitor for changes in entries section. Again, everything is working fine and there is data in the snapshot when I comment out the database persistence line. I even moved it to the AppDelegate, but it has the same result.
I am at a bit of a loss as to why this is happening when persistence was working before. Any suggestions would be greatly appreciated!
When you have disk persistence enabled, the Firebase client immediately fires with the current value that it knows for the node. It sounds like in your case it thinks that value is null, so it fires with that. If you have a connection to the server, it will moments later fire with the correct value that it got from the server.
The normal approach to deal with this situation is to first check whether the snapshot exists, before processing its contents.

How to update the labels in app as soon as JSON is fetched from server? If should be real time update

I am making a cricket app. I want to update the labels in iOS from URL every time server sends me the JDON file. How should I write the URL code?
import UIKit
import Foundation
struct jsonScore : Decodable {
let comment : String
let venuDetails : String
let valueToDisplay : String
let bowlingTeam : String
let battingTeam : String
let overs : Double
let targetScore : Int
let wickets : Int
let score : Int
let striker : striker
let nonStriker : nonStriker
let strikerBowler : strikerBowler
}
struct striker : Decodable{
let name: String
let runs: String
let balls: String
let fours: String
let sixes: String
}
struct nonStriker : Decodable{
let name: String
let runs: String
let balls: String
let fours: String
let sixes: String
}
struct strikerBowler : Decodable{
let name : String
let overs : String
let maidens : String
var runs : String
let wickets : String
}
class ViewController: UIViewController {
#IBOutlet weak var Comment: UITextView!
#IBOutlet weak var VenuDetails: UILabel!
#IBOutlet weak var ValueToDisplay: UILabel!
#IBOutlet weak var battingTeam: UILabel!
#IBOutlet weak var bowlingTeam: UILabel!
#IBOutlet weak var wickets: UILabel!
#IBOutlet weak var overs: UILabel!
#IBOutlet weak var score: UILabel!
#IBOutlet weak var striker: UILabel!
#IBOutlet weak var strikerRun: UILabel!
#IBOutlet weak var nonStriker: UILabel!
#IBOutlet weak var nonStrikerRun: UILabel!
#IBOutlet weak var strikerBowlerName: UILabel!
#IBOutlet weak var strikerBowlerOver: UILabel!
var timer = Timer()
// start the timer
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(getUpdatedData), userInfo: nil, repeats: true)
}
#objc func getUpdatedData() {
let jsonUrlString = "url from server"
guard let url = URL(string : jsonUrlString ) else
{ return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
//get your updated data here and update it into the UI.
do {
let jsonscore = try JSONDecoder().decode(jsonScore.self, from: data)
self.VenuDetails.text = jsonscore.venuDetails
self.Comment.text = jsonscore.comment
self.bowlingTeam.text = jsonscore.bowlingTeam
self.battingTeam.text = jsonscore.battingTeam
self.ValueToDisplay.text = jsonscore.valueToDisplay
self.wickets.text = String(jsonscore.wickets)
self.overs.text = String(jsonscore.overs)
self.score.text = String(jsonscore.score)
self.striker.text = jsonscore.striker.name + "*"
self.strikerRun.text = jsonscore.striker.runs
self.nonStriker.text = jsonscore.nonStriker.name
self.nonStrikerRun.text = jsonscore.nonStriker.runs
self.strikerBowlerName.text = jsonscore.strikerBowler.name
self.strikerBowlerOver.text = jsonscore.strikerBowler.overs
print(jsonscore)
}catch let jsonErr{
print("Error serializing json:", jsonErr)
}
}.resume()
}
}
How to I write the code so that as soon as JSON is fetched from server it gets updated in the app? I used timer also but the labels in app are not updating after first update. I want to update the app label as soon as server sends the JSON file and the update should be repeatedly.
Please help me writing the code.
As #Rmaddy said your timer use is quiet a bad idea. You can see why in the apple's Energy Efficiency Guide for iOS Apps
Timers prevent the CPU from going to or staying in the idle state,
which increases energy usage and consumes battery power.
That kind of over use will end up draining the device battery.
You should use something more efficient like web sockets(As #the4kman suggested). By using web sockets you will need to change a little bit your approach. You should consider to change your server to notify the app instead of the app being constantly downloading data from the server.
Here are some web sockets libraries that you can use:
Starscream
Socket.io
SwiftWebSocket

How To Fix: "Expression is Ambiguous".

I am trying to create an app that can help you calculate sales tax on an item. Of course the app requires multiplication But I keep encountering the error:
"Type of expression is ambiguous without more context"
Can you help me? I'm new to swift so also try to explain why I am incorrect. This is my code so far:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var Item: UITextField!
#IBOutlet weak var Tax: UITextField!
#IBOutlet weak var Answer: UITextField!
#IBAction func Calculate(_ sender: Any) {
let a = Item.text
let conversionRate = Tax
let b = Int(a!)! * conversionRate
Answer.text = ("\(b)")
}
}
Thanks!!
Your primary issue is your attempt to multiply an Int and a UITextField.
You attempt to create an Int from Item.text but you make no similar attempt to convert Tax.text to a number.
There are also many other issues with your code. You are using the ! operator too much and your app will crash as a result.
And your naming conventions need to be improved. Variables and methods should start with lowercase letters.
Here's your code as it should be written:
class ViewController: UIViewController {
#IBOutlet weak var item: UITextField!
#IBOutlet weak var tax: UITextField!
#IBOutlet weak var answer: UITextField!
#IBAction func calculate(_ sender: Any) {
if let itemStr = item.text, let taxStr = tax.text, let itemVal = Int(itemStr), let taxVal = Int(taxStr) {
let result = itemVal * texVal
answer.text = "\(result)"
} else {
answer.text = "Invalid values"
}
}
}
You're trying to multiply a variable of UITextField type with a variable of Int. Try this:
class ViewController: UIViewController {
#IBOutlet weak var Item: UITextField!
#IBOutlet weak var Tax: UITextField!
#IBOutlet weak var Answer: UITextField!
#IBAction func Calculate(_ sender: Any) {
guard let a = Int(Item.text ?? "0") else { return }
guard let conversionRate = Int(Tax.text ?? "0") else { return }
let b = a * conversionRate
Answer.text = ("\(b)")
}
}

Failure when connecting iOS app to Database using Alamofire

I am trying to complete my user registration from my app.
Using Alamofire I am trying to connect my iOS app to my database via my php files/server - yet I am receiving this error message:
FAILURE: responseSerializationFailed(Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.}))
this is my code -
import Alamofire
import UIKit
class SignUpComplete: UIViewController {
let URL_USER_REGISTER = "https:/mooverdata345.co.uk/registerIPHONE.php";
#IBOutlet var Email: UITextField!
#IBOutlet var Password: UITextField!
#IBOutlet var Username: UITextField!
#IBOutlet var Firstname: UITextField!
#IBOutlet var Surname: UITextField!
#IBOutlet var HouseNo: UITextField!
#IBOutlet var Postcode: UITextField!
#IBOutlet var labelMessage: UILabel!
#IBAction func submit(_ sender: UIButton) {
let parameters: Parameters=[
"Email":Email.text!,
"Password":Password.text!,
"Firstname":Firstname.text!,
"Surname":Surname.text!,
"HouseNo":HouseNo.text!,
"Postcode":Postcode.text!
]
Alamofire.request(URL_USER_REGISTER, method: .post, parameters: parameters).responseJSON
{
response in
//printing response
print(response)
//getting the json value from the server
if let result = response.result.value {
//converting it as NSDictionary
let jsonData = result as! NSDictionary
//displaying the message in label
self.labelMessage.text = jsonData.value(forKey: "message") as! String?
}
}
}
}
I think it may be something to do with JSON. The SQL queries are working so the backend stuff is okay. And my mate's Android app is connecting okay.

Cannot assign value of type '[String]' to type 'String?' (Swift)

in swift (Xcode) for ios development, I'm trying to set my UILabel's text to the elements of an array. This is for a simple project where when you press a button: out of a array for 50 elements, the 50 elements will be randomized and then 3 will be chosen and i want those 3 to be displayed on the UILabel, but I get the error that I cannot assign value of type '[String]' to type 'String?' (Swift). Here is my code main code
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var altLabel: UILabel!
#IBOutlet weak var asianLabel: UILabel!
#IBOutlet weak var bluesLabel: UILabel!
#IBOutlet weak var classicalLabel: UILabel!
#IBOutlet weak var countryLabel: UILabel!
#IBOutlet weak var danceLabel: UILabel!
#IBOutlet weak var edmLabel: UILabel!
#IBOutlet weak var emotionalLabel: UILabel!
#IBOutlet weak var euroLabel: UILabel!
#IBOutlet weak var indieLabel: UILabel!
#IBOutlet weak var inspirationalLabel: UILabel!
#IBOutlet weak var jazzLabel: UILabel!
#IBOutlet weak var latinLabel: UILabel!
#IBOutlet weak var newAgeLabel: UILabel!
#IBOutlet weak var operaLabel: UILabel!
#IBOutlet weak var popLabel: UILabel!
#IBOutlet weak var rbLabel: UILabel!
#IBOutlet weak var reggaeLabel: UILabel!
#IBOutlet weak var rockLabel: UILabel!
#IBOutlet weak var rapLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func altButton(sender: UIButton) {
let altSongs: [String] = ["Spirits by The Strumbellas", "Ride by Twenty One Pilots", "Ophelia by The Lumineers", "Dark Necessities by Red Hot Chili Peppers", "Bored to Death by Blink-182", "HandClap by Fitz And Tantrums", "Walking An A Dream by Empire Of The Sun", "Kiss This by The Struts", "Woman Woman by AWOLNATION", "First by Cold War Kids", "Way Down We Go by Kaleo", "Gone by Jr Jr", "Genghis Khan by Miike Snow", "Stressed Out by Twenty One Pilots", "Adventure Of A Lifetime by Coldplay", "2AM by Bear Hands", "Take It From Me by KONGOS", "Soundcheck by Catfish And The Bottlemen", "Brazil by Declan McKenna", "Destruction by Joywave", "Centuries by Fallout Boy", "Castle by Hasley", "First by Cold war Kids", "Unsteady (Erich Lee Gravity Remix) by X Ambadassadors", "Best Day Of My Life by American Authors", "Hymn For The Weekend by Coldplay", "Seven Nation Army by The White Stripes", "This is Gospel by Panic! At The Disco", "Riptide by Vance Joy", "Uma Thurman by Fallout Boy", "My Song Know What You Did In The Dark (Light Em Up) by Fall Out Boy", "Radioactive by Imagine Dragons", "Car Radio by Twenty One Pilots", "Walking On A Dream by Empire Of The Sun", "Viva La Vide by Coldplay", "Left Hand Free by Alt-J", "Tear in My Heart by Twenty One Pilots", "Death Of A Bachelor by Panic! At The Disco", "Demons by Imagine Dragons", "Emperor's New Clothes by Panic! At The Disco", "I Write Sins Not Tradegies by Panic! At The Disco", "Sail by AWOLNATION", "Twice by Catfish And The Bottlemen", "Colors by Hasley", "Nobody Really Cares If You Don't Go To The Party", "Courtney Barnett", "A Sky Full Of Stars", "On Top Of The World by Imagine Dragons", "Woman Woman by AWOLNATION", "Take Me T Church by Hozier"]
var shuffled = altSongs.shuffle;
shuffled = altSongs.choose(3)
altLabel.text = shuffled //(ending brackets are in place, just not shown here. **Rest of the code is just buttons structured in same format as this one**)
I'm only a beginner at iOS developing
Code for the methods: //(choose) and (shuffle)
import Foundation
import UIKit
extension Array {
var shuffle: [Element] {
var elements = self
for index in indices.dropLast() {
guard
case let swapIndex = Int(arc4random_uniform(UInt32(count - index))) + index
where swapIndex != index else {continue}
swap(&elements[index], &elements[swapIndex])
}
return elements
}
mutating func shuffled() {
for index in indices.dropLast() {
guard
case let swapIndex = Int(arc4random_uniform(UInt32(count - index))) + index
where swapIndex != index
else { continue }
swap(&self[index], &self[swapIndex])
}
}
var chooseOne: Element {
return self[Int(arc4random_uniform(UInt32(count)))]
}
func choose(n: Int) -> [Element] {
return Array(shuffle.prefix(n))
}
}
For your error: "unexpectedly found nil while unwrapping an Optional value", have a look at my post about them, understanding '!' and '?' is crucial for Swift development:
What are '!' and '?' marks used for in Swift
Also, as other answers mentioned also, you are returning an array value instead you should give a String value and then assign it to your label.text value. For this, you can try below:
altLabel.text = "\(shuffled[0]), \(shuffled[1]), \(shuffled[2])"
var shuffled = altSongs.shuffle; // Line 1
shuffled = altSongs.choose(3) // Line 2
altLabel.text = shuffled // Line 3
Replace the above code with
let shuffled = altSongs.shuffle;
let selectedThree = shuffled.choose(3)
altLabel.text = selectedThree[0] + " " + selectedThree[1] + " " + selectedThree[2]
Here you shuffle the array and put it in shuffled then pick array of first three elements in selectedThree.
selectedThree is an array of strings. We can either iterate the array to get the string or just use the first three elements.
I don't know where did you define shuffle and choose and I think you implemented them incorrectly.
I think you can just create a choose extension method that returns an array of strings:
func chooseFromArray<T>(array: [T], amountToChoose: Int) -> [T] {
var returnVal: [T] = []
var arrayCopy = array
for _ in 0..<amountToChoose {
let chosen = Int(arc4random_uniform(UInt32(arrayCopy.count)))
returnVal.append(arrayCopy[chosen])
arrayCopy.removeAtIndex(chosen)
}
return returnVal
}
And then you can just call it like this:
var chosenSongs = chooseFromArray(altSongs, amountToChoose: 3)
You said you want to display an array in a label. So I guess you want to do it like this?
altLabel.text = chosenSongs.joinWithSeparator(", ")
I think that should fix it.

Resources