138

Following code I have written and I am getting response in JSON also but the type of JSON is "AnyObject" and I am not able to convert that into Array so that I can use that.

Alamofire.request(.POST, "MY URL", parameters:parameters, encoding: .JSON) .responseJSON
{
    (request, response, JSON, error) in

    println(JSON?)
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Developer
  • 6,375
  • 12
  • 58
  • 92
  • I didn't downvote your question but I assume its because parsing JSON is too broad topic to give a clear, straight answer. Try this library called [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON). – Isuru Sep 30 '14 at 10:21
  • @Isuru Its OK! I have seen that Library but I am using Alamofire! But can you send me the sample code in which you have used the SwiftyJson? There code doesn't worked for me! – Developer Sep 30 '14 at 10:49
  • I too use SwiftyJSON along with Alamofire. I just pass the response in like this `let data = JSONValue(JSON!)`. Then I can extract values like this `data["Id"]`. The SwiftyJSON documentation provides examples of how to retrieve those values in desired types. What exactly the error are you getting? – Isuru Sep 30 '14 at 12:09

14 Answers14

165

The answer for Swift 2.0 Alamofire 3.0 should actually look more like this:

Alamofire.request(.POST, url, parameters: parameters, encoding:.JSON).responseJSON
{ response in switch response.result {
                case .Success(let JSON):
                    print("Success with JSON: \(JSON)")

                    let response = JSON as! NSDictionary

                    //example if there is an id
                    let userId = response.objectForKey("id")!

                case .Failure(let error):
                    print("Request failed with error: \(error)")
                }
    }

https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%203.0%20Migration%20Guide.md

UPDATE for Alamofire 4.0 and Swift 3.0 :

Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default)
            .responseJSON { response in
                print(response)
//to get status code
                if let status = response.response?.statusCode {
                    switch(status){
                    case 201:
                        print("example success")
                    default:
                        print("error with response status: \(status)")
                    }
                }
//to get JSON return value
            if let result = response.result.value {
                let JSON = result as! NSDictionary
                print(JSON)
            }

        }
Joseph Geraghty
  • 1,827
  • 1
  • 10
  • 7
  • 17
    How do you get at the actual content of JSON? What type of object is this? The design and documentation is so obscure I can't figure it out and can't find any examples on the internet... – Alex Worden Jan 18 '16 at 21:22
  • I added a couple lines in my answer that should help. – Joseph Geraghty Jan 19 '16 at 22:29
  • @JosephGeraghty having the encoding parameter results in the compiler telling me there is an extra argument call... Any idea? – dispatchswift Nov 19 '16 at 23:17
  • @jch-duran not positive, but I vaguely remember running into something similar a while back. I think it had something to do with the libraries not being updated or maybe not current with the swift version. Making sure you are on the latest versions might help – Joseph Geraghty Dec 01 '16 at 22:25
  • 1
    @AlexWorden agreed, this page helped me answer those questions and provides a nice solution: https://github.com/SwiftyJSON/SwiftyJSON – iljn Jun 09 '17 at 03:10
  • also posted an answer with an up to date implementation: https://stackoverflow.com/a/44448861/7263704 – iljn Jun 09 '17 at 03:37
  • The updated Swift 3 answer is a terrible example. Do not use `NSDictionary` in Swift 3. Use a Swift dictionary. – rmaddy Sep 02 '17 at 16:18
32

like above mention you can use SwiftyJSON library and get your values like i have done below

Alamofire.request(.POST, "MY URL", parameters:parameters, encoding: .JSON) .responseJSON
{
    (request, response, data, error) in

var json = JSON(data: data!)

       println(json)   
       println(json["productList"][1])                 

}

my json product list return from script

{ "productList" :[

{"productName" : "PIZZA","id" : "1","productRate" : "120.00","productDescription" : "PIZZA AT 120Rs","productImage" : "uploads\/pizza.jpeg"},

{"productName" : "BURGER","id" : "2","productRate" : "100.00","productDescription" : "BURGER AT Rs 100","productImage" : "uploads/Burgers.jpg"}    
  ]
}

output :

{
  "productName" : "BURGER",
  "id" : "2",
  "productRate" : "100.00",
  "productDescription" : "BURGER AT Rs 100",
  "productImage" : "uploads/Burgers.jpg"
}
Ryan Brodie
  • 6,554
  • 8
  • 40
  • 57
Vikram Pote
  • 5,433
  • 4
  • 33
  • 37
  • I'm trying to use the SwiftyJson thing after installing but gives some 300 error's in SwiftyJson file, did anyone face the issue? i', using Xcode version 6.2, ios version 8.1, cocoaPods 36 as mentioned in the [github] (https://github.com/SwiftyJSON/SwiftyJSON) documentation . – Sashi Apr 17 '15 at 19:33
  • 2
    Dude. What are the errors? Ask a separate question and provide some details. SwiftyJSON is as beautiful as magic. Use it if possible. – Zia Aug 10 '15 at 17:17
  • Really should be converting the json string to a concrete swift object so that you can cleanly use it in a natural way. Accessing fields by their string name is ridiculous and prone to error. – The Muffin Man Sep 02 '17 at 21:55
31

Swift 3, Alamofire 4.4, and SwiftyJSON:

Alamofire.request(url, method: .get)
  .responseJSON { response in
      if response.data != nil {
        let json = JSON(data: response.data!)
        let name = json["people"][0]["name"].string
        if name != nil {
          print(name!)
        }
      }
  }

That will parse this JSON input:

{
  people: [
    { name: 'John' },
    { name: 'Dave' }
  ]
}
iljn
  • 889
  • 9
  • 7
  • There's also an Alamofire Swifty-JSON-specific plugin that removes the need for the explicit `JSON()` conversion: https://github.com/SwiftyJSON/Alamofire-SwiftyJSON – Robin Macharg Mar 29 '18 at 08:56
  • This help me, but I did have some problems with JSON method because throws Exception – iGhost May 08 '19 at 16:46
25

I found the answer on GitHub for Swift2

https://github.com/Alamofire/Alamofire/issues/641

Alamofire.request(.GET, URLString, parameters: ["foo": "bar"])
    .responseJSON { request, response, result in
        switch result {
        case .Success(let JSON):
            print("Success with JSON: \(JSON)")

        case .Failure(let data, let error):
            print("Request failed with error: \(error)")

            if let data = data {
                print("Response data: \(NSString(data: data, encoding: NSUTF8StringEncoding)!)")
            }
        }
    }
Jeef
  • 26,861
  • 21
  • 78
  • 156
mick80234
  • 897
  • 8
  • 9
  • 3
    This is correct version for Swift 2.0 + Alamofire JSON parsing. – Saqib Omer Sep 21 '15 at 13:49
  • 5
    hmm i am still getting fail built an error message: '(_, _, _) -> Void' is not convertible to 'Response -> Void' – alex Oct 02 '15 at 18:33
  • @alex See [this answer](http://stackoverflow.com/a/32960847/596239) for what I used to resolve it. – Joseph Oct 29 '15 at 16:27
  • Thank you so much ! You have no idea how many things i tried to display properly the response message from the server, life savior ! – thibaut noah Feb 04 '16 at 12:20
17

I'm neither a JSON expert nor a Swift expert, but the following is working for me. :) I have extracted the code from my current app, and only changed "MyLog to println", and indented with spaces to get it to show as a code block (hopefully I didn't break it).

func getServerCourseVersion(){

    Alamofire.request(.GET,"\(PUBLIC_URL)/vtcver.php")
        .responseJSON { (_,_, JSON, _) in
          if let jsonResult = JSON as? Array<Dictionary<String,String>> {
            let courseName = jsonResult[0]["courseName"]
            let courseVersion = jsonResult[0]["courseVersion"]
            let courseZipFile = jsonResult[0]["courseZipFile"]

            println("JSON:    courseName: \(courseName)")
            println("JSON: courseVersion: \(courseVersion)")
            println("JSON: courseZipFile: \(courseZipFile)")

          }
      }
}

Hope this helps.

Edit:

For reference, here is what my PHP Script returns:

[{"courseName": "Training Title","courseVersion": "1.01","courseZipFile": "101/files.zip"}]
KenL
  • 197
  • 1
  • 6
  • This should be the selected answer although you may want to update it as Alamofire has updated their methods a little bit – Snymax Aug 15 '18 at 14:03
17

Swift 5

class User: Decodable {

    var name: String
    var email: String
    var token: String

    enum CodingKeys: String, CodingKey {
        case name
        case email
        case token
    }

    public required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        self.email = try container.decode(String.self, forKey: .email)
        self.token = try container.decode(String.self, forKey: .token)
    }
}

Alamofire API

    Alamofire.request("url.endpoint/path", method: .get, parameters: params, encoding: URLEncoding.queryString, headers: nil)
     .validate()
     .responseJSON { response in

        switch (response.result) {

            case .success( _):

            do {
                let users = try JSONDecoder().decode([User].self, from: response.data!)
                print(users)

            } catch let error as NSError {
                print("Failed to load: \(error.localizedDescription)")
            }

             case .failure(let error):
                print("Request error: \(error.localizedDescription)")
         }
Sazzad Hissain Khan
  • 37,929
  • 33
  • 189
  • 256
11

swift 3

pod 'Alamofire', '~> 4.4'
pod 'SwiftyJSON'

File json format:
{
    "codeAd": {
        "dateExpire": "2017/12/11",
        "codeRemoveAd":"1231243134"
        }
}

import Alamofire
import SwiftyJSON
    private func downloadJson() {
        Alamofire.request("https://yourlinkdownloadjson/abc").responseJSON { response in
            debugPrint(response)

            if let json = response.data {
                let data = JSON(data: json)
                print("data\(data["codeAd"]["dateExpire"])")
                print("data\(data["codeAd"]["codeRemoveAd"])")
            }
        }
    }
Giang
  • 3,553
  • 30
  • 28
7

This was build with Xcode 10.1 and Swift 4

Perfect combination "Alamofire"(4.8.1) and "SwiftyJSON"(4.2.0). First you should install both pods

pod 'Alamofire' and pod 'SwiftyJSON'

The server response in JSON format:

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip;q=1.0, compress;q=0.5", 
    "Accept-Language": "en;q=1.0", 
    "Host": "httpbin.org", 
    "User-Agent": "AlamoFire TEST/1.0 (com.ighost.AlamoFire-TEST; build:1; iOS 12.1.0) Alamofire/4.8.1"
  }, 
  "origin": "200.55.140.181, 200.55.140.181", 
  "url": "https://httpbin.org/get"
}

In this case I want print the "Host" info : "Host": "httpbin.org"

Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in
        switch response.result {
        case .success:
            print("Validation Successful)")

            if let json = response.data {
                do{
                    let data = try JSON(data: json)
                    let str = data["headers"]["Host"]
                    print("DATA PARSED: \(str)")
                }
                catch{
                print("JSON Error")
                }

            }
        case .failure(let error):
            print(error)
        }
    }

Keep Calm and happy Code

iGhost
  • 1,009
  • 11
  • 21
  • 1
    Thank you. I have used and reformatted to fit my needs on my project and I can vouch that this still holds for Swift 5. – Jiraheta Sep 16 '21 at 06:20
2

I found a way to convert the response.result.value (inside an Alamofire responseJSON closure) into JSON format that I use in my app.

I'm using Alamofire 3 and Swift 2.2.

Here's the code I used:

    Alamofire.request(.POST, requestString,
                      parameters: parameters,
                      encoding: .JSON,
                      headers: headers).validate(statusCode: 200..<303)
                                       .validate(contentType: ["application/json"])
                                       .responseJSON { (response) in
        NSLog("response = \(response)")

        switch response.result {
        case .Success:
            guard let resultValue = response.result.value else {
                NSLog("Result value in response is nil")
                completionHandler(response: nil)
                return
            }

            let responseJSON = JSON(resultValue)

            // I do any processing this function needs to do with the JSON here

            // Here I call a completionHandler I wrote for the success case
        break
        case .Failure(let error):
            NSLog("Error result: \(error)")
            // Here I call a completionHandler I wrote for the failure case
            return
        }
Carl Smith
  • 1,236
  • 15
  • 18
2

I usually use Gloss library to serialize or deserialize JSON in iOS. For example, I have JSON that looks like this:

{"ABDC":[{"AB":"qwerty","CD":"uiop"}],[{"AB":"12334","CD":"asdf"}]}

First, I model the JSON array in Gloss struct:

Struct Struct_Name: Decodable {
   let IJ: String?
   let KL: String?
   init?(json: JSON){
       self.IJ = "AB" <~~ json
       self.KL = "CD" <~~ json
   }
}

And then in Alamofire responseJSON, I do this following thing:

Alamofire.request(url, method: .get, paramters: parametersURL).validate(contentType: ["application/json"]).responseJSON{ response in
 switch response.result{
   case .success (let data):
    guard let value = data as? JSON,
       let eventsArrayJSON = value["ABDC"] as? [JSON]
    else { fatalError() }
    let struct_name = [Struct_Name].from(jsonArray: eventsArrayJSON)//the JSON deserialization is done here, after this line you can do anything with your JSON
    for i in 0 ..< Int((struct_name?.count)!) {
       print((struct_name?[i].IJ!)!)
       print((struct_name?[i].KL!)!)
    }
    break

   case .failure(let error):
    print("Error: \(error)")
    break
 }
}

The output from the code above:

qwerty
uiop
1234
asdf
Luthfi Rahman
  • 424
  • 6
  • 17
1

in swift 5 we do like, Use typealias for the completion. Typlealias nothing just use to clean the code.

typealias response = (Bool,Any?)->()


static func postCall(_ url : String, param : [String : Any],completion : @escaping response){
    Alamofire.request(url, method: .post, parameters: param, encoding: JSONEncoding.default, headers: [:]).responseJSON { (response) in

        switch response.result {
           case .success(let JSON):
               print("\n\n Success value and JSON: \(JSON)")

           case .failure(let error):
               print("\n\n Request failed with error: \(error)")

           }
    }
}
Talha Rasool
  • 1,126
  • 14
  • 12
1

The easy answer is to let AlamoFire do the decoding directly.

Surprisingly, you don't use the .responseJSON because that returns an untyped json object

Instead, you make your objects Decodable - and ask AF to decode directly to them

My json response contains an array of Account objects. I only care about the id and name keys (though there are many more)

struct Account:Codable {
    let id:Int
    let name:String
}

then simply

    AF.request(url,
               method: .get)
        .responseDecodable(of:[Account].self) { response in
            switch response.result {
            case .success:
                switch response.response?.statusCode {
                case 200:
                    //response.value is of type [Account]
                default:
                    //handle other cases
                }
            case let .failure(error):
                //probably the decoding failed because your json doesn't match the expected format
            }
        }
Confused Vorlon
  • 9,659
  • 3
  • 46
  • 49
-3
let semaphore = DispatchSemaphore (value: 0)

        var request = URLRequest(url: URL(string: Constant.localBaseurl2 + "compID")!,timeoutInterval: Double.infinity)
                            request.httpMethod = "GET"
                            let task = URLSession.shared.dataTask(with: request) { data, response, error in
                                if let response = response {
                                let nsHTTPResponse = response as! HTTPURLResponse
                                print(nsHTTPResponse)
                                          
                                }
                                if let error = error {
                                print ("\(error)")
                                    return
                                 }
                                
                                if let data = data {
                                    DispatchQueue.main.async {
                                        
                                        let decoder = JSONDecoder()
                                        decoder.keyDecodingStrategy = .convertFromSnakeCase//or any other Decoder\
                                        
                                        do{
                                            
                                            let jsonDecoder = JSONDecoder()
                                            let memberRecord = try jsonDecoder.decode(COMPLAINTSVC.GetComplaints.self, from: data)
                                            print(memberRecord.message)
                                         
                                            for detailData in memberRecord.message{
                                                print(detailData)
                                                
                                                
                                                
                                                
                                            
                                            }
                                            
                                            
                                            
                                        }catch{
                                            
                                            print(error.localizedDescription)
                                        }
                                        
                                      
                                        
                                    }
                             
                                }
                              
                              semaphore.signal()
                            }

                            task.resume()
                            semaphore.wait()

}

  • OP asks about Alamofire, not about URLRequest, so this is off-topic. Also your answer contains unrelated elements and seems to just be randomly copied from an existing code base. Please edit and make it specific to this question. Thanks. – Eric Aya Jun 13 '21 at 11:03
-11
 pod 'Alamofire'
 pod 'SwiftyJSON'
 pod 'ReachabilitySwift'



import UIKit
import Alamofire
import SwiftyJSON
import SystemConfiguration

class WebServiceHelper: NSObject {

    typealias SuccessHandler = (JSON) -> Void
    typealias FailureHandler = (Error) -> Void

    // MARK: - Internet Connectivity

    class func isConnectedToNetwork() -> Bool {

        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
        zeroAddress.sin_family = sa_family_t(AF_INET)

        guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                SCNetworkReachabilityCreateWithAddress(nil, $0)
            }
        }) else {
            return false
        }

        var flags: SCNetworkReachabilityFlags = []
        if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
            return false
        }

        let isReachable = flags.contains(.reachable)
        let needsConnection = flags.contains(.connectionRequired)

        return (isReachable && !needsConnection)
    }

    // MARK: - Helper Methods

    class func getWebServiceCall(_ strURL : String, isShowLoader : Bool, success : @escaping SuccessHandler, failure : @escaping FailureHandler)
    {
        if isConnectedToNetwork() {

            print(strURL)

            if isShowLoader == true {

                AppDelegate.getDelegate().showLoader()
            }

            Alamofire.request(strURL).responseJSON { (resObj) -> Void in

                print(resObj)

                if resObj.result.isSuccess {
                    let resJson = JSON(resObj.result.value!)

                    if isShowLoader == true {
                        AppDelegate.getDelegate().dismissLoader()
                    }

                    debugPrint(resJson)
                    success(resJson)
                }
                if resObj.result.isFailure {
                    let error : Error = resObj.result.error!

                    if isShowLoader == true {
                        AppDelegate.getDelegate().dismissLoader()
                    }
                    debugPrint(error)
                    failure(error)
                }
            }
        }else {


            CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
        }
    }

    class func getWebServiceCall(_ strURL : String, params : [String : AnyObject]?, isShowLoader : Bool, success : @escaping SuccessHandler,  failure :@escaping FailureHandler){
        if isConnectedToNetwork() {

            if isShowLoader == true {
                AppDelegate.getDelegate().showLoader()
            }


            Alamofire.request(strURL, method: .get, parameters: params, encoding: JSONEncoding.default, headers: nil).responseJSON(completionHandler: {(resObj) -> Void in

                print(resObj)

                if resObj.result.isSuccess {
                    let resJson = JSON(resObj.result.value!)

                    if isShowLoader == true {
                        AppDelegate.getDelegate().dismissLoader()
                    }

                    success(resJson)
                }
                if resObj.result.isFailure {
                    let error : Error = resObj.result.error!

                    if isShowLoader == true {
                        AppDelegate.getDelegate().dismissLoader()
                    }

                    failure(error)
                }

            })
        }
    else {

            CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
    }

    }



    class func postWebServiceCall(_ strURL : String, params : [String : AnyObject]?, isShowLoader : Bool, success : @escaping SuccessHandler, failure :@escaping FailureHandler)
    {
        if isConnectedToNetwork()
        {

            if isShowLoader == true
            {
                AppDelegate.getDelegate().showLoader()
            }

            Alamofire.request(strURL, method: .post, parameters: params, encoding: JSONEncoding.default, headers: nil).responseJSON(completionHandler: {(resObj) -> Void in

                print(resObj)

                if resObj.result.isSuccess
                {
                    let resJson = JSON(resObj.result.value!)

                    if isShowLoader == true
                    {
                        AppDelegate.getDelegate().dismissLoader()
                    }

                    success(resJson)
                }

                if resObj.result.isFailure
                {
                    let error : Error = resObj.result.error!

                    if isShowLoader == true
                    {
                        AppDelegate.getDelegate().dismissLoader()
                    }

                    failure(error)
                }
            })
        }else {
            CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
        }
    }


    class func postWebServiceCallWithImage(_ strURL : String, image : UIImage!, strImageParam : String, params : [String : AnyObject]?, isShowLoader : Bool, success : @escaping SuccessHandler, failure : @escaping FailureHandler)
    {
        if isConnectedToNetwork() {
            if isShowLoader == true
            {
                AppDelegate.getDelegate().showLoader()
            }

            Alamofire.upload(
                multipartFormData: { multipartFormData in
                    if let imageData = UIImageJPEGRepresentation(image, 0.5) {
                        multipartFormData.append(imageData, withName: "Image.jpg")
                    }

                    for (key, value) in params! {

                        let data = value as! String

                        multipartFormData.append(data.data(using: String.Encoding.utf8)!, withName: key)
                        print(multipartFormData)
                    }
                },
                to: strURL,
                encodingCompletion: { encodingResult in
                    switch encodingResult {
                    case .success(let upload, _, _):
                        upload.responseJSON { response in
                            debugPrint(response)
                            //let datastring = String(data: response, encoding: String.Encoding.utf8)
                           // print(datastring)
                        }
                    case .failure(let encodingError):
                        print(encodingError)
                        if isShowLoader == true
                        {
                            AppDelegate.getDelegate().dismissLoader()
                        }

                        let error : NSError = encodingError as NSError
                        failure(error)
                    }

                    switch encodingResult {
                    case .success(let upload, _, _):
                        upload.responseJSON { (response) -> Void in

                            if response.result.isSuccess
                            {
                                let resJson = JSON(response.result.value!)

                                if isShowLoader == true
                                {
                                    AppDelegate.getDelegate().dismissLoader()
                                }

                                success(resJson)
                            }

                            if response.result.isFailure
                            {
                                let error : Error = response.result.error! as Error

                                if isShowLoader == true
                                {
                                    AppDelegate.getDelegate().dismissLoader()
                                }

                                failure(error)
                            }

                        }
                    case .failure(let encodingError):
                        if isShowLoader == true
                        {
                            AppDelegate.getDelegate().dismissLoader()
                        }

                        let error : NSError = encodingError as NSError
                        failure(error)
                    }
                }
            )
        }
        else
        {
            CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
        }
    }

}


==================================


Call Method


let aParams : [String : String] = [
                "ReqCode" : Constants.kRequestCodeLogin,
                ]

            WebServiceHelper.postWebServiceCall(Constants.BaseURL, params: aParams as [String : AnyObject]?, isShowLoader: true, success: { (responceObj) in


                if "\(responceObj["RespCode"])" != "1"
                {
                    let alert = UIAlertController(title: Constants.kAppName, message: "\(responceObj["RespMsg"])", preferredStyle: UIAlertControllerStyle.alert)
                    let OKAction = UIAlertAction(title: "OK", style: .default) { (action:UIAlertAction!) in
                    }
                    alert.addAction(OKAction)
                    self.present(alert, animated: true, completion: nil)
                }
                else
                {
                    let aParams : [String : String] = [
                        "Password" : self.dictAddLogin[AddLoginConstants.kPassword]!,
                        ]
                    CommonMethods.saveCustomObject(aParams as AnyObject?, key: Constants.kLoginData)

                }
                }, failure:
                { (error) in

                    CommonMethods.showAlertWithError(Constants.kALERT_TITLE_Error, strMessage: error.localizedDescription,withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
            })
        }
Nikunj Patel
  • 304
  • 2
  • 7