I am trying to take information from a Firebase snapshot of each child within a certain branch of the database and pass it into a function which uses a google API request. However, instead of passing in one lot of information, completing the function, and then doing the same for the next lot of information it is instead passing all the information all at once. This is leading the url only getting one address instead of all of them. I am getting the error "Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444" too.
Code For Getting The Snapshot:
func fetchSalonsForMarkers() {
let ref = Database.database().reference()
let _ = ref.child("user_profile").observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.value as! [String : AnyObject] {
let snapshotChildData = child.value as! [String : AnyObject]
var uniqueSalonData = Salon()
for (key, value) in snapshotChildData {
uniqueSalonData.acceptedTerms = snapshotChildData["acceptedterms"] as? String
uniqueSalonData.acceptedTermsModdifiedAt = snapshotChildData["acceptedtermsmoddifiedat"] as? String
uniqueSalonData.businessName = snapshotChildData["businessname"] as? String
uniqueSalonData.companyAddressCounty = snapshotChildData["companyaddresscounty"] as? String
uniqueSalonData.companyAddressPostCode = snapshotChildData["companyaddresspostcode"] as? String
uniqueSalonData.companyAddressStreet = snapshotChildData["companyaddressstreet"] as? String
uniqueSalonData.companyAddressTown = snapshotChildData["companyaddresstown"] as? String
uniqueSalonData.companyEmail = snapshotChildData["companyemail"] as? String
uniqueSalonData.companyMobilePhone = snapshotChildData["companymobilephone"] as? Int
uniqueSalonData.ownerFirstName = snapshotChildData["ownerFirstName"] as? String
uniqueSalonData.ownerLastName = snapshotChildData["ownerLastName"] as? String
uniqueSalonData.stripeCustomerId = snapshotChildData["stripeCustomerId"] as? String
}
self.salons.append(uniqueSalonData)
self.longLatLookUp(street: uniqueSalonData.companyAddressStreet ?? "Street Error", town: uniqueSalonData.companyAddressTown ?? "Town Error", county: uniqueSalonData.companyAddressCounty ?? "County Error", postcode: uniqueSalonData.companyAddressPostCode ?? "Postcode Error", businessName: uniqueSalonData.businessName ?? "Name Error")
}
})
}
Code For API Call(Google Geocoding)
func longLatLookUp(street: String, town : String, county: String, postcode: String, businessName: String) {
let completeSalonAddress = ("\(street) \(county) Scotland")
let formattedSalonAddress = completeSalonAddress.replacingOccurrences(of: " ", with: "")
let url = URL(string: "https://maps.googleapis.com/maps/api/geocode/json?address=\(formattedSalonAddress)&key=APIKEY")!
//API Key does have valid key.
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
let json = try? JSONDecoder().decode(Welcome.self, from: data)
let salonLat = json?.results[0].geometry.location.lat
let salonLng = json?.results[0].geometry.location.lng
self.addMarkers(lat: salonLat!, lng: salonLng!, businessName: businessName)
}
task.resume()
}
Code For Adding Markers(For Context):
func addMarkers(lat: Double, lng: Double, businessName: String) {
DispatchQueue.main.async {
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: lat , longitude: lng)
marker.title = businessName
marker.snippet = "Test"
marker.appearAnimation = GMSMarkerAnimation.pop
let markerPic: UIImage = UIImage(named: "AppIcon")!
marker.icon = markerPic
marker.map = self.mapView
}
}
Related
I am wondering what i am doing wrong . I am trying to understand how to use urlsession and codable protocol using JSONDecoder. When i use JSONDecoder i am getting the following error message :
keyNotFound(CodingKeys(stringValue: "name", intValue: nil), my resaponse contain ''name'' . But when i use JSONSerialization, I am able to print the response . If someone can explain me.
Code using JSONDecoder
struct Business:Codable {
let name: String
enum CodingKeys: String, CodingKey {
case name = "name"
}
init(from decoder: Decoder) throws {
let value = try decoder.container(keyedBy: CodingKeys.self)
self.name = try value.decode(String.self, forKey:CodingKeys.name)
}
}
let task = session.dataTask(with: request) { (data, response, error) in
if let response = response as? HTTPURLResponse {
print(response)
} else{
print("error")
}
guard let data = data else {return}
do {
let business = try JSONDecoder().decode(Business.self, from: data)
print(business.name)
} catch {
print("Error parsing JSON: \(error)")
}
}
task.resume()
Code using JSONSerialization
struct Business: Decodable {
let name: String
let displayAddress: String
let categories: String
let imageUrl : String
init(json: [String:Any]) {
name = json["name"] as? String ?? ""
displayAddress = json["location"] as? String ?? ""
categories = json["categories"] as? String ?? ""
imageUrl = json["image_url"] as? String ?? ""
}
}
let task = session.dataTask(with: request) { (data, response, error) in
if let response = response as? HTTPURLResponse {
print(response)
} else{
print("error")
}
guard let data = data else {return}
do {
if let myjson = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? Dictionary<String,Any> {
print(myjson)
}
} catch {
print("Error parsing ")
}
}
task.resume()
The response
["region": {
center = {
latitude = "43.67428196976998";
longitude = "-79.39682006835938";
};
}, "businesses": <__NSArrayM 0x60000211cff0>(
{
alias = "pai-northern-thai-kitchen-toronto-5";
categories = (
{
alias = thai;
title = Thai;
}
);
coordinates = {
latitude = "43.647866";
longitude = "-79.38864150000001";
};
"display_phone" = "+1 416-901-4724";
distance = "3010.095870925626";
id = "r_BrIgzYcwo1NAuG9dLbpg";
"image_url" = "https://s3-media3.fl.yelpcdn.com/bphoto/t-g4d_vCAgZH_6pCqjaYWQ/o.jpg";
"is_closed" = 0;
location = {
address1 = "18 Duncan Street";
address2 = "";
address3 = "";
city = Toronto;
country = CA;
"display_address" = (
"18 Duncan Street",
"Toronto, ON M5H 3G8",
Canada
);
state = ON;
"zip_code" = "M5H 3G8";
};
name = "Pai Northern Thai Kitchen";
phone = "+14169014724";
price = "$$";
rating = "4.5";
"review_count" = 2405;
transactions = (
);
url = "https://www.yelp.com/biz/pai-northern-thai-kitchen-toronto-5?adjust_creative=A4ydpSOHv8wBNquTDeh0DQ&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=A4ydpSOHv8wBNquTDeh0DQ";
},
Business is not the root data object in your JSON. You need something like this:
struct Business: Codable {
let name: String
}
struct RootObject: Codable {
let businesses: [Business]
}
let rootObject = try JSONDecoder().decode(RootObject.self, from: data)
print(rootObject.businesses.first?.name)
I have successfully parsed JSON for:
birthday = "04/10/1986";
id = 202038339983;
location = {
city = Jupiter;
country = "United States";
state = FL;
};
My question is when part of the JSON is:
submissions = {
data = (
{
"created_time" = "2018-02-16T05:11:56+0000";
id = "131448394823824_167398094382256";
viewer = "Any random string and/or emojis";
},
{
"created_time" = "2018-02-14T23:36:41+0000";
id = "809809871824_8908987486899";
message = "vday \Ud83d\Udda4\U2665\Ufe0f";
});}
How am I supposed to access created_time, id, viewer, and message?
I have been able to print the whole submissions JSON response to the console with this code :
guard let jsonD = responseFromServer as? [String : Any] else {return}
let subs1 = (jsonD["submissions"] as? [String : Any])
let accessSubs1 = theSubs1
guard let parsedPost = theSubs1 else {
return
}
My console will display:
["data": <__NSArrayI 0x6040001a86c0>(
{
"created_time" = "2018-02-16T05:11:56+0000";
id = "131448394823824_167398094382256";
viewer = "Any random string and/or emojis";
},
{
"created_time" = "2018-02-14T23:36:41+0000";
id = "809809871824_8908987486899";
message = "vday \Ud83d\Udda4\U2665\Ufe0f";
})]
My question is how should I parse the JSON so I can access the created_time inside submissions?
Here is the HTTP Request:
struct XClass: RequestProtocol {
var Path = "/User"
var parameters: [String : Any]? = ["stuff": "id, birthday, location, submissions"]
var aToken = aToken.current
var httpMethod: RequestHTTPMethod = .GET
var apiVersion: APIVersion = .defaultVersion
struct Response: ResponseProtocol {
var id = String()
var birthday = String()
var city = String()
var state = String()
var country = String()
var viewSubs = [String : Any]()
init(XResponse: Any?) {
guard let jsonD = XResponse as? [String : Any] else {return}
id = (jsonD["id"] as? String)!
birthday = (jsonD["birthday"] as? String)!
let XArr = (jsonD["location"] as? [String : String])
city = XArr!["city"]!
country = XArr!["country"]!
state = XArr!["state"]!
let subs1 = (jsonD["submissions"] as? [String : Any])
let accessSubs1 = theSubs1
guard let parsedPost = theSubs1 else {
return
}
viewSubs = theSubs1
}}}
func getXData(){
let connection = RequestConnection()
connection.add(XClass()) { response, result in
switch result {
case .success(let response):
print("Request Succeeded: \(response)\n\n\n")
case .failed(let error):
print("Request Failed: \(error)")
}}
connection.start()
}
Create a struct
struct Data: Decodable {
var created_time : String
var id : String
var viewer : String
}
call to the api url from URLSession
guard let url = URL(string: "your api url")
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error.localizedDescription)
} else {
guard let data = data else {return}
var data: [Data]() = JSONDecoder().decode(Data.self, data)
for dat in data{
print(dat.created_time)
print(dat.id)
print(dat.viewer)
}
}
If you are not using Decodable from Swift 4, or still in Swift 3,
then you can specify that the data in "submissions" is an array of dictionaries (double brackets) then you can iterate that.
Change
let subs1 = (jsonD["submissions"] as? [String : Any])
To
let subs1 = (jsonD["submissions"] as? [[String : Any]])
for sub in subs1 {
let time = sub["created_time "] as? [String : Any]
...
}
Hi I am making an app which works with an API. I have a working code which receives data from the API. But I thought it would be better to make my code a bit cleaner. I want to set the data from the api in an dictionary but I can't get it working. Any help would be appreciated, thanx!
Here is the api result:
I want to set the AutorId and BranchId etc etc in a dictionary.
And this is de code which I have now.
This is the Project class:
class Project: NSObject {
var AuthorId: String?
var BranchId: String?
var CompanyId: String?
var ContactId: String?
var Date: String?
var Deadline: String?
var Description: String?
var Id: String?
var State: String?
init(dictionary: [String: Any]) {
self.AuthorId = dictionary["AuthorId"] as? String
self.BranchId = dictionary["BranchId"] as? String
self.CompanyId = dictionary["CompanyId"] as? String
self.ContactId = dictionary["ContactId"] as? String
self.Date = dictionary["Date"] as? String
self.Deadline = dictionary["Deadline"] as? String
self.Description = dictionary["Description"] as? String
self.Id = dictionary["Id"] as? String
self.State = dictionary["State"] as? String
}
}
and here I am trying to set it in an dictionary:
func apiRequest() {
apiRequestHeader()
var running = false
let urlProjects = NSURL(string: "https://start.jamespro.nl/v4/api/json/projects/?limit=10")
let task = session?.dataTask(with: urlProjects! as URL) {
( data, response, error) in
if let taskHeader = response as? HTTPURLResponse {
print(taskHeader.statusCode)
}
if error != nil {
print("There is an error!!!")
print(error)
} else {
if let content = data {
do {
let dictionary = try JSONSerialization.jsonObject(with: content) as! [String:Any]
print(dictionary)
if let items = dictionary["items"] as? [[String:Any]] {
let project = Project(dictionary: items)
print(project)
self.projects.append(project)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}
catch {
print("Error: Could not get any data")
}
}
}
running = false
}
running = true
task?.resume()
while running {
print("waiting...")
sleep(1)
}
}
I am trying to get data that has been encoded as json in a php script.
My code:
func getJson(completion: #escaping (Array<CarPark>) -> Void) {
activityIndicatior.center = self.view.center
activityIndicatior.hidesWhenStopped = true
activityIndicatior.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
view.addSubview(activityIndicatior)
self.activityIndicatior.startAnimating()
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil
{
print("ERROR")
}
else
{
if let content = data
{
do{
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
for index in 0..<myJson.count {
if let entry = myJson[index] as? NSDictionary{
let name = entry["Name"] as! String
let longitude = Double(entry["Longitude"] as! String)
let latitude = Double(entry["Latitude"] as! String)
let quiet = Int(entry["Quiet"] as! String)
let moderate = Int(entry["Moderate"] as! String)
let busy = Int(entry["Busy"] as! String)
let coordinate = CLLocationCoordinate2D( latitude: latitude!, longitude: longitude!)
print("coordinate lat is : \(coordinate.latitude)")
print("coordinate long is : \(coordinate.longitude)")
print("coordinate full is: \(coordinate)")
let tempPark = CarPark(name: name, latitude: latitude!, longitude: longitude!, quiet: quiet!, moderate: moderate!, busy: busy!, coordinate: coordinate, level: "Nil")
let level = tempPark.calcAvailability()
tempPark.level = level
print("Availability is \(tempPark.level)")
self.tempCarParks.append(tempPark)
// print("amount of parks: \(self.carParks.count)")
print("name of parks in array: \(self.tempCarParks[index].name)")
print("Availability is \(tempPark.level)")
}
}
completion(self.tempCarParks)
}
catch
{
print("Error")
}
}
}
}
task.resume()
}
I am getting an error that says 'Ambiguous use of subscript' at the line:
if let entry = myJson[index] as? NSDictionary{
How can I fix this?
Since you know myJson is an array why do you cast the object to unspecified AnyObject?
That causes the error, the compiler needs to know the concrete types of all subscripted objects. Help the compiler then the compiler helps you:
let myJson = try JSONSerialization.jsonObject(with: content) as! [[String:Any]]
for entry in myJson { // don't use old-fashioned index based loops
let name = entry["Name"] as! String
...
.mutableContainers is completely useless in Swift.
I am trying to parse some weather data, and I have ran into an issue. I created a default value in an array that gives the user a dummy location and value in a table view. This value is unwrapped using the same code below that I am trying to use for real data. It works fine for the dummy value and can find the locationStore easily, but when I am loading from the view that has the parsed data, the locationStore is showing as nil in the debugger. I am not sure how to solve this, so any help would be greatly appreciated.
class LocationWeatherViewController: UITableViewController{
var locationStore: LocationStore!
var imageStore: ImageStore!
var newLocation: Location!
var locationCreated : NSMutableArray = NSMutableArray( array: ["Test ", 0, 0.0, 0.0 , 0.0])
override func viewDidLoad() {
super.viewDidLoad()
print(locationCreated.count)
if let locationName = locationCreated[0] as? String {
print(locationName)
if let currentTemp = locationCreated[2] as? Double{
print(currentTemp)
if let zip32 = (locationCreated[1] as? Int){
let zip = Int64(zip32)
print(zip)
if let xCrd = locationCreated[3] as? Double{
print(xCrd)
if let yCrd = locationCreated[4] as? Double{
print(yCrd)
newLocation = locationStore.createLocation(false, location: locationName, currentTemp: currentTemp, zipCode: zip, xCord: xCrd, yCord: yCrd)
if let index = locationStore.allLocations.indexOf(newLocation){
let indexPath = NSIndexPath(forRow: index, inSection: 0)
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
}
}
}
}
}
}
func createLocation(random: Bool, location: String, currentTemp: Double, zipCode: Int64, xCord: Double, yCord: Double ) -> Location{
if !random{
let newLocation = Location(random: false, locationNme: location, currentTmperature: currentTemp, locationZpCode: zipCode, xCrd: xCord, yCrd: yCord)
allLocations.append(newLocation)
return newLocation
}
LocationStore:
let newLocation = Location(random: true, locationNme: location, currentTmperature: currentTemp, locationZpCode: zipCode, xCrd: xCord, yCrd: yCord)
allLocations.append(newLocation)
return newLocation
}
Location:
init( locationName: String, currentTemperature: Double, locationZipCode: Int64, xCord: Double, yCord: Double){
self.locationName = locationName
self.currentTemperature = currentTemperature
self.locationZipCode = Int64(locationZipCode)
self.xCord = xCord
self.yCord = yCord
self.itemKey = NSUUID().UUIDString
super.init()
}
convenience init( random:Bool = false, locationNme: String, currentTmperature: Double, locationZpCode: Int64, xCrd: Double, yCrd: Double ){
if random {
let locations = ["Louisville", "Gainesville", "Austin", "San Francisco"]
//38.2527, 85.7585
//29.6516, 82.3248
// 30.2672, -97.7431
// 37.7749, -122.4194
let xCords = [38.2527, 29.6516, 30.2672, 37.7749 ]
let yCords = [-85.7585, -82.3248, -97.7431, -122.4194 ]
let zips = [40217, 32608, 77878, 46454]
let temps = [76.0, 101.3, 95.4, 68.5]
var idx = arc4random_uniform(UInt32(locations.count))
let randomLocation = locations[Int(idx)]
let randomLocationxCord = xCords[Int(idx)]
let randomLocationyCord = yCords[Int(idx)]
let randomZip = zips[Int(idx)]
idx = arc4random_uniform(UInt32(temps.count))
let randomTemp = temps[Int(idx)]
self.init ( locationName: randomLocation, currentTemperature: randomTemp, locationZipCode: Int64(randomZip), xCord: randomLocationxCord, yCord: randomLocationyCord)
}
else{
self.init( locationName: locationNme, currentTemperature: currentTmperature, locationZipCode: locationZpCode, xCord: xCrd, yCord: yCrd)
}
}
APIFinder:
func useAPI(zipCode: Int) -> NSArray{
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let locationURL = NSURL( string: "http://api.openweathermap.org/data/2.5/forecast/city?zip=\(zipCode),us&APPID=74b78f4effe729b2a841cb35e3862d85")
let request = NSURLRequest(URL: locationURL!)
let task = session.dataTaskWithRequest(request) {
(data,response, error) -> Void in
if let locationData = data {
if let jsonString = NSString( data:locationData,encoding: NSUTF8StringEncoding){
let data = jsonString.dataUsingEncoding(NSUTF8StringEncoding)
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as! [String: AnyObject]
if let city = json["city"]{
if let name = city["name"]{
self.locationArray[0] = name!
self.locationArray[1] = zipCode
}
if let coord = city["coord"]{
if let xCord = coord!["lat"]{
self.locationArray[3] = xCord!
}
if let yCord = coord!["lon"]{
self.locationArray[4] = yCord!
}
}
}
if let list = json["list"]{
if let main = list[0]["main"]{
if let temp = main!["temp"]{
self.locationArray[2] = temp!
}
}
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
}
else if let requestError = error {
print("Error fetching weather data: \(requestError)")
}
else {
print("Unexpected error with request")
}
}
task.resume()
return locationArray
}
Output:
5
Test
0.0
0
0.0
0.0
5
Hartford
295.54
42328
37.45116
-86.909157
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
Solved:
The problem was that I needed to pass the LocationStore between the views in the segue. Such a dumb problem once the answer was shown.
Thanks for all of the help and suggestions.