How to access data outside of an if statement in swift - arrays

I want to use the variables or array outside of the if statement. In javascript, you can hoist the variables outside of the if statement, but not sure how to do it in swift? I have tried using a struct, but I am not sure which direction to go in. I want to use num in a function and increment through each array. But, need to be able to access variables through the file.
let jsonWithArrayRoot = try? JSONSerialization.jsonObject(with: fileData, options: [])
var zone: Int
var sound: String
var distance: Int
var title: String
var duration: Int
var type: String
var color: Int
var num:Int
num=0
if let array = jsonWithArrayRoot as? [AnyObject] {
let json = array[num]
test = array
zone = json["zone"] as? Int ?? 0
sound = json["sound"] as? String ?? ""
distance = json["distance"] as? Int ?? 0
title = json["title"] as? String ?? ""
duration = json["duration"] as? Int ?? 0
type = json["type"] as? String ?? ""
color = json["color"] as? Int ?? 0
print(zone)
}
//I want to access variables and later on
print(zone)
print(sound)
print(distance)
print(title)
print(type)
print(color)

Use guard.
guard let array = jsonWithArrayRoot as? [AnyObject] else { return }
let json = array[num]
zone = json["zone"] as? Int ?? zone
sound = json["sound"] as? String ?? sound
distance = json["distance"] as? Int ?? distance
title = json["title"] as? String ?? title
duration = json["duration"] as? Int ?? duration
type = json["type"] as? String ?? type
color = json["color"] as? Int ?? color
Use array variable anywhere now.

You should give your variables default values right away, and then change them later based on the JSON data. Your current code puts the default values inside the JSON parsing block, which means that the variables would have no value if the if let array = jsonWithArrayRoot... fails.
Personally, I would also refactor the as? Int ?? lines to something prettier, but that's another issue.
Here's your code with a minimal fix that should work nicely:
let jsonWithArrayRoot = try? JSONSerialization.jsonObject(with: fileData, options: [])
var zone = 0
var sound = ""
var distance = 0
var title = ""
var duration = 0
var type = ""
var color = 0
var num = 0
if let array = jsonWithArrayRoot as? [AnyObject] {
let json = array[num]
zone = json["zone"] as? Int ?? zone
sound = json["sound"] as? String ?? sound
distance = json["distance"] as? Int ?? distance
title = json["title"] as? String ?? title
duration = json["duration"] as? Int ?? duration
type = json["type"] as? String ?? type
color = json["color"] as? Int ?? color
}

Related

I am able to access dataArray from view controller file but having an error Fatal error: Index out of range?

This is my code I am able to download data from API and data is also stored in dataArray but if this array is accessed from another view controller or within this class only it shoes error.
import Foundation
import Alamofire
class DashboardData {
struct User {
var StateName: String
var schemes: Int
var trainingCenter: Int
var DDSubmitted: Int
var ddApproved:Int
var trainingBatches: Int
var ongoingBatches : Int
var closedBatches: Int
var trainingCandidates: Int
var underTraining: Int
var droppedOut: Int
var trained: Int
var placements: Int
var totalJobs: Int
var consideredPlaced:Int
var averageSalary:Int
init(_ dictionary: [String: Any]) {
self.StateName = dictionary["StateName"] as? String ?? ""
self.schemes = dictionary["No_of_scheme"] as? Int ?? 0
self.trainingCenter = dictionary["Training_Centres_Proposed_by_OPS"] as? Int ?? 0
self.DDSubmitted = dictionary["DD_done"] as? Int ?? 0
self.ddApproved = dictionary["DD_approved"] as? Int ?? 0
self.trainingBatches = dictionary["Total_Batches_till_Date"] as? Int ?? 0
self.ongoingBatches = dictionary["Batches_in_Progress"] as? Int ?? 0
self.closedBatches = dictionary["Closed_Batches"] as? Int ?? 0
self.trainingCandidates = dictionary["Candidates_Enrolled"] as? Int ?? 0
self.underTraining = dictionary["Candidates_Under_Training"] as? Int ?? 0
self.droppedOut = dictionary["Candidates_Dropped_out"]as? Int ?? 0
self.trained = dictionary["Candidates_Trained"]as? Int ?? 0
self.placements = dictionary["Total_interviews"] as? Int ?? 0
self.totalJobs = dictionary["Total_offered_jobs"] as? Int ?? 0
self.consideredPlaced = dictionary["Consider_placed"] as? Int ?? 0
self.averageSalary = dictionary["Average_salary"]as? Int ?? 0
}
}
var dataArray = [User]()
func downloadData(){
let defaultValues = UserDefaults.standard
let userID = defaultValues.string(forKey: "userid")
let url = URL(string: "http://192.168.1.241:9011/api/\(userID!)")
//`print(url!)
// credentials
let user = "***"
let password = "***"
let credentialData = "\(user):\(password)".data(using: String.Encoding.utf8)!
let base64Credentials = credentialData.base64EncodedString(options: [])
let headers = ["Authorization": "Basic \(base64Credentials)"]
Alamofire.request(url!,
method: .get,
parameters: nil,
encoding: URLEncoding.default,
headers:headers)
.validate()
.responseJSON { response in
if let result = response.result.value{
print(result)
let jsonArray = result as! [Dictionary<String, AnyObject>]
for dic in jsonArray{
// model.append(User(dic))// adding now value in Model array
self.dataArray.append(User(dic))// adding now value in dataArray
}
print(self.dataArray[0].StateName)
}else{
print("Error")
}
}
if i do//print(self.dataArray[0].StateName) here xcode shows error
}
could any one expalin me where i am lagging?
Guess your jsonArray is empty for some reason and it makes your dataArray to stay empty as well. To avoid this error replace your print line with with
print(self.dataArray.first?.StateName)
or this for more informative log
if self.dataArrat.isEmpty {
print("Data array is empty")
} else {
// now we can be sure that our array has at leat one element
print(self.dataArray[0].StateName)
}

Retrieve JSON data in swift

I can't retrieve and print the jobDate data. what's wrong my code?
let retrievetime = UserDefaults().value(forKey: "retrieve")as? NSDictionary
print(retrievetime!)
if let job = retrievetime!["jobs"] as? [String:Any], let jobTitle = job["jobDate"] as? String {
self.navigationItem.title = jobTitle
print(jobTitle)
}
//self.navigationItem.title = "Test"
print retrieve time! json output:
{
jobs =({
jobDate = "2017-08-31";
jobEndTime = 1504144800;
jobID = 87;
jobTime = 1504137600;
});
message = "Retrieve Sucessfully";
result = success;
}
First of all use use the standard singleton instance and optional bindings to safely get data from UserDefaults – unless you registered the key/value pairs.
Second of all never use value(forKey with UserDefaults and never use NSDictionary in Swift unless the compiler tells you to do.
Finally as mentioned in the comments the value for key jobs – as the plural form implies – is an array, you have to get the first object of the array
if let retrievetime = UserDefaults.standard.object(forKey: "retrieve") as? [String:Any] {
print(retrievetime)
if let jobs = retrievetime["jobs"] as? [[String:Any]],
let job = jobs.first,
let jobTitle = job["jobDate"] as? String {
self.navigationItem.title = jobTitle
print(jobTitle)
}
}
let retrievetime = UserDefaults().value(forKey: "retrieve")as? NSDictionary
print(retrievetime!)
if let job = retrievetime!["jobs"] as? [String:Any], let jobTitle = ((retrievetime!["jobs"] as? [String:Any])[0] as? NSDictionary)["jobDate"] as? String {
self.navigationItem.title = jobTitle
print(jobTitle)
}
//self.navigationItem.title = "Test"

Swift Array sort with JSON

Data:
tried get data from JSON
has code with Swift run on Playground
var roadWayStatusArray = [AnyObject]()
let url = URL(string: "http://od.moi.gov.tw/data/api/pbs")
let data = try? Data(contentsOf: url!)
let json = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
if let results = json as? [String : AnyObject] {
if let result = results["result"] as? [[String : AnyObject]] {
for data in result {
let happendate = data["happendate"] as? String
roadWayStatusArray.append(data as AnyObject!)
}
}
}
I has tried using roadWayStatusArray.sort(by: >) but Xcode report me Ambiguous reference member '>'
How to create sort by hapendate or happentime
Your roadWayStatusArray is an array of AnyObject. There is no > operator defined for AnyObject.
The objects in roadWayStatusArray are actually dictionaries that look like this:
{
UID = "10602090007-0";
areaNm = "\U4e2d\U5c71\U9ad8\U901f\U516c\U8def-\U570b\U9053\Uff11\U865f";
comment = "\U5317\U4e0a.\U4e2d\U58e2\U670d\U52d9\U5340 \U51fa\U53e3\U531d\U9053\U4e2d \U53f3\U5074 \U5c0f\U5ba2\U8eca\U505c\U653e\Uff0c\U99d5\U99db\U7591\U4f3c\U7761\U8457\U4e86";
direction = "\U5317\U4e0a";
happendate = "2017-02-09";
happentime = "01:08:00.0000000";
modDttm = "2017-02-09 01:15:43.603";
region = N;
road = "";
roadtype = "\U5176\U4ed6";
srcdetail = "\U71b1\U5fc3\U807d\U773e";
x1 = "121.73558";
y1 = "25.12263";
}
You need to call sort(by:) with a closure that determines the sort order. For example, if you want to sort by happendate and then happentime:
roadWayStatusArray.sort(by: { (lhsAny, rhsAny) -> Bool in
let lhs = lhsAny as? [String: AnyObject]
let rhs = lhsAny as? [String: AnyObject]
let lhsKey = (lhs?["happendate"] as? String ?? "", lhs?["happentime"] as? String ?? "")
let rhsKey = (rhs?["happendate"] as? String ?? "", rhs?["happentime"] as? String ?? "")
return lhsKey < rhsKey
})

Ambiguous use of subscript

This block of code was working and now it's not. I get the error "Ambiguous use of 'subscript'" on the lat and long variables. What's going on? Is this because of a Swift update?
func showPrecincts() {
var urlBoundaries = "http://www.oklahomadata.org/boundary/1.0/boundary/?contains=" + "\(coords!.latitude)" + "," + "\(coords!.longitude)" + "&sets=precincts"
Alamofire.request(.GET, urlBoundaries, parameters: ["foo": "bar"])
.responseJSON { response in
if let data = response.result.value {
let nestedCoordinates = data.valueForKeyPath("objects.simple_shape.coordinates") as! Array<AnyObject>
let bug1 = nestedCoordinates.first as! Array<AnyObject>
let bug2 = bug1.first as! Array<AnyObject>
let coordinates = bug2.first as! Array<AnyObject>
var convertedCoords: [CLLocationCoordinate2D] = []
for individualCoordinates in coordinates {
let lat = (individualCoordinates[1] as! Double)
let long = (individualCoordinates[0] as! Double)
var newCoords = CLLocationCoordinate2DMake(lat, long)
convertedCoords.append(newCoords)
}
print(convertedCoords)
}
coordinates is cast to an array of AnyObject.
The compiler does not know that it's actually an array of Double in another array.
Downcast coordinates to Array<[Double]>
let coordinates = bug2.first as! Array<[Double]>
then you can get the elements without further type casting
let lat = individualCoordinates[1]
let long = individualCoordinates[0]

Converting String array into Int Array Swift

After converting the String values into Int values in an array, when I print to the logs, all I get is: [0, 0, 0, 0] when the output should be: ["18:56:08", "18:56:28", "18:57:23", "18:58:01"]
(without the quotations and the : colon).
I'm converting the string array, directly after the values have been added to the string array. I'm assuming that I'm not converting the values at the right time, or that my methods are placed wrong and that's why I get the 0 0 0 0 output.
Here is my ViewController code:
class FeedTableViewController: UITableViewController {
var productName = [String]()
var productDescription = [String]()
var linksArray = [String]()
var timeCreatedString = [String]()
var minuteCreatedString = [String]()
var intArray = Array<Int>!()
override func viewDidLoad() {
super.viewDidLoad()
var query = PFQuery(className: "ProductInfo")
query.findObjectsInBackgroundWithBlock ({ (objects, error) -> Void in
if let objects = objects {
self.productName.removeAll(keepCapacity: true)
self.productDescription.removeAll(keepCapacity: true)
self.linksArray.removeAll(keepCapacity: true)
self.timeCreatedString.removeAll(keepCapacity: true)
for object in objects {
self.productName.append(object["pName"] as! String)
self.productDescription.append(object["pDescription"] as! String)
self.linksArray.append((object["pLink"] as? String)!)
// This is where I'm querying and converting the date:
var createdAt = object.createdAt
if createdAt != nil {
let date = NSDate()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM/dd/YYY/HH/mm/ss"
let string = dateFormatter.stringFromDate(createdAt as NSDate!)
var arrayOfCompontents = string.componentsSeparatedByString("/")
self.timeCreatedString.append("\(arrayOfCompontents[0]) \(arrayOfCompontents[1]) \(arrayOfCompontents[2])")
self.minuteCreatedString.append("\(arrayOfCompontents[3]):\(arrayOfCompontents[4]):\(arrayOfCompontents[5])")
self.intArray = self.minuteCreatedString.map { Int($0) ?? 0}
print("INT ARRAY \(self.intArray)")
print(self.minuteCreatedString.map { Int($0) ?? 0})
print(self.minuteCreatedString)
}
self.tableView.reloadData()
}
}
})
}
When I tried this in another ViewController in the viewDidLoad method without Parse/queries happening, I get the correct output: a converted array of Ints. I'm assuming there's an issue of when and where i'm converting the Strings into Ints.
In what order/where should I convert the array of Strings into an array of Ints? Should I convert from Date to Int instead? If so, how do I do that? Am i doing something else wrong? I'm awfully confused....
Any help is very much appreciated!
If you have an NSDate object anyway, you can create the date string with the date formatter
let createdAt = NSDate() // or give date object
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm:ss"
let string = dateFormatter.stringFromDate(createdAt) // "18:56:08"
Or if you want the integer values of hours, minutes and seconds, use NSDateComponents:
let comps = NSCalendar.currentCalendar().components([.Hour, .Minute, .Second], fromDate: createdAt)
let hour = comps.hour
let minute = comps.minute
let seconds = comps.second
let intArray = [hour, minute, second]

Resources