How to make network calls from Siri Intent Extension? - ios

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))

Related

Creating Library Playlist using Apple Music API and getting 400 - Bad Request?

As per this documentation shared link below,
https://developer.apple.com/documentation/applemusicapi/create_a_new_library_playlist
I'm able to get developer token and music user token but I am getting error for 'Invalid Request Body' (400 Bad Request).
This is the function that I'm using to create apple music playlist.
func createAppleMusicPlaylist() {
let playlistURL = URL(string: Config.appleCreatePlaylist)!
var playlistRequest = URLRequest(url: playlistURL)
playlistRequest.httpMethod = "POST"
let params = ["name" : kPlaylistName,
"description" : kPlaylistDesc]
playlistRequest.httpBody = try? JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
playlistRequest.addValue("Bearer \(DeveloperToken)", forHTTPHeaderField: "Authorization")
playlistRequest.addValue("\(UserToken)", forHTTPHeaderField: "Music-User-Token")
URLSession.shared.dataTask(with: playlistRequest) { data, response, error in
guard error == nil else {
print("\(String(describing: error?.localizedDescription))")
return
}
if let data = data {
let json = try? JSONDecoder().decode(AppleCreatePlaylistModel.self, from: data)
}
}.resume()
}
API Response:
{
"errors": [
{
"id": "X4IK3UU6LYGGRSQ6U2KQANL63A",
"title": "Invalid Request Body",
"detail": "Unable to parse request body",
"status": "400",
"code": "40007"
}
]
}
Please suggest required body parameters.
Thanks!
You need to follow the API documentation, Create a New Library Playlist, and follow the HTTP Body part that causing issue in your case.
HTTP Body
LibraryPlaylistCreationRequest
The POST request containing the name, tracks and parent playlist folder for the playlist to be added.
Now, let's check LibraryPlaylistCreationRequest:
Properties
attributes LibraryPlaylistCreationRequest.Attributes (Required) A dictionary that includes strings for the name and
description of the new playlist.
relationships LibraryPlaylistCreationRequest.Relationships An optional key
including tracks for the new playlist.
So, body should like this, at this point:
{
"attributes": ..., //Required
"relationships": ... //Optional
}
Let's see now LibraryPlaylistCreationRequest.Attributes:
Properties
description string The description of the playlist.
name string (Required) The name of the playlist.description string
So, now, the JSON should look like this (I skipped the relationships part since you don't use it):
{
"attributes": {
"name": "playlist name",
"description": "playlist description"
}
}

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)
}

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.

Post multiple object data in alamofire

The number of posts is available in StackOverflow but i can't get the solution, I am using Alamofire Library and post my result to hit API but I'm not able to get the result please help me to fix it.
{
"People":{ "Prefix": "Mr.",
"FirstName": "Eric",
"MiddleName": null,
"LastName": "John",
"FullName": "Smith GOldi6",
"Email1": "abc123#example.com",
"Email2": null,
"PhoneNumber1": "0000",
"PhoneNumber2": null,
"DateOfBirth": "1975-01-01T00:00:00",
"Status": 3,
"DeleteFlag": true,
"Comments": null,
"Description": null,
"UserName": "abc123"
},
"Password": "password1"
}
this is the result which I create in parameters to post.
Alamofire.request(signup_api, method: .post, parameters: parameters, encoding: URLEncoding.httpBody).responseJSON { (response) in
switch response.result {
case .success(let data) :
let json = JSON(data)
print("response.result = \(response.result)")
print("response = \(response)")
let resJson = JSON(response.result.value!)
print("success = \(resJson)")
if (resJson.count == 0) {
if (resJson.null == nil) {
// self.DimBackground.isHidden = true
// self.activator.stopAnimating()
}
return
}
if let err = response.error{
print("err === \(err)")
return
}
return
case .failure(let _error):
print("_error = \(_error)")
print("false")
print("response.result = \(response.result)")
print("response = \(response)")
print("Network Problem: There might be an issue with your internet connection, or server not responding. Please try again.")
return
}
}
This shows this error:
_error = responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(error:
Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around
character 0." UserInfo={NSDebugDescription=Invalid value around
character 0.})) false response.result = FAILURE response = FAILURE:
responseSerializationFailed(reason:
Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(error:
Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around
character 0." UserInfo={NSDebugDescription=Invalid value around
character 0.}))
if you are getting proper JSON response than there should be issue in your condition
if (resJson.count == 0)
Actually there should be status (true/false) in your response so you get idea which action you need to take if status is true than you have to get proper data from response and processed for further. else you have to show error message and stop for processed further.

Alamofire: NSLocalizedFailure error (status code 403)

I am trying to retrieve the various user ids from task api, perform a loop on them and POST the array of values to submitScore api.
Desired workflow:
User taps on table cell -> app gets the cell id and display items related to that cell -> user presses a button -> app gets stores data in the form of an array and POSTS to an submitScore api
I can retrieve the various user data with a GET request from task api but I don't know how to:
associate my retrieved ids with the cell id
store the retrieved data (user ids and score) in an array and POST it to my submitScore api in the form below
Array form for POST body:
{ "ratings": [{
"safety_rating_id": 105,
"schedule_job_id": 18,
"score": 9,
"submission_id": 27
},
{
"safety_rating_id": 105,
"schedule_job_id": 18,
"score": 9,
"submission_id": 27
}]
}
Which is why I get thrown this error:
Request failed with error: Error Domain=com.alamofire.error Code=-6003 "Response status code was unacceptable: 403" UserInfo={NSLocalizedFailureReason=Response status code was unacceptable: 403}
Code for POST:
func getTaskDetails(onCompletion: () -> (), onError: ((NSError) -> ())? = nil) {
guard let endPoint = Data.sharedInstance.submitScoreEndpoint
else { print("Empty endpoint"); return }
let user = Users()
let Auth_header = [
"Authorization" : user.token,
]
let parameters = [
"ratings" : [
[
"safety_rating_id" : 106,
"schedule_job_id" : 18,
"score" : 10,
"submission_id" : 27
],
[
"safety_rating_id" : 105,
"schedule_job_id" : 18,
"score" : 9,
"submission_id" : 27
]
]
]
Alamofire.request(.POST, endPoint, headers: Auth_header, parameters: parameters)
.validate()
.responseData {
response in
switch response.result {
case .Success(let data):
let json = JSON(data)
print(json)
onCompletion()
case .Failure(let error):
print("Request failed with error: \(error)")
onError?(error)
}
}
}

Resources