Swift: Extract Section from Wiki article API - json

I want to extract the Event section from a wikipedia article: https://en.wikipedia.org/wiki/January_4
But I Can't figure out how to do it. I am using SwiftyJSON and Alamofire.
I have managed to use extracts to get the first paragraph of the article with this code:
func requestWikiInfo(pickedDate : String){
let parameters : [String:String] = [
"format" : "json",
"action" : "query",
"prop" : "extracts",
"exintro" : "",
"explaintext" : "",
"titles" : pickedDate,
"indexpageids" : "",
"redirects" : "1"
]
Alamofire.request(wikipediaURL, method: .get, parameters: parameters).responseJSON { (response) in
if response.result.isSuccess{
print("We got the wikipedia info")
//print(response)
let dateJSON : JSON = JSON(response.result.value!)
print(JSON(response.result.value))
let pageid = dateJSON["query"]["pageids"][0].stringValue
let dateDescription = dateJSON["query"]["pages"][pageid]["extract"].stringValue
self.dayDescriptionText.text = dateDescription
}
}
}
Thank you.

Use this: https://en.wikipedia.org/w/api.php?format=json&action=parse&page=January_4&prop=text&section=1
Removing &section=1 will return all the wikitext rather than just the events section (is this always the first section of the pages?). See the parse docs if you want to tweak it further.

Related

How do I get data from a json file after it has been parsed? (Swift 5)

I have a data.json file containing this,
[
{
"question": "TEST QUESTIONS",
"answer" : ["Answer1", "Answer2"],
"correctAnswer": "Answer "
},
{
"question" : "TEST QUESTION 2",
"answer" : ["Answer1", "Answer2"],
"correctAnswer" : "Answer 2"
}
]
And i parse the data with this function
func Parser() -> [Questions] {
let url = Bundle.main.url(forResource: "data", withExtension: "json")!
let data = try! Data(contentsOf: url)
let decoder = JSONDecoder()
let questions = try? decoder.decode([Questions].self, from: data)
return questions!
}
When i call Parser() how do I get the information such as question or answer, I thought to use let question = Parser().question but that doesn't work.
Any help would be appreciated
First, I assume you have a class or struct created elsewhere in your code that contains vars for each of your JSON dict keys (i.e. “question”, “answer”, “correcctanswer”) and that that struct is called Questions?
If so, then you can call your Parser function as follows:
Let newDataFromJson: [Questions] = Parser()
From there, if you want to access the questions instance, you can do that with:
newDataFromJson.question
Parser()
already returning an Array to You
you can get it by
let questions = Parser()
for question in questions {
print("Question: \(question.question), Correct answer: \(question.answer)")
}

Return a value in a nested JSON array, using Alamofire and Swift

Super new to swift, JSON, and pretty all coding so I apologize in advance if this question is redundant to others on the site or I am missing something simple here.
I am looking to return the value of "text" ("1.7 mi") associated with "distance" in the "elements" array in the JSON code below:
{
"destination_addresses" : [ "30 Rockefeller Plaza, New York, NY 10112, USA" ],
"origin_addresses" : [ "352 7th Ave, New York, NY 10001, USA" ],
"rows" : [
{
"elements" : [
{
"distance" : {
"text" : "1.7 mi",
"value" : 2729
},
"duration" : {
"text" : "15 mins",
"value" : 887
},
"status" : "OK"
}
]
}
],
"status" : "OK"
}
I retrieved the JSON data using Alamofire and the Google DistanceMatrix (see my code below), but am having trouble parsing the data to isolate what I need. I know the code below isn't close to what I need, but am unsure as to how to proceed.
func distanceMatrix(startLocation: String, endLocation: String) {
let myOrigin = startLocationTFText
let myDestination = destinationLocationTFText
let url = "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=\(myOrigin)&destinations=\(myDestination)&key=API_Key
let encodedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
AF.request(encodedUrl!).responseJSON { response in
print(response.request as Any)
print(response.response as Any)
print(response.data as Any)
print(response.result as Any)
let json = JSON(response.data as Any)
Any help is much appreciated. Thank you.
You can use Decodable to get the desired result.
struct RootResponse: Decodable {
let destinationAddresses, originAddresses: [String]
let rows: [Rows]
let status: String
}
struct Rows: Decodable {
let elements: [Elements]
}
struct Elements: Decodable {
let distance, duration: Details
let status: String
}
struct Details: Decodable {
let text, value: String
}
This will be your model file and once you have added it then you can go back to your function and use it as:
func distanceMatrix(startLocation: String, endLocation: String) {
let myOrigin = startLocationTFText
let myDestination = destinationLocationTFText
let url = "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=\(myOrigin)&destinations=\(myDestination)&key=API_Key"
let encodedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
AF.request(encodedUrl!).responseJSON { response in
print(response.request as Any)
print(response.response as Any)
print(response.data as Any)
print(response.result as Any)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
guard let json = try? decoder.decode(RootResponse.self, from: response.data) else { print("Unable to parse JSON"); return }
print(json)
print(json.rows.first?.elements.first?.distance.value) // This is how you can get the value, but it will be better to safely unwrap them and I have also used first? to get the first object but this is an array and you can always use a for loop for further purpose
}

Match row selected to pass data

I've taken a look at all these Swift, and asp.net, and javascript questions.
1
2
3
4
5
Goal:
When I select a messages from the list of chat messages in the MessageListController I want the opened session in the next ChatDetailController to be the conversation that was selected.
I'm doing the same thing in this iOS image for my WatchKit app. The message with Sophia is selected and the chat with Sophia opens.
[![enter image description here][6]][6]
I want to pass the json "message_id" i.e. the chatMessageId property. I'm already passing the chatMessageId from the MessageModelto the ChatDetailController as you can see in code.
Is it the chatMessageId of the ChatModelI need to pass? Or am I already passing the data that I need?
Passed context: Optional(HTWatch_Extension.MessageModel(partner: "9859", nickname: "Marco", message: "Have you seen is dog?", city: "Madrid", countryBadgeImageURL: https://i.imgur.com/PJcyle7.jpg, messageListImageURL: https://i.imgur.com/PJcyle7.jpg, chatMessageId: "Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ", status: "offline"))
Attempt:
Do I need to take the do-catch block where I parse the ChatModel out of the ChatDetailController's awakeWithContext method and put it in the didSelectRowAt method of the MessageListController?
MessageListController
// ...code...
var messageObject = [MessageModel]()
var chatObject = [ChatModel]()
// ...code...
override func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) {
var messageContext = messageObject[rowIndex]
var chatContext = chatObject[rowIndex]
do {
guard let fileUrl = Bundle.main.url(forResource: "Chats", withExtension: "json") else {
print("File could not be located")
return
}
let data = try Data(contentsOf: fileUrl)
let decoder = JSONDecoder()
let msg = try decoder.decode([ChatModel].self, from: data)
self.chatObject = msg
} catch let error {
print(error)
}
messageContext.chatMessageId = (chatObject as AnyObject).filter { (dictionaryTemp:[String:String]) -> Bool in
return dictionaryTemp["message_id"] == chatContext.chatMessageId
}
// WatchKit's model presentation method.
presentController(withName: "ChatDetailController", context: messageContext)
}
If I have understood correctly, your Chat.json, will have chat's for all message id's. Select one of the message id row and load the respective chat history.
In that case you can parse based on message by using filter. Let's say you have it in a dictionary like this.
Example:
let responseString = "{\"name\":\"Tom\"}"
if let responseData = responseString.data(using: .utf8){
do {
let object = try JSONSerialization.jsonObject(with:responseData , options: .allowFragments)
print("Response Object=\(object)")
} catch{
print("parsing Error=\(error)")
}
}
You can use a similar code to create your Object. The final object should be something like chatDictionary
let chatDictionary = [
[
"fromId": "zz1234skjksmsjdfwe2zz",
"toId": "qq43922sdkfjsfmmxdfqq",
"messageText": "Have you seen is dog?",
"imageUrl": "https://i.imgur.com/PJcyle7.jpg",
"message_id": "Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ",
"read": "true"
],
[
"fromId": "zz1234skjksmsjdfwe2zz",
"toId": "qq43922sdkfjsfmmxdfqq",
"messageText": "Yes I have. It's cute.",
"imageUrl": "https://i.imgur.com/PJcyle7.jpg",
"message_id": "Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ",
"read": "true"
],
[
"fromId": "zz1234skjksmsjdfwe2zz",
"toId": "qq43922sdkfjsfmmxdfqq",
"messageText": "I want to get a pet too.",
"imageUrl": "https://i.imgur.com/PJcyle7.jpg",
"message_id": "Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ1",
"read": "true"
]
]
Your did Select Row At index
override func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) {
let message = messageObjects[rowIndex]
// Create a chat object Dictionary, parse it before you pass it to the detail View Controller , if you have the chat.json.
// I have used 'Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ', but here you can your message id property to make it dynamic
message.chatObjects= chatDictionary.filter { (dictionaryTemp:[String : String]) -> Bool in
return dictionaryTemp["message_id"] == "Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ"
}
presentController(withName: "ChatDetailController", context: message)
}

Swift : The data couldn’t be read because it isn’t in the correct format

I try to call the POST Api with Alamofire but it's showing me an error of incorrect format.
This is my JSON response:
[
{
"_source": {
"nome": "LOTERIAS BELEM",
"endereco": "R DO COMERCIO, 279",
"uf": "AL",
"cidade": "BELEM",
"bairro": "CENTRO"
},
"_id": "010177175"
},
{
"_source": {
"nome": "Bel Loterias"
},
"_id": "80224903"
},
{
"_source": {
"nome": "BELLEZA LOTERIAS",
"endereco": "R RIVADAVIA CORREA, 498",
"uf": "RS",
"cidade": "SANTANA DO LIVRAMENTO",
"bairro": "CENTRO"
},
"_id": "180124986"
}
]
class Album: Codable {
var _source : [_source]
}
class _source: Codable {
var nome : String
var endereco : String
var uf : String
var cidade : String
var bairro : String
}
var arrList = [Album]()
And this is how i try to Decoding with Alamofire.
func request() {
let urlString = URL(string: "My Url")
// Alamofire.request(url!).responseJSON {(response) in
Alamofire.request(urlString!, method: .post, parameters: ["name": "belem"],encoding: JSONEncoding.default, headers: nil).responseJSON {
(response) in
switch (response.result) {
case .success:
if let data = response.data {
do {
let response = try JSONDecoder().decode([Album].self, from: data)
DispatchQueue.main.async {
self.arrList = response
}
}
catch {
print(error.localizedDescription)
}
}
case .failure( let error):
print(error)
}
}
}
Just your Album model is incorrect.
struct Album: Codable {
var source : Source
var id : String
enum CodingKeys: String, CodingKey {
case source = "_source"
case id = "_id"
}
}
struct Source: Codable {
var nome : String
var endereco : String?
var uf : String?
var cidade : String?
var bairro : String?
}
If you don't want _id altogether then simply remove the related parts.
As for your Alamofire related code, that part is good.
Notable improvements:
Have avoided underscored variable name in model by customizing CodingKeys for key mapping purpose
Typenames should always start with a Capital letter (so _source is Source)
Similarly, variable names should always start with a lowercase letter
Made some variables optional (based on your updated response)
Keeping a variable non-optional means it must be present in the response for the model to be created
Making a variable optional means that key may or may not be present in the response and it not being there won't prevent the model from being created
I would like to recommend you to use json4swift.com. You just have to copy your json and paste there. It will automatically create modal struct or class from your json.
Coming back to your question, Your class Album doesn't have array of [_source]. That's the reason you are getting following error "The data couldn’t be read because it isn’t in the correct format".
Try below given format of album class,
class Album: Codable
{
var source: Source?
var id: String?
}
Please try to avoid using underscore in Swift.

simple way to map a json collection response into swift object class

Ive tried a lot of libraries, Alamofire, JsonHelper, ObjectMapper etc..., but unfortunately, Ive coundn't map a json collection response into an object class.
Im developing an IOS 8 App with swift 1.2 and xcode 6.3, and two classes of my model are:
Club.swift
class Club {
var id: String = ""
var name: String = ""
var imageUrl: String = ""
var hasVip: Bool = false
var desc: String = ""
var location: [Location] = []
}
Location.swift
class Location {
var country: String = ""
var city: String = ""
var address: String = ""
var zip: String = ""
var underground: [String] = []
}
I have another class to request to my API:
apliClient.swift
class ApiClient {
var clubs = [Club]?()
func getList(completionHandler: ([JSON]) -> ()) {
let URL = NSURL(string: "https://api.com/v1/clubs")
let mutableURLRequest = NSMutableURLRequest(URL: URL!)
mutableURLRequest.setValue("Content-Type", forHTTPHeaderField: "application/json")
mutableURLRequest.HTTPMethod = "GET"
mutableURLRequest.setValue("Bearer R01.iNsG3xjv/r1LDkhkGOANPv53xqUFDkPM0en5LIDxx875fBjdUZLn1jtUlKVJqVjsNwDe1Oqu2WuzjpaYbiWWhw==", forHTTPHeaderField: "Authorization")
let manager = Alamofire.Manager.sharedInstance
let request = manager.request(mutableURLRequest)
request.responseJSON { (request, response, json , error) in
if (json != nil){
var jsonObj = JSON(json!)
if let data = jsonObj["hits"].arrayValue as [JSON]?{
completionHandler(data)
}
}
}
}
}
and I think, there is a simple way to mapping objects in swift. I would like to know, how I can return the completionHandler(data) converted into a [Club] object?
let data = jsonObj["hits"].arrayValue as [JSON]? is
[{
"_id" : "5470def9e0c0be27780121d7",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/5470def9e0c0be27780121d7_180.png",
"name" : "Mondo",
"hasVip" : false,
"location" : {
"city" : "Madrid"
}
}, {
"_id" : "540b2ff281b30f3504a1c72f",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/540b2ff281b30f3504a1c72f_180.png",
"name" : "Teatro Kapital",
"hasVippler" : false,
"location" : {
"address" : "Atocha, 125",
"city" : "Madrid"
}
}, {
"_id" : "540cd44581b30f3504a1c73b",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/540cd44581b30f3504a1c73b_180.png",
"name" : "Charada",
"hasVippler" : false,
"location" : {
"address" : "La Bola, 13",
"city" : "Madrid"
}
}]
You can do this with the ObjectMapper library that you mentioned above. Simply create the Club class and make sure it implements the Mappable protocol. Then you can use ObjectMapper as follows to map the data:
let clubs = Mapper<Club>().mapArray(JSONString)
Full disclosure: I am the author of ObjectMapper.
With Swift 2.0, it is now possible, simple copy your json to http://www.json4swift.com and the swift models with entire key-value mapping will be auto generated, all you need to do is instantiate the models by passing either array or dictionary out of your Json.
For objective-c try JSONModel it can be what you are looking for...
and here you can find some more example of using it
note this about swift (from JSONModel's GitHub page):
Swift works in a different way under the hood than Objective-C. Therefore I can't find a way to re-create JSONModel in Swift. JSONModel in Objective-C works in Swift apps through CocoaPods or as an imported Objective-C library.
Update:
Check ups this blog post from apple about Working with JSON in Swift
https://developer.apple.com/swift/blog/?id=37