Get objects with request and mapping progress in RestKit - ios

Let's say I have an api like the follwing:
//http://example.com/api/comment/
response:
[
{
"id": 1,
"username": "some_user",
"content": "content of the comment"
},
...
{
"id": 9999,
"username": "some_other_user",
"content": "content of the comment"
}
]
It is critical that I fetch all the objects in one request.
Since RestKit appears to be slow when mapping responses with many objects into core data the following takes very very long (more than a minute in the simulator).
RKObjectManager.sharedManager().getObjectsAtPath("/api/comment/", parameters: nil, success: { (operation, result) -> Void in
}) { (operation, error) -> Void in
}
Since I couln't find a way to speed up the request/mapping I was hoping to be able to display a progress bar for the duration of this operation. Is it possible to get a progress block for the mapping with someting like objectsToMap and mappedObjects?

Related

Trying to Parse this JSON in IOS - seems v difficult is it Valid? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
Here is the JSON:
https://skrapapis.skrap.xyz/newApi/public/scrapapi/getServices
this is my code:
if let url = URL(string: SERVICE_URL) {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
if let jsonString = String(data: data, encoding: .utf8) {
if let data = jsonString.data(using: String.Encoding.utf8) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any]
print(json!["result"]!)
} catch {
print("Something went wrong")
}
}
}
}
}.resume()
}
Seems to be valid, but then parsing the 'result' part is not valid?
Decode the JSON using Decodable protocol conformance instead of JSONSeraialization which is outdated. Here is the code:
let json = """
{
"code": 0,
"description": "Services found successfuly",
"result": [
{
"service_id": 2,
"service_name": "Skip Hire",
"description": "<p>Ideal for disposing of large amounts of building, construction and garden waste. Skips come in varied sizes and are measured in yards.</p>",
"img_url": "/service_images/skip_hire.png"
},
{
"service_id": 1,
"service_name": "Grab Hire",
"description": "<p>Ideal for disposing of large amounts of building, construction and garden waste. A grab lorry uses a grab arm and bucket to collect and clear away large quantities of waste from otherwise inaccessible areas</p>",
"img_url": "/service_images/grab_hire.png"
},
{
"service_id": 3,
"service_name": "Cage Hire",
"description": "<p>An alternative removal solution to skip hire, suitable for all types of waste removals and includes a professional and experienced driver who will assist you in loading your waste. Ideal for removal of bulky items or for locations where a skip isn't practical, such as more congested roads or busy urban areas with parking restrictions.</p>",
"img_url": "/service_images/cage_hire.png"
},
{
"service_id": 29,
"service_name": "Aggregates",
"description": "<p>Aggregates give volume, stability, resistance to wear or erosion, and other desired physical properties to the finished product.</p>",
"img_url": "/service_images/aggregate.png"
},
{
"service_id": 80,
"service_name": "Concrete",
"description": "<p>Ready Mix Concrete is a batch of concrete that is pre-mixed before being delivered to the project site. Tell us the volume required, and we have it delivered to site in a volume metric vehicle. </p>",
"img_url": "/service_images/concrete.png"
},
{
"service_id": 43,
"service_name": "Portaloo",
"description": "<p>We have portaloos available for varied workforce sizes, hired on a weekly basis with an additional haulage charge.</p>",
"img_url": "/service_images/portallo.png"
},
{
"service_id": 60,
"service_name": "Plant Hire",
"description": "<p>Plant hire consists of a varirty of tools, plant and equipment necessary for construction site operations. </p>",
"img_url": "/service_images/plant_hire.png"
}
]
}
"""
let data = Data(json.utf8)
struct Response: Decodable {
let code: Int
let description: String
let result: [Result]
}
struct Result: Decodable {
let serviceId: Int
let serviceName, description, imgUrl: String
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let response = try decoder.decode(Response.self, from: data)
print(response.result)
} catch {
print(error)
}

How to handle failure cases of JSON response using Codable?

I have some JSON response, that I take from a server. In success case, it might be like:
{
"success": true,
"data": [
{
/// something here
}
]
}
If all server responses would be successful, it would be really easy to parse that JSON. But we have also failure cases like:
{
"success": false,
"msg": "Your session expired",
"end_session": true
}
That means we need to handle two cases. As you noticed, attributes like success, msg may occur in any response. In order to handle that, I created following struct:
struct RegularResponse<T: Codable>: Codable {
let success: Bool
let msg: String?
let endSession: Bool?
let data: T?
enum CodingKeys: String, CodingKey {
case success, msg, data
case endSession = "end_session"
}
}
It may contain some data if response is successfull or otherwise, it is possible to identify why the error occurred(using success attribute or msg). Parsing process would go like following:
let model = try JSONDecoder().decode(RegularResponse<MyModel>.self, from: data)
if model.success {
// do something with data
} else {
// handle error
}
Everything works fine, but what if following JSON comes as following:
{
"success": true,
"name": "Jon Snow",
"living_place": "Nights Watch",
//some other fields
}
Here, I don't have data attribute. It means, my RegularResponse cannot be parsed. So, the question is how to handle these kind of situations? My idea for solution is simple: always put data in success cases into data field on my API. By doing so, my RegularResponse will always work, no matter what is inside data. But, it requires changes on a server side. Can this be fixed in a client side, not changing a server side? In other words, how to handle above situation in Swift using Codable?
I'm not sure if this is the best solution but if you know that your error response is in that shape, i.e.:
{
"success": false,
"msg": "Some error",
"end_session": "true",
}
then you could make another Codable struct/class that follows this response.
struct ErrorResponse: Codable {
let success: Bool
let msg: String
let end_session: String
}
and then when you are responding to your JSON you could adjust your code to:
if let successResponse = try? JSONDecoder().decode(RegularResponse<MyModel>.self, from: data) {
//handle success
} else if let responseError = try? JSONDecoder().decode(ErrorResponse.self, from data) {
//handle your error
}

How to make network calls from Siri Intent Extension?

I am trying to integrate Siri from my app. Actually my requirement is do some UDP call with my server but as it was not happening I tried with simple network call with URLSession and URLRequest. For testing I am trying to visit google.com and print the response, but the retured data from the completion handler is not convertible to String and is returning nil. Is the anything I need to add extra either in the Info.plist file or in other places to make the network calls succeed in Intent Extension ?
In the official documentation also there is no mention of this. The code I am using is simple actually but still I am pasting that below:
let session = URLSession.shared
let task = session.dataTask(with: URLRequest(url: URL(string: "https://www.google.com/")!), completionHandler: { (tcpData, response, error) in
if error != nil {
print("tcp error \(error!.localizedDescription)")
}
if let res = response as? HTTPURLResponse {
print(res.statusCode)
}
if tcpData != nil {
print(String(data: tcpData!, encoding: String.Encoding.utf8) ?? "N/A")
}
})
task.resume()
completion(TurnOnNodeAppIntentResponse.init(code: TurnOnNodeAppIntentResponseCode.success, userActivity: nil))
here is the response is always nil though statusCode is 200.
There is nothing wrong with your code. Not all data can be converted to a String. You can read more about that in this answer. Just hitting https://www.google.com/ doesn't return a string. However, if you really want to print the a string of the data from https://www.google.com/ just use print(tcpData!.base64EncodedString()) and you'll get something like: PCFkb2N0eXBlIGh0bWw+PGh0bWwgaXRlbXNjb3BlPSIiIGl0ZW10eXBlPSJodHRwOi8vc....
Switch that URL to an open API like the SpaceX API and you'll get a valid string.
{
"flight_number": 83,
"mission_name": "Amos-17",
"mission_id": [
],
"launch_year": "2019",
"launch_date_unix": 1565131920,
"launch_date_utc": "2019-08-06T22:52:00.000Z",
"launch_date_local": "2019-08-06T18:52:00-04:00",
"is_tentative": false,
"tentative_max_precision": "hour",
"tbd": false,
"launch_window": 5280,
"rocket": {
"rocket_id": "falcon9",
"rocket_name": "Falcon 9",
"rocket_type": "FT",
"first_stage": {
"cores": [
{
"core_serial": "B1047",
"flight": 3,
"block": 5,
"gridfins": false,
"legs": false,
"reused": true,
"land_success": null,
"landing_intent": false,
"landing_type": null,
"landing_vehicle": null
}
]
},
"second_stage": {
"block": 5,
"payloads": [
{
"payload_id": "Amos-17",
"norad_id": [
],
"reused": false,
"customers": [
"Spacecom"
],
"nationality": "Israel",
"manufacturer": "Boeing Satellite Systems",
"payload_type": "Satellite",
"payload_mass_kg": 6500,
"payload_mass_lbs": 14330.05,
"orbit": "GTO",
"orbit_params": {
"reference_system": "geocentric",
"regime": "geostationary",
"longitude": 17,
"semi_major_axis_km": null,
"eccentricity": null,
"periapsis_km": null,
"apoapsis_km": null,
"inclination_deg": null,
"period_min": null,
"lifespan_years": 15,
"epoch": null,
"mean_motion": null,
"raan": null,
"arg_of_pericenter": null,
"mean_anomaly": null
},
"uid": "UerI6qmZTU2Fx2efDFm3QQ=="
}
]
},
"fairings": {
"reused": false,
"recovery_attempt": true,
"recovered": true,
"ship": "GOMSTREE"
}
},
"ships": [
"GOMSTREE",
"GONAVIGATOR"
],
"telemetry": {
"flight_club": null
},
"launch_site": {
"site_id": "ccafs_slc_40",
"site_name": "CCAFS SLC 40",
"site_name_long": "Cape Canaveral Air Force Station Space Launch Complex 40"
},
"launch_success": true,
"links": {
"mission_patch": "https://images2.imgbox.com/a0/ab/XUoByiuR_o.png",
"mission_patch_small": "https://images2.imgbox.com/f1/4a/WAkSmKfY_o.png",
"reddit_campaign": "https://www.reddit.com/r/spacex/comments/cjaawx/amos17_launch_campaign_thread",
"reddit_launch": "https://www.reddit.com/r/spacex/comments/cmedgn/rspacex_amos17_official_launch_discussion_updates",
"reddit_recovery": null,
"reddit_media": "https://www.reddit.com/r/spacex/comments/cmppne/rspacex_amos17_media_thread_videos_images_gifs",
"presskit": "https://www.spacex.com/sites/spacex/files/amos-17_mission_press_kit_8_6_2019.pdf",
"article_link": "https://spaceflightnow.com/2019/08/07/spacex-launches-israeli-owned-telecom-satellite/",
"wikipedia": "https://en.wikipedia.org/wiki/Spacecom",
"video_link": "https://youtu.be/fZh82-WcCuo",
"youtube_id": "fZh82-WcCuo",
"flickr_images": [
"https://live.staticflickr.com/65535/48478269312_58dd3dc446_o.jpg",
"https://live.staticflickr.com/65535/48478269747_353dcb2e62_o.jpg",
"https://live.staticflickr.com/65535/48478119901_2de0441026_o.jpg",
"https://live.staticflickr.com/65535/48478120646_ab72c2c6c3_o.jpg",
"https://live.staticflickr.com/65535/48478120031_5aae1f6131_o.jpg",
"https://live.staticflickr.com/65535/48478269442_08479bed36_o.jpg"
]
},
"details": "SpaceX will launch Boeing built Amos-17, a geostationary communications satellite for Israeli company Spacecom. The satellite will be delivered to GTO from KSC LC-39A or possibly CCAFS SLC-40, and will replace the defunct Amos-5 at 17° E. Amos-17 carries multi-band high throughput and regional beams servicing Africa, Europe and the Middle East. The cost of this launch is covered for Spacecom by SpaceX credit following the Amos-6 incident. A recovery of the booster for this mission is not expected.",
"upcoming": false,
"static_fire_date_utc": "2019-08-01T00:00:00.000Z",
"static_fire_date_unix": 1564617600,
"timeline": null,
"crew": null
}
Also you might want to stop force unwrapping and instead use if let or guard
let session = URLSession.shared
let task = session.dataTask(with: URLRequest(url: URL(string: "https://api.spacexdata.com/v3/launches/latest")!), completionHandler: { (tcpData, response, error) in
if let err = error {
print("tcp error \(err.localizedDescription)")
}
if let res = response as? HTTPURLResponse {
print(res.statusCode)
}
if let data = tcpData {
print(String(data: data, encoding: String.Encoding.utf8) ?? "N/A")
}
})
task.resume()
completion(TurnOnNodeAppIntentResponse.init(code: TurnOnNodeAppIntentResponseCode.success, userActivity: nil))

Multiple JSON parsing from different URLs in Swift

I want to parse JSON strings from different URLs in my iOS Tab Bar app:
Parsing.swift
FirstViewController.swift (INITIAL Tab Bar View Controller)
SecondViewController.swift
...
In Parsing.swift I have various struct (TopLevel) and enum schemes I have controlled in Playground: they works perfectly. In every ViewController I have a Table View that I want to popolate with results of different JSON parsing. This is my simplified code:
FirstViewController.swift viewDidLoad()
let url = // my first URL to parse
let urlObj = URL(string: url)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: urlObj!) { (data, response, error) in
do {
let results = try JSONDecoder().decode(TopLevel.self, from: data!)
... for ...
self.table.reloadData()
}
catch {
...
}
}
task.resume()
This code works perfectly: When app first open, Table View in FirstViewController popolates with results of JSON Parsing from url. But now its time to click on second Bar Item to open SecondViewController. The code is obviously:
SecondViewController.swift viewDidLoad()
let url2 = // my second URL to parse
let urlObj2 = URL(string: url2)
let config2 = URLSessionConfiguration.default
let session2 = URLSession(configuration: config2)
let task2 = session.dataTask(with: urlObj2!) { (data2, response2, error2) in
do {
let results2 = try JSONDecoder().decode(TopLevel.self, from: data2!)
... for ...
self.table2.reloadData()
}
catch {
...
}
}
task2.resume()
Well, when I tap on second Tab Bar Item to open the SecondViewController, Table View don't popolate and XCode gives an error: dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}))) But JSON text is valid.
I have tried a lot of solutions: I've changed tasks to URLSession.shared, I have used private struct and enum, I have controlled variables and costants, well, no way to parse second URL correctly. Even if I create a NEW Single View App and I copy the SecondViewController.swift code into the viewDidLoad() func, it works perfectly, so, again, its not a problem of the second URL, the JSON strings are valid. I think there is an interference between the two parsing tasks, it looks like the first corrupted the second one. What can I do? Thanks.
EDIT: this is my JSON (all fields are valid strings, I have deleted it for simplify)
{
"attributes": {
"version": "2.0",
"nodeValue": "\n"
},
"channel": {
"title": " ",
"link": " ",
"description": " ",
"lastBuildDate": " ",
"language": " ",
"copyright": " ",
"item": [
{
"title": " ",
"link": " ",
"guid": {
"attributes": {
"isPermaLink": "false",
"nodeValue": " "
}
},
"pubDate": " ",
"category": " "
},
{
"title": " ",
"link": " ",
"guid": {
"attributes": {
"isPermaLink": "false",
"nodeValue": " "
}
},
"pubDate": " ",
"category": " "
}
]
} }
As I don't have access to JSON response and Model used.
I can assume few possibilities that may cause this issue.
1) You have your model and JSON response. When you are trying to decode, there could any field in JSON response that is null and same property in your model is not made optional.
2) The model may not have same structure (properties) as JSON response.
Well, I resolved the issue changing my second URL from "WWW.myserver.net/string2.json" to "myserver.net/string2.json", simply without WWW. In this way both tasks works and parses respective strings from different URLs.

How to get facebook feed with images and videos along with text?

I want to fetch logged user feeds within ios app, I have try with graph api that, graph api returns the data, its fine.
But when I have cross check the data to the actual facebook feed on facebook user page there are some problems found:
If post has text only than text coming on message key, along with post id,
If post has text with image/video only text coming on message key
And if post has only image/video than message key not coming.
I know message key is only for description/text but images? Is there any way to get the feed with whole content just same as appeared on facebook.
I have try below code:
For Login
FBSDKLoginManager().logOut()
FBSDKLoginManager().logIn(withReadPermissions: ["email", "public_profile", "user_posts"], from: self) { (result, error) in
if error != nil {
print("failed to start graph request: \(String(describing: error))")
return
}
// self.getPosts()//old
self.getFBPost()
}
For getFBPost
func getFBPost() {
FBSDKGraphRequest(graphPath: "/me/feed", parameters: nil)?.start(completionHandler: { (fbConnectionErr, result, error) in
// print(fbConnectionErr)
print(result)
//print(error)
})
}
Coming response like this:
{
"data": [
{
"message": "https://medium.com/frame-io-engineering/huge-images-small-phone-15414b30d39e",
"created_time": "2018-12-03T13:59:01+0000",
"id": "68288174406_653966194"
},
{
"created_time": "2018-12-01T13:43:02+0000",
"id": "68288174406_6528518443724"
},
{
"message": "I love my Mom",
"created_time": "2018-11-30T13:27:38+0000",
"id": "68288174406_652289420323"
}
}
as you can see the second post has only id and created time, while we check this on facebook page this the post with image, and third post has video but only text coming from graph api
Please guide whats wrong I did? Or What should I do to getting whole feed data in json formate just same as appeared on facebook?
You are not asking for any fields in the API call, so you only get default ones. It is called "Declarative Fields" (/me?fields=message,created_time,...) and came with v2.4:
https://developers.facebook.com/docs/graph-api/changelog/archive#v2_4_new_features
https://developers.facebook.com/docs/graph-api/using-graph-api/#reading
I have read the give doc that are shared by #luschn. And got the solution I forgot the declarative fields as #luschn suggest me, Now solution given below: Only need to change in the graph api,
FBSDKGraphRequest(graphPath: "/me/feed", parameters: ["fields":"created_time,attachments,type,message"])?.start(completionHandler: { (fbConnectionErr, result, error) in
print(fbConnectionErr)
print(result)
print(error)
let re = result as? [String: Any]
let data = re?["data"] as! [[String: Any]]
for dict in data {
print(dict)
}
})
OR
If you have page id you can use below url to hit the get api.
https://graph.facebook.com/(api_version)/(page_id)?fields=feed{created_time,attachments,message}&access_token=(token_id)

Resources