How to use PUT request in Alamofire - ios

I am new in swift and I am also trying to use Alamofire to call data from API. I am quite puzzled on how I will use the PUT Request to update data. I've read some solutions here in SO but I don't know how I will apply on my app. I am creating an Event App, the scenario should be, when the participant clicked the Check In Button, it will update the registered_flag to true meaning the participant will marked as Registered and the button will be changed to Check Out. I really don't know if my API Service is correct or not. Hope you could help me. Thank you so much.
JSON of the Event Participant Where in the registered_flag should be updated once checkInOutButton
{
"event_name": "Q & A",
"event_participants": [
{
"participant_id": "70984656-92bc-4c36-9314-2c741f068523",
"employee_number": null,
"last_name": "Surname",
"first_name": "FirstName",
"middle_name": null,
"display_name": "Surname, FirstName ",
"department_name": "Medical Informatics",
"position_name": "Application Developer",
"registered_flag": true,
"registered_datetime": "2018-09-13T08:54:40.150",
"registration_type": 1,
"delete_flag": false,
"manual_reg_flag": false,
"out_flag": false,
"out_datetime": null,
"classification": 6,
"others": "Guest"
}
}
JSON to update for check in
{
"registered_flag": true,
"registration_type": 1
}
updateType
enum UpdateParticipantType: String {
case checkIn = "Check In"
case checkOut = "Check Out"
}
APIService for UpdateParticipant
func updateParticipant(updateType: UpdateParticipantType,
participantID: String,
successBlock: #escaping ([Attendee]) -> Void,
failureBlock: #escaping (Error) -> Void)
{
let updateParticipantURL = URL(string: "\(REGISTER_PARTICIPANT_URL)/\(updateType)/\(participantID)")
Alamofire.request(updateParticipantURL!, method: .put).responseJSON { (response) in
print(response)
if let error = response.error
{
failureBlock(error)
print(error)
return
}
if let jsonArray = response.result.value as? [[String : Any]] {
for anItem in jsonArray {
if let eventparticipants = anItem["event_participants"] as? [[String : Any]] {
var extractedAttendees = [Attendee]()
for participants in eventparticipants{
let success = Attendee.init(JSON: participants)
extractedAttendees.append(success!)
}
successBlock(extractedAttendees)
}
}
}
}
}

As per Alamofire documentation:
let parameters: Parameters = [
"foo": "bar",
"baz": ["a", 1],
"qux": [
"x": 1,
"y": 2,
"z": 3
]
]
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters)
For a given json
{
"registered_flag": true,
"registration_type": 1
}
let parameters: Parameters = [
"registered_flag": true
"registration_type": 1
]

Related

Having Error 3840 "JSON text did not start with array or object and option to allow fragments not set." after a while on the application

I'm having the error 3840 "JSON text did not start with array or
object and option to allow fragments not set." on my application after
being on the same page for a while.
I'm calling a cloud code function from my Parse server, getting the answer and displaying it. It works fine, but after waiting a little bit the application crashes.
I'm calling my manager for the cloud code function:
ParseManager.sharedManager().fetchPrice(startDate: startDate, endDate: endDate, housingId: housing.objectId!) { (objects, error) in
if (objects != []) {
var price = 0
for object in objects! {
price += object["prix"] as! Int
}
price /= objects!.count
self.priceByNight.text = "Price: \(price)"
}
}
In my manager:
func fetchPrice(startDate: Date, endDate: Date, housingId: String, completion: PFResults?) {
let params = [
"startDate": startDate,
"endDate": endDate,
"housing": housingId
] as [String : Any]
PFCloud.callFunction(inBackground: "fetchPrice", withParameters: params) { (objects, error) in
if let error = error {
print(error)
completion?(nil, error)
return
}
if let objects = objects as? [PFObject] {
completion?(objects, nil)
}
}
}
This is what my function send me back:
{
"result": [{
"price": 10,
"createdAt": "2019-05-07T12:39:47.320Z",
"updatedAt": "2019-05-09T15:31:25.957Z",
"date": {
"__type": "Date",
"iso": "2019-05-20T12:00:00.000Z"
},
"idHousing": {
"__type": "Relation",
"className": "myOtherClassName"
},
"objectId": "XXXXXXXXXX",
"__type": "Object",
"className": "MyClassName"
}]
}
The problem was how I was checking what the manager was returning, checking it the wrong way made it try to do things with a null object.
The solution was to change if (objects != []) { to if let objs = objects { and it works now.

Filter a nested array and dictionary

I am trying to filter the array which consist of array and dictionary inside it. I want to filter based on the type of the service and then whose isPrimaryMailbox is Yes under attributes array.
This is what I have done:-
let services = Manager.realm().objects(Service.self).filter("type = %#", "MAILBOX")
let serviceWithPrimaryEmail = services.filter({$0.attributes.first?.value == "Yes"})
But this is showing the data which has isPrimaryMailbox value is No
Below is the json response :-
{
"data": {
"cust": [
{
"customerId": "2040349110",
"serv": [
{
"bill": "2010007656959",
"services": [
{
"type": "MOBILE",
"status": "ACTIVE",
"plan": {
"name": "Mobil"
},
"addOns": [
{
"status": "Active"
}
],
"hardware": [
{
"type": "HANDSET"
}
]
},
{
"type": "MOBILE",
"plan": {
"name": "Mobile Service"
},
"addOns": [
{
"status": "Active"
}
],
"hardware": [
{
"type": "HANDSET",
}
]
},
{
"type": "MAILBOX",
"plan": {
"name": "Service"
},
"attributes": [
{
"name": "mailboxSize",
"value": "1 GB"
},
{
"name": "isPrimaryMailbox",
"value": "Yes"
}
]
},
{
"type": "MAILBOX",
"status": "ACTIVE",
"plan": {
"name": "Service"
},
"attributes": [
{
"name": "mailboxSize",
"value": "1 GB"
},
{
"name": "isPrimaryMailbox",
"value": "No"
}
]
}
]
}
]
}
]
}
}
You can try this:
let serviceWithPrimaryEmail = services.filter({$0.attributes.[1].value == "Yes"})
// I used the index 1 because as I see in you JSON data, the isPrimaryMailBox is always in the second row.
Also, your JSON is badly formatted. If you are gonna use an associated Array, then why do you have to separate the key and value to a different key-value pair?
The attributes can simply be:
"attributes": {
"mailboxSize":"1 GB",
"isPrimaryMailbox":true
}
Using boolean instead of string "YES" or "NO"
So that when you filter, you simple say
let serviceWithPrimaryEmail = services.filter({$0.attributes["isPrimaryMailbox"]})
Or if you really prefer the string "YES" then:
let serviceWithPrimaryEmail = services.filter({$0.attributes["isPrimaryMailbox"] == "YES"})
I don't know the type of services object. But here is how you can get primary email service from your JSON:
typealias JSON = [String: Any]
let json = yourJSONDict as? JSON
let services = (((json["data"] as? JSON)?["cust"] as? JSON)?["serv"] as? JSON)?["services"] as? [JSON]
let primaryEmailService = services?.filter { service in
if (service["type"] as? String) == "MAILBOX" {
for attribute in service["attributes"] as? [[String: String]] ?? [] {
if (attribute["name"] == "isPrimaryMailbox") && (attribute["value"] == "Yes") {
return true
}
}
}
return false
}
.first
Edited:
If you don't want to use for loop, you can use filter and reduce only:
let primaryEmailService = services?
.filter {
let attributes = $0["attributes"] as? [[String: String]] ?? []
return ($0["type"] as? String) == "MAILBOX"
&& attributes.reduce(false, { $0 || (($1["name"] == "isPrimaryMailbox") && ($1["value"] == "Yes")) })
}
.first
Please note, that worse case complexity for both approaches are equal. But best case complexity for the first approach is better, because you don't have to loop over all attributes in reduce function. And Swift compiler isn't smart enough for now to stop reduce looping after the first match (false || true always gives true. There is no need to proceed if we have one true).

getting nil value while parsing JSON in swift using Alamofire

i am parsing "switch_name" from switch array but i am getting nil value while parsing
{
"status": "true",
"result": {
"hubs": [
{
"hub_id": "1",
"user_id": "35",
"switch": [
{
"id": "4",
"hub_id": "1",
"switch_name": "Test2",
"user_id": "35",
"serial_no": "445112",
"topic_sense": "rer",
"device_room": "25",
"switch_type": "LIGHTS",
"types_of_relay_switch": "S"
}
],
"relay": []
}
],
"switchwithouhub": []
}
}
how i am parsing : -
let sName = jsonDict.value(forKeyPath: "result.hubs.switch.switch_name") as? [String]
i am getting nil value while parsing switch_name.
please help and suggest how can i parse JSON
You are trying to access the element of an arrays (hubs, switch) directly. You must provide the proper index to access the item.
let sName = jsonDict.value(forKeyPath: "result.hubs[0].switch[0].switch_name") as? String
UPDATE: You can use SwiftyJson for parsing json data.
import SwiftyJSON
do { let jsonData = try JSON(data: response.data) {
let names = jsonData["hubs"][0]["switch"].array.flatMap({ (switch) -> String in
return switch.name
})
}
catch {
print("Swifty Error")
}

send array of json objects in ios

I have a post request that has a body like this
{
"cars": [
{
"id": 126,
"drivers": [
"xxx#gmail.com"
]
},
{
"id": 128,
"drivers": [
"mmm#gmail.com"
]
}
]
}
the id and drivers are changeable, and I got them from another api so how to send this body with the post request?
on the other hand I have a textField that takes another email of driver, I want to change drivers when this request was sent.
example:
{
"cars": [
{
"id": 126,
"drivers": [
"xxx#gmail.com",
"sss#gmail.com"
]
},
{
"id": 128,
"drivers": [
"mmm#gmail.com"
]
}
]
}
As you can see I want to update the drivers to the new one when I tap add button on the specific textField depends on the id.
This is my code
public static func loadDrivers(owners: [Owner], drivers: [Driver], driverEmail: String!, i: Int, completion: #escaping (_ code:Int?)-> Void) {
let headers: HTTPHeaders = [
"Accept": "application/json"
]
var para2 = [String : [[String : Any]]]()
para2 = [
"cars": [
[
"id": owners[i].id,
"drivers": [
drivers[i].email,
driverEmail
]
]
]
]
if driverEmail != nil {
Alamofire.request(APIHelper.BASE_URL + APIHelper.API.ADD_DRIVERS.rawValue, method: .post, parameters: para2, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
switch response.result {
case .success:
let json = response.result.value as? NSDictionary
let code = json!["code"]
completion(code as? Int)
case .failure(let error):
print(error)
completion(nil)
return
}
}
}
}
Thanks in advance
This question is screaming Codable protocol at me, so here it goes. The Codable protocol makes using true Swift objects to generate JSON a real breeze. Try this in a Playground:
import Cocoa
struct Car : Codable {
let id:Int
let drivers:[String] // you will want to improve on this
}
struct Mobiles : Codable {
let cars:[Car]
}
var mobiles = Mobiles(cars:[Car(id:126, drivers:["xxx#gmail.com", "sss#gmail.com"]),
Car(id:128, drivers:["mmm#gmail.com"])])
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
print(String(data:try encoder.encode(mobiles), encoding:.utf8)!)
Since you should be able to employ Swift on both ends of this equation it is easy to see that this requires a lot less code than your example.
As you said in the Json example you have a array of dictionaries in 'Cars' field but you are making a dictionary inside a dictionary in this line:
var para2 = [String : [[String : Any]]]()
You can define your para2 in this way:
var para2 = [String, Array<[String:Any]>]
and then send it as a parameters

Why I cannot add data into Firebase in this way?

I am doing a project which is concerned about Firebase, I have tried to upload the data to the Firebase as the code in the tutorial from the Firebase website:
// An index to track Ada's memberships
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
// Index Ada's groups in her profile
"groups": {
// the value here doesn't matter, just that the key exists
"techpioneers": true,
"womentechmakers": true
}
},
...
},
"groups": {
"techpioneers": {
"name": "Historical Tech Pioneers",
"members": {
"alovelace": true,
"ghopper": true,
"eclarke": true
}
},
...
}
}
and I follow this structure to write my own data structure, which looks like this:
func pushUser() {
let username = self.username
let userid = FIRAuth.auth()?.currentUser?.uid
let userInfo : [String : Any] = ["username" : username,
"userID" : userid,
"sggestedRoutes" : "",
"myTracedRoutes" : "",
"routes": {
"usualInfo" : true,
"calcuInfo" : true
}
]
let userRef = FIRDatabase.database().reference()
userRef.child("user").childByAutoId().setValue(userInfo)
}
in Swift3, but the debugger says the nested routes{} is not correct, I am quite confused about this, could anyone give me a hint on this?
Kind regards
After received the advice, I make some change on the code, but it is still not working on the Route part:(
func pushUser() {
let username = self.username
let userid = FIRAuth.auth()?.currentUser?.uid
let userInfo : [String : Any] =
["username" : username,
"userID" : userid,
"sggestedRoutes" : "",
"uploadedRoutes" : "",
"myTracedRoutes" : "",
["Routes":
["usualInfo" : true,
"calcuInfo" : true]
]
]
let userRef = FIRDatabase.database().reference()
userRef.child("user").childByAutoId().setValue(userInfo)
}
Try adding the routes as ["routes": ["usualInfo": true, "calcuInfo": true]]
Don't forget the correct closures for both children "user info" and "routes"

Resources