I can't figure out why my view controller is not showing the data, even though I can see it in the output window.
Output:
Muḩāfaz̧at Al Jīzah
Clear
88.0
my code:
override func viewDidLoad() {
super.viewDidLoad()
loadCurrentWeather = currentWeatherData()
loadCurrentWeather.downloadWeatherData {
//setting uo UI to download data
self.updateTodayUI()
}
}
func updateTodayUI() {
locationLabel.text = loadCurrentWeather.cityName
weatherTypeLabel.text = loadCurrentWeather.weatherType
currentTempLabel.text = "\(loadCurrentWeather.currentTemp)"
weatherTypeImage.image = UIImage(named: loadCurrentWeather.weatherType)
}
My view controller in Xcode:
My view controller on iphone:
currentweatherData the code where I'm downloading the data form.
import UIKit
import Alamofire
class currentWeatherData {
var cityNameone: String!
var dateone: String!
var weatherTypeone: String!
var currentTempone: Double!
var cityName: String {
if cityNameone == nil {
cityNameone = ""
}
return cityNameone
}
var date: String {
if dateone == nil {
dateone = ""
}
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .none
let currentDate = dateFormatter.string(from: Date())
self.dateone = "Today, \(currentDate)"
return dateone
}
var weatherType: String{
if weatherTypeone == nil{
weatherTypeone = ""
}
return weatherTypeone
}
var currentTemp: Double {
if currentTempone == nil {
currentTempone = 0.0
}
return currentTempone
}
func downloadWeatherData(completed: DownloadComplete){
// to tell alamofire where to download the data
let weatherURL = URL (string: currentWeatherURL)!
Alamofire.request(weatherURL).responseJSON{ response in
let result = response.result
if let dictionary = result.value as? Dictionary<String, AnyObject>{
if let name = dictionary["name"] as? String {
self.cityNameone = name.capitalized
print(self.cityNameone ?? "No city name")
}
if let weather = dictionary["weather"] as? [Dictionary<String, AnyObject>]{
if let main = weather[0]["main"] as? String {
self.weatherTypeone = main.capitalized
print(self.weatherTypeone ?? "No weather type")
}
}
if let main = dictionary["main"] as? Dictionary<String, AnyObject> {
if let currentTemperature = main["temp"] as? Double {
let kelvintoFarenheit = (currentTemperature * (9/5) - 459.67)
let totalKelvinToFarenheit = Double(round(10 * kelvintoFarenheit/10))
self.currentTempone = totalKelvinToFarenheit
print(self.currentTempone ?? .nan)
}
}
}
}
completed()
}
}
Is problem with my code or my view controller? Is it something wrong with my constraints?
I can't seem to figure it out.
You are calling completed too early - before the JSON response arrives. You have to call it inside the closure of the responseJSON call instead:
Alamofire.request(weatherURL).responseJSON { response in
let result = response.result
// ...
completed()
}
I cannot see all of your code to troubleshoot, but you may have a concurrency issue. Try putting the call to updateTodayUI inside of viewDidLoad(_:) inside of an async block like this:
DispatchQueue.main.async {
updateTodayUI()
}
You can find more information on dispatch queues and concurrency in the documentation.
Update:
In the end, I go to the local SQLite database and replace the NULL value to
"unkown". This works!
I am using FMDatabaseQueue to search an existing sqlite database in iOS.
//Data model
import Foundation
import UIKit
class scoreModel: NSObject {
var lessonName:String = String()
var lessonCode:String = String()
var creditPoint:Double = Double()
var totalStudentNumber:Int = Int()
var teacherName:String = String()
var semesterName:String = String()
var scoreValue:String = String()
var studentCount:Int = Int()
}
Unfortunately, there are some "" string in my database. Like:
teacherName ""
scoreValue ""
While searching, Xcode alerted that
"Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value" in line "teacherName".
I don't need these "" results necessarily as they are not important. How can I fix it?
func queryDB(sql: String) -> NSMutableArray {
openDB ();
let resultArray = NSMutableArray()
SQLiteManager.shareInstance.dbQueue?.inDatabase { (db) in
let dbResult: FMResultSet! = db.executeQuery(sql, withArgumentsIn:[])
if (dbResult != nil)
{
while dbResult.next() {
let model:scoreModel = scoreModel()
model.lessonName = String(dbResult.string(forColumn: "lessonName")!)
model.lessonCode = String(dbResult.string(forColumn: "lessonCode")!)
model.creditPoint = Double(dbResult.double(forColumn: "creditPoint"))
model.semesterName = String(dbResult.string(forColumn: "semesterName")!)
model.teacherName = String(dbResult.string(forColumn: "teacherName")!)
model.totalStudentNumber = Int(dbResult.int(forColumn: "totalStudentNumber"))
model.scoreValue = String(dbResult.string(forColumn: "scoreValue")!)
model.studentCount = Int(dbResult.int(forColumn: "studentCount"))
resultArray.add(model)
}
}
}
return resultArray
}
Thank you!
The problem is that dbResult.string(forColumn: "teacherName") is returning an optional with a nil value, so maybe this object had a nil value when you saved it. It doesn't really matter, what's important is how you want to treat objects that don't have all the fields that you're expecting. The question you need to ask yourself is "how should I handle the case where the thing in the database doesn't have a teacher name?" Here are two ideas:
In order to be considered "valid," everything in the database needs to have all the properties I'm expecting, otherwise I ignore it. That would look like this:
while dbResult.next() {
if let lessonName = String(dbResult.string(forColumn: "lessonName")),
let lessonCode = String.dbResult.string(forColumn: "lessonCode")),
let creditPoint = Double(dbResult.double(forColumn: "creditPoint")),
let semesterName = String(dbResult.string(forColumn: "semesterName")),
let teacherName = String(dbResult.string(forColumn: "teacherName")),
let totalStudentNumber = Int(dbResult.int(forColumn: "totalStudentNumber")),
let scoreValue = String(dbResult.string(forColumn: "scoreValue")),
let studentCount = Int(dbResult.int(forColumn: "studentCount")) {
let model = scoreModel()
model.lessonName = lessonName
... // set lessonCode, etc
resultArray.add(model)
}
}
Another choice is to provide default values using optional coalescing or similar.
while dbResult.next() {
let lessonName = String(dbResult.string(forColumn: "lessonName")) ?? ""
let lessonCode = String.dbResult.string(forColumn: "lessonCode")) ?? ""
let creditPoint = Double(dbResult.double(forColumn: "creditPoint")) ?? ""
let semesterName = String(dbResult.string(forColumn: "semesterName")) ?? ""
let teacherName = String(dbResult.string(forColumn: "teacherName")) ?? ""
let totalStudentNumber = Int(dbResult.int(forColumn: "totalStudentNumber")) ?? 0
let scoreValue = String(dbResult.string(forColumn: "scoreValue")) ?? ""
let studentCount = Int(dbResult.int(forColumn: "studentCount")) ?? 0
let model = scoreModel()
model.lessonName = lessonName
... // set lessonCode, etc
resultArray.add(model)
}
I am trying to get values from my firebasedb, on run, ref.observeSingleEvent(of: .value, with: { snapshot in causes fatal error unexpectedly found nil while unwrapping an Optional value. As im sure you can tell... I have no idea what im doing... Thank you in advance...
func geths() -> Int{
var sch:Int = 0
var nam:String = ""
print("start geths")
ref.observeSingleEvent(of: .value, with: { snapshot in
if (snapshot.exists()){
print("snapexist")
if let snapval = snapshot.value as? [String:AnyObject]{
let hs = snapval["hs"] as? String
let name = snapval["name"] as? String
self.hso = hs!
self.nameo = name!
nam = self.nameo
if let myNumber = NumberFormatter().number(from: self.hso) {
let i = myNumber.intValue
sch = i
}else{
sch = 0
}
}else{
print("error")
}
}else{
print("error")
}
})
return sch
}
EDIT************
still dont work :( same errors
func geths() -> Int{
var sch:Int = 0
var nam:String = ""
print("start geths")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if (snapshot.exists()){
print("snapexist")
let snapval = snapshot.value as? NSDictionary
let hs = snapval?["hs"] as? String ?? ""
let name = snapval?["name"] as? String ?? ""
if (hs != nil){
self.hso = hs
}else{
self.hso = "0"
}
if (name != nil){
self.nameo = name
}else{
self.nameo = "bob"
}
nam = self.nameo
if let myNumber = NumberFormatter().number(from: self.hso) {
let i = myNumber.intValue
sch = i
}else{
sch = 0
}
}else{
print("error")
}
})
return sch
}
create firebase reference like this :
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
for more details go though this link :
https://firebase.google.com/docs/database/ios/read-and-write
You must initialize the ref variable, you was only declare it by
var ref: FIRDatabaseReference!
For example
var ref: FIRDatabaseReference! = FIRDatabase.database().reference(withPath: "hs")
You may read example about using Firebase here:
https://www.raywenderlich.com/139322/firebase-tutorial-getting-started-2
Here's what I am trying to do :
let courseName = "Bachelor of Tourism Administration(B.T.A)".condensedWhitespace
let upperCaseCourseName = courseName.uppercaseString
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") { $0.0 + String($0.1.characters.first!) }
let upperCasecourseFirstCharcters = extrctCourseName
print(upperCasecourseFirstCharcters) // output : "BOTA" but i want "BTA"
as you see that my outPut of "Bachelor of Tourism Administration(B.T.A)" is BOTA but the desired output is BTA because word of is starting from a lowerCase and i want to ignore that word in my this method , how am gonna do that any idea ?
let courseName = "Bachelor of Tourism Administration(B.T.A)" //.condensedWhitespace
var newString = ""
let array : NSArray = courseName.componentsSeparatedByString(" ")
for chr in array {
let str = chr as! NSString
if str.lowercaseString != str{
if newString.characters.count > 0{
newString = newString.stringByAppendingString(" "+(str as String))
continue
}
newString = newString.stringByAppendingString((str as String))
}
}
let upperCaseCourseName = newString.uppercaseString
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") { $0.0 + String($0.1.characters.first!) }
let upperCasecourseFirstCharcters = extrctCourseName
print(upperCasecourseFirstCharcters)
//This will defiantly meet to your problem/. Let me know if it works for u or not
You can paste this into a playground:
extension String {
func array() -> [String] {
return self.componentsSeparatedByString(" ")
}
func abbreviate() -> String {
var output = ""
let array = self.array()
for word in array {
let index = word.startIndex.advancedBy(0)
let str = String(word[index])
if str.lowercaseString != str {
output += str
}
}
return output
}
}
let courseName = "Bachelor of Tourism Administration(B.T.A)".abbreviate()
print(courseName) // prints BTA
A clean approach would be:
extension Character
{
public func isUpper() -> Bool
{
let characterString = String(self)
return (characterString == characterString.uppercaseString) && (characterString != characterString.lowercaseString)
}
}
let courseName = "Bachelor of Tourism Administration(B.T.A)"
let upperCaseCourseName = courseName
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") {
if($0.1.characters.first!.isUpper()) {
return $0.0 + String($0.1.characters.first!)
}else {
return $0.0
}
}
I have EKParticipant object with description looks like:
item description: EKAttendee <0x1c0b7d90> {UUID = 116B99AB9-41AC-4741-A288-B67172298625; name = Snaggy Snags; email = snaggy#gmail.com; status = 4; role = 1; type = 1}
How to safety split this string to Dictionary in order to fetch after email key value?
This is what I did so far:
extension String {
func split(splitter: String) -> Array<String> {
let regEx = NSRegularExpression(pattern: splitter, options: NSRegularExpressionOptions(), error: nil)!
let stop = "SomeStringThatYouDoNotExpectToOccurInSelf"
let modifiedString = regEx.stringByReplacingMatchesInString(self, options: NSMatchingOptions(), range: NSMakeRange(0, countElements(self)), withTemplate: stop)
return modifiedString.componentsSeparatedByString(stop)
}
func removeCharsFromEnd(count:Int) -> String{
let stringLength = countElements(self)
let substringIndex = (stringLength < count) ? 0 : stringLength - count
return self.substringToIndex(advance(self.startIndex, substringIndex))
}
}
var str = "item description: EKAttendee <0x1c0b7d90> {UUID = 16B99AB9-41AC-4742-A288-B67172299625; name = Snaggy Snags; email = snaggy#gmail.com; status = 4; role = 1; type = 1}"
var newStr = str.split("\\{")[1]
newStr = newStr.removeCharsFromEnd(1)
So now newStr equals:
UUID = 16B99AB9-41AC-4741-A288-B67172298625; name = Snaggy Snags; email = snaggy#gmail.com; status = 4; role = 1; type = 1
What next?
Thanks,
You can use componentsSeparatedByString method to extract your elements as follow:
extension String {
var elements:(udid: String, name: String, email: String, status: Int, role: Int, type: Int) {
let components = componentsSeparatedByString("; ")
if components.count == 6 {
let udid = components[0].componentsSeparatedByString(" = ").last ?? ""
let name = components[1].componentsSeparatedByString(" = ").last ?? ""
let email = components[2].componentsSeparatedByString(" = ").last ?? ""
let status = components[3].componentsSeparatedByString(" = ").last ?? ""
let role = components[4].componentsSeparatedByString(" = ").last ?? ""
let type = components[5].componentsSeparatedByString(" = ").last ?? ""
return (udid, name, email, (status as NSString).integerValue, (role as NSString).integerValue, (type as NSString).integerValue)
}
return ("","","",0,0,0)
}
}
let input = "UUID = 16B99AB9-41AC-4741-A288-B67172298625; name = Snaggy Snags; email = snaggy#gmail.com; status = 4; role = 1; type = 1"
let result = input.elements // (.0 "16B99AB9-41AC-4741-A288-B67172298625", .1 "Snaggy Snags", .2 "snaggy#gmail.com", .3 4, .4 1, .5 1, .6 "UUID = 16B99AB9-41AC-4741-A288-B67172298625; name = Snaggy Snags; email = snaggy#gmail.com; status = 4; role = 1; type = 1")
println(result.udid) // "16B99AB9-41AC-4741-A288-B67172298625"
println(result.name) // "Snaggy Snags"
println(result.email) // "snaggy#gmail.com"
println(result.status.description) // "4"
println(result.role.description) // "1"
println(result.type.description) // "1"
You can also use String's method hasPrefix to make sure you are grabbing the right info from your elements even if they return unordered as follow:
extension String {
var elements:(udid: String, name: String, email: String, status: Int, role: Int, type: Int) {
let components = componentsSeparatedByString("; ")
var udid = "", name = "", email = "", status = 0, role = 0, type = 0
for item in components {
println(item)
if item.hasPrefix("UUID = "){
udid = item.substringWithRange(Range(start: advance(item.startIndex, 7), end: item.endIndex))
}
if item.hasPrefix("name = "){
name = item.substringWithRange(Range(start: advance(item.startIndex, 7), end: item.endIndex))
}
if item.hasPrefix("email = "){
email = item.substringWithRange(Range(start: advance(item.startIndex, 8), end: item.endIndex))
}
if item.hasPrefix("status = "){
status = (item.substringWithRange(Range(start: advance(item.startIndex, 9), end: item.endIndex)) as NSString).integerValue
}
if item.hasPrefix("role = "){
role = (item.substringWithRange(Range(start: advance(item.startIndex, 7), end: item.endIndex)) as NSString).integerValue
}
if item.hasPrefix("type = "){
type = (item.substringWithRange(Range(start: advance(item.startIndex, 7), end: item.endIndex)) as NSString).integerValue
}
}
return (udid, name, email, status, role, type)
}
}
let input = "UUID = 16B99AB9-41AC-4741-A288-B67172298625; name = Snaggy Snags; email = snaggy#gmail.com; status = 4; role = 1; type = 1"
let elements = input.elements // (.0 "16B99AB9-41AC-4741-A288-B67172298625", .1 "Snaggy Snags", .2 "snaggy#gmail.com", .3 4, .4 1, .5 1)
let udid = elements.udid // "16B99AB9-41AC-4741-A288-B67172298625"
let name = elements.name // "Snaggy Snags"
let email = elements.email // "snaggy#gmail.com"
let status = elements.status.description // "4"
let role = elements.role.description // "1"
let type = elements.type.description // "1"
Here is method that fetches email or returns nil if something goes wrong:
func fetchEmailIfExists(str:String) -> String?{
var email:String?
for item:String in str.split(";"){
if item.contains("email"){
var emailPart = item.trim()
if emailPart.componentsSeparatedByString("=").first?.trim() == "email" {
if let temp:String = emailPart.componentsSeparatedByString("=").last?.trim(){
return temp
}
}
}
}
return email
}
Helpers
extension String {
func split(splitter: String) -> Array<String> {
let regEx = NSRegularExpression(pattern: splitter, options: NSRegularExpressionOptions(), error: nil)!
let stop = "SomeStringThatYouDoNotExpectToOccurInSelf"
let modifiedString = regEx.stringByReplacingMatchesInString(self, options: NSMatchingOptions(), range: NSMakeRange(0, countElements(self)), withTemplate: stop)
return modifiedString.componentsSeparatedByString(stop)
}
func trim() -> String {
return self.stringByTrimmingCharactersInSet(.whitespaceAndNewlineCharacterSet())
}
func contains(find: String) -> Bool{
return self.rangeOfString(find) != nil
}
}
This a perfect use case for NSScanner. The stringToDictionary takes a String like yours and returns a dictionary of [String: String]. You can use it for any string in your format, regardless of the amount of key/value pairs. However it will break down when the values contain semicolons or equal signs.
let string = "item description: EKAttendee <0x1c0b7d90> {UUID = 116B99AB9-41AC-4741-A288-B67172298625; name = Snaggy Snags; email = snaggy#gmail.com; status = 4; role = 1; type = 1}"
var dictionary = stringToDictionary(string)
func stringToDictionary(input: String) -> [String: String] {
var output = [String: String]()
let scanner = NSScanner(string: input)
let separatingCharacters = NSCharacterSet(charactersInString: ";}")
scanner.scanUpToString("{", intoString: nil)
scanner.scanString("{", intoString: nil)
var key: NSString?, value: NSString?
while !scanner.atEnd {
scanner.scanUpToString(" =", intoString: &key)
scanner.scanString("= ", intoString: nil)
scanner.scanUpToCharactersFromSet(separatingCharacters, intoString: &value)
scanner.scanCharactersFromSet(separatingCharacters, intoString: nil)
if let key = key as? String, value = value as? String {
output[key] = value
}
}
return output
}