I am using Alamofire to call the Riot API and I want to display the information that it has called. I have the get request working, I just don't know how to link to a label in the application. I have included screenshots of the code!
Code
Response
It is just a simple app I am creating!
func callAlamo(url: String){
Alamofire.request(url).responseJSON(completionHandler: {
response in
self.pasrseData(JSONData: response.data!)
})
}
func parseData(JSONData: Data){
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as? JSONStandard
print(readableJSON)
}
catch {
print(error)
}
}
No need to serialize since responseJSONfrom Alamofire has done it. Since I don't know what is inside of your JSON object, let's say that you get a return of age and name:
struct InfoModel { // this struct will decompose our JSON object from the response that we get
var age:Int
var name:String
init(json:Dictionary<String,Any>?) {
guard let dict = json,
let age = dict["age"] as? Int,
let name = dict["name"] as? String
else {fatalError() }
self.age = age
self.name = name
}
}
func parse(url: String, completion:#escaping (InfoModel)-> Void) {
Alamofire.request(url).responseJSON {response in
// get the JSON dictionary
if let JSON = response.result.value {
// no need to decompose your object since your struct does it inside of its initializer
completion(InfoModel(json: JSON as? Dictionary<String, Any>))
}
}
}
// call this function anywhere
parse(url: "") { (m:InfoModel) in
print("age= \(m.age), name= \(m.name)")
// now populate your label
labelOne.text = "\(m.age)"
labelTwo.text = name
}
You set the text property of the label in the completion block, basically:
func callAlamo(url: String){
Alamofire.request(url).responseJSON(completionHandler: {
response in
// here we say get me a non optional data (otherwise don't do the if)
if let data = response.data {
// here we are saying if you can't get me a value (i.e. not nil) for:
// json (note the try? will give nil if there is an error)
// name, we get the name out of the json dictionary
// then go to the else block, where we exit the function
// Happy case where we values for json and name we now have non optional variables W00t
guard
let json = try? self.parseData(JSONData: data),
let name = json["name"] as? String
else {
print("name does not exist in json: \(json)")
return
}
// Time to set the label
self.name.text = name
}
})
}
// Changed this to return JSON as a dictionary (it looks like what you printed was a dictionary)
// I also changed this so it throws the error and doesn't deal with it.
// It probably doesn't know what to do if it can't read json something
// else should handle the error higher up the stack
func parseData(JSONData: Data) throws -> [String: Any]? {
return try JSONSerialization.jsonObject(with:
JSONData, options: .mutableContainers) as? [String: Any]
}
NB: This is untested if your having problems and I'll go for a tested solution.
Edit: Answering how to get another property.
The way we got "name" was this chunk of code:
guard
let json = try? self.parseData(JSONData: data),
let name = json["name"] as? String
else {
print("name does not exist in json: \(json)")
return
}
To get another property out we could do this:
guard
let json = try? self.parseData(JSONData: data),
let name = json["name"] as? String,
let summonerLevel = json["summonerLevel"] as? Int
else {
print("name does not exist in json: \(json)")
return
}
Then to display summonerLevel we do the same as with name (although we have an int not a String)
// Time to set the label
self.name.text = name
// (you will have to create this new label)
self.summonerLevelLabel.text = "Level: \(summonerLevel)"
Related
I am trying to convert stored coredata values to JSON format and the JSON format value need to assign a single variable, because this generated JSON I need to send to server. Below code I tried to get coredata stored values but don’t know how to generate JSON required format.
Getting values from coredata
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "User")
do {
let results = try context.fetch(fetchRequest)
let dateCreated = results as! [Userscore]
for _datecreated in dateCreated {
print("\(_datecreated.id!)-\(_datecreated.name!)") // Output: 79-b \n 80-c \n 78-a
}
} catch let err as NSError {
print(err.debugDescription)
}
Need to Convert Coredata Value to Below JSON format
{
"status": true,
"data": [
{
"id": "20",
"name": "a"
},
{
"id": "21",
"name": "b"
},
{
"id": "22",
"name": "c"
}
]
}
Probably the easiest is to convert your object(s) to either dictionaries or arrays (depending on what you need).
First you need to be able to convert your Userscore to dictionary. I will use extension on it since I have no idea what your entity looks like:
extension Userscore {
func toDictionary() -> [String: Any]? {
guard let id = id else { return nil }
guard let name = name else { return nil }
return [
"id": id,
"name": name
]
}
}
Now this method can be used to generate an array of your dictionaries simply using let arrayOfUserscores: [[String: Any]] = userscores.compactMap { $0.toDictionary() }.
Or to build up your whole JSON as posted in question:
func generateUserscoreJSON(userscores: [Userscore]) -> Data? {
var payload: [String: Any] = [String: Any]()
payload["status"] = true
payload["data"] = userscores.compactMap { $0.toDictionary() }
return try? JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted)
}
This will now create raw data ready to be sent to server for instance
var request = URLRequest(url: myURL)
request.httpBody = generateUserscoreJSON(userscores: userscores)
You can use the properties of an Encodable to make this happen. This has the added benefit of not resorting to the Any type.
For the JSON, you could use the following types:
struct JSONMessage: Encodable {
var status: Bool
var data: [JSONDataEntry]
}
struct JSONDataEntry: Encodable {
var id: String
var name: String
}
Then you can adjust your do/try/catch as follows:
do {
let results = try context.fetch(fetchRequest)
let dateCreated = results as! [Userscore]
// *starting here*
let data = dateCreated.map { JSONDataEntry(id: String($0.id!), name: $0.name!) }
let status = true // <- not sure where status comes from, so adding here
let message = JSONMessage(status: status, data: data)
let jsonData = try JSONEncoder().encode(message)
if let json = String(data: jsonData, encoding: .utf8) {
// do something with the JSON string
print(json)
}
// *ending here*
} catch let err as NSError {
print(err.debugDescription)
}
I am trying to parse some nested JSON retrieved through an API but am having trouble isolating specific key-value pairs. In fact, I have some confusion over the difference between the JSON data and the dictionary obtained through serialization.
To retrieve the data I am using:
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
return
}
To convert the data to a JSON dictionary, I am doing
do {
let stringDic = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
} catch let error {
print(error)
}
When printed, this produces nested output of the form:
Optional(["document_tone": {
"tone_categories" = (
{
"category_id" = "emotion_tone";
"category_name" = "Emotion Tone";
and so forth
My question is how can I get a unique value such as that for the key category_name?
If I try to use
let myCat = stringDic["category_name"]
Fix-it requires let document_tone = stringDic?["document_tone"] which if printed to console just prints whole dictionary over again.
Thanks in advance for any suggestions.
It's pretty easy: () is array, {} is dictionary and the compiler must know the static types of all subscripted objects:
if let documentTone = stringDic?["document_tone"] as? [String:Any],
let toneCategories = documentTone["tone_categories"] as? [[String:Any]] {
for category in toneCategories {
print(category["category_name"])
}
}
I think it's better to use Decodable
struct Root:Decodable {
let documentTone : InnerItem
}
struct InnerItem:Decodable {
let toneCategories: [BottomItem]
}
struct BottomItem:Decodable {
let categoryId: String
let categoryName: String
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Root.self, from: data)
//print all names
result.documentTone.toneCategories.forEach {print($0.categoryName) }
} catch {
print(error)
}
I'm having a name assigned to my name textfield like so..
for asd in orderDetails {
if let jsonStr = asd.value(forKey: "customerJson") as? String {
let data = sdf?.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
if let json = try JSONSerialization.jsonObject(with: data!) as? [String: Any] {
for item in json {
if item.key == "first_Name" {
cell.nameLabel.text = item.value as? String //ASSIGNED HERE
}
}
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
}
Now I want to search on the search bar based on this name. While searching in other views where the core data attributes were mentioned directly I did something like so which worked fine..
filtered = self.newProdDetails.filter({( data : NewProduct) -> Bool in
return (data.name?.lowercased().contains(searchText.lowercased()))! //Here the entity NewProduct has an attribute name
})
But in the current scenario, the attribute is a called customer_json which is a json string like so..
customer_json={
"mobile_number”:”9876543210”,
"first_name”:”testName”,
"last_name”:”testLastName”,
"seller_id":"93"
}
How can I mention first_name in the search parameter
It's bad idea to keep working with Data object or even JSON string directly as your Model. I recommend you to create few Structs, make the conform to Codeable protocol, so I will make the easy to work with and easy to convert into JSON whoever you want. I'm strongly advice you to consider it.
JSONSerialization.jsonObject(with:) converting your data into Dictionary, so instead of enumerating it you can access value you want by key:
Like this:
if let json = try JSONSerialization.jsonObject(with: data!) as? [String: Any],
let name = json["first_Name"] {
cell.nameLabel.text = name
}
And definitely you can access you data same way:
orderDetails.filter{ asd in
if
let jsonStr = asd.value(forKey: "customerJson") as? String,
let data = sdf?.data(using: String.Encoding.utf8, allowLossyConversion: false)
let json = try JSONSerialization.jsonObject(with: data!) as? [String: Any],
let name = json["first_Name"],
name == cell.nameLabel.text {
return true
} else {
return false
}
}
Okay, so, swift noob alert:
How do I do the simplest of iterations given the following array (I dont know what to call this shape: array, dictionary, object...)?
func showNotification(_ sender: [AnyHashable: Any]) { ... }
sender["actions"]:
Optional([{"text":"Confirm","type":"response"},{"text":"Decline","type":"response"},{"link":"https://www.stackoverflow.com","text":"Website","type":"info"}])
attempts:
if let result = sender["actions"] {
print("YES \(result)")
for action in result as! [String] {
print(action)
}
}
the above prints:
YES [{"text":"Confirm","type":"response"},{"text":"Decline","type":"response"},{"link":"https:\/\/www.stackoverflow.com","text":"Website","type":"info"}]
...however, returns the following error:
Could not cast value of type '__NSCFString' (0x1a7c28d50) to 'NSArray' (0x1a7c297c8)
The end goal here is to simply get to each one of the actions individually, ie:
{"text":"Confirm","type":"response"}
{"text":"Decline","type":"response"
ect...
Does Swift have a map function... FYI Im coming in from a Java and JavaScript world... swiftyjson seems a bit heavy for one loop.
Thanks, and as always any help and direction is appreciated!
edit:
This is the print via the param passed to the function sender:
sender: [AnyHashable("title"): title!, AnyHashable("message"): message, AnyHashable("message_id"): 0:1503511875428318%03300c3203300c32, AnyHashable("id"): 1497708240713, AnyHashable("actions"): [{"text":"Confirm","type":"response"},{"text":"Decline","type":"response"},{"link":"https:\/\/www.notifyd.com","text":"Website","type":"info"}], AnyHashable("aps"): {
"content-available" = 1;
}]
You want to decode the JSON String and then cast to an array of Dictionary:
if
// cast sender["actions"] to String
let actionsString = sender["actions"] as? String,
// decode data of string and then cast to Array<Dictionary<String, String>>
let actionsStringData = actionsString.data(using: .utf8),
let result = try JSONSerialization.jsonObject(with: actionsStringData, options: []) as? [[String : String]]
{
print("YES \(result)")
for action in result {
print(action)
}
}
Is it really true that what you've got here is an undecoded JSON string? In that case, Swift 4 makes this really easy:
struct S : Decodable {
let link : String?
let text : String
let type : String
}
if let acts = sender["actions"] as? String {
let data = acts.data(using: .utf8)!
if let arr = try? JSONDecoder().decode(Array<S>.self, from: data) {
arr.forEach {print($0)}
}
}
/*
S(link: nil, text: "Confirm", type: "response")
S(link: nil, text: "Decline", type: "response")
S(link: Optional("https://www.stackoverflow.com"), text: "Website", type: "info")
*/
There is something fishy going on here with the data. Let's treat the data with more care. Here is a method to get JSON from a JSON object, a JSON array, a Data object or a string.
enum JsonError: Error { case notJson; case notJsonArray }
func json(from any: Any?) throws -> Any {
if let json = any as? [String: Any] { return json }
if let json = any as? [Any] { return json }
if let data = any as? Data {
return try JSONSerialization.jsonObject(with: data)
}
if let string = any as? String, let data = string.data(using: .utf8) {
return try JSONSerialization.jsonObject(with: data)
}
throw JsonError.notJson
}
Now that I'm being more careful with the JSON object, I should get the what I want or know more about the error.
func showNotification(_ sender: [AnyHashable: Any]) {
do {
guard let result = try json(from: sender["actions"]) as? [Any] else {
throw JsonError.notJsonArray
}
print("YES \(result)")
for action in result {
print("Action: \(action)")
}
} catch {
// Do Something
}
}
I am using Alamofire to parse a JSON API, however, I can't figure out how to parse the response data from Alamofire.
When I try to loop through the fetched data, XCode gives me "Segmentation Fault: 11" error.
Here is my current code:
var tableData:NSArray // I have tried several variable types, NSDictionary, String etc.
--
override func viewDidLoad() {
super.viewDidLoad()
self.getJsonData()
}
func getJsonData() {
Alamofire.request(.GET, "https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn", parameters: [:])
.responseJSON { response in
if let JSON = response.result.value {
// print("JSON: \(response.result)")
for entry in JSON["entries"] {
print("\(entry)") // this is where everything crashes
}
}
self.doTableRefresh()
}
}
func doTableRefresh() {
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
}
What is the correct data format for this JSON result: https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn ? And how do I take the data and populate the tableview?
Convert the response to NSDictionary and NSArray:
func getJsonData() {
Alamofire.request(.GET, "https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn", parameters: [:])
.responseJSON { response in
if let JSON = response.result.value as? NSDictionary{
if let entries = JSON["entries"] as? NSArray{
for entry in entries {
if let entry = entry as? NSDictionary {
for (key, value) in entry {
print("\(key) - \(value)")
}
}
}
}
}
}
}
Without using SwiftyJSON, the idea is to know the right type for each object and successfully downcast.
response.result.value is a dictionary: [String:AnyObject], and the content of json["entries"] is an array of dictionaries: [[String:AnyObject]]. Etc.
Example:
func getJsonData() {
Alamofire.request(.GET, "https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn", parameters: [:])
.responseJSON { response in
if let json = response.result.value as? [String:AnyObject] {
if let entries = json["entries"] as? [[String:AnyObject]] {
for entry in entries {
print(entry) // each entry is a dictionary of type [String:AnyObject]
}
// example of accessing an entry:
if let firstEntry = entries.first, value = firstEntry["adrlinje1"] as? String {
print(value) // "Christian IV gate 3"
}
}
}
}
}
You have to specify the type of your expected value (With SwiftyJSON):
for entry in JSON["entries"] { // Here
print(entry.stringValue)
}