Uploading video to Youtube using its REST API without the iOS SDK - ios

Still a noob, so bear with me. I am using SWIFT 3 and the V3 YouTube Data API with REST. I can pull a list of my videos so my connection and authorization is working just fine.
I can't seem to figure out how to upload though. I found an old post that was very similar to mine (Setting snippet data for youtube upload via REST API using Swift).
I'm confused where they are getting that token variable from and how they pass it into this function. Also, not sure how to set the upload variable that is right before the post. Any help is appreciated!
func uploadVideo(token: String, callback: #escaping (Bool) -> Void){
let headers = ["Authorization": "Bearer \(token)"]
let path = Bundle.main.path(forResource: "intro", ofType: "mov")
let videodata: NSData = NSData.dataWithContentsOfMappedFile(path!)! as! NSData
upload(
.POST,
"https://www.googleapis.com/upload/youtube/v3/videos?part=snippet",
headers: headers,
multipartFormData: { multipartFormData in
multipartFormData.appendBodyPart(data:"{'snippet':{'title' : 'TITLE_TEXT', 'description': 'DESCRIPTION_TEXT'}}".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!, name :"snippet", mimeType: "application/json")
multipartFormData.appendBodyPart(data: videodata, name: "intro", fileName: "intro.mov", mimeType: "application/octet-stream")
},
encodingCompletion: { encodingResult in
switch encodingResult {
case .Success(let upload, _, _):
upload.responseJSON { request, response, error in
print(response)
callback(true)
}
case .Failure(_):
callback(false)
}
})
}

Updated code from https://github.com/mfriedl89/Developer5/blob/master/Conari/Conari/. It's just a poc mostly, refactoring required (e.g: Use Codable for JSON parsing instead of force-unwrapping). And a proper OAuth2 flow should be implemented using SFSafariViewController/SFAuthenticationSession (depending on the targeted iOS version) or Google SignIn
import Foundation
class YoutubeTokenProvider {
// TODO: Store token, evaluate ttl, retry request if needed
static func getAccessToken(callback: #escaping (String?) -> Void){
/* Remark (Security)
Storing the data inside here is not secure, but still better than requesting it from a custom request.
Two possibilities:
1. Storing all data here
2. Storing all data on an external server and only requesting the access token from this server
Option 1 was chosen based on the assumption that decompiling the app should be more difficult than just
monitoring the request and getting the access token (which allows the attacker to do anything with our
YouTube account). The disadvantage is that an attacker gets everything after a successful decompilation
and not only the access token.
*/
let client_secret = "..."
let grant_type = "refresh_token"
let refresh_token = "..."
let client_id = "..."
var request = URLRequest(url: URL(string: "https://accounts.google.com/o/oauth2/token")!)
request.httpMethod = "POST"
let postString = "client_secret=" + client_secret +
"&grant_type=" + grant_type +
"&refresh_token=" + refresh_token +
"&client_id=" + client_id
request.httpBody = postString.data(using: .utf8)
URLSession.shared.dataTask(with: request, completionHandler: {data, response, error in
guard let data = data, error == nil else {
callback(nil)
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
callback(nil)
return
}
do {
let jsonData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! YouTubeManager.JSON
let accessToken = jsonData["access_token"]! as? String
callback(accessToken)
} catch {
callback(nil)
}
return
}).resume()
}
}
import Foundation
struct YoutubeVideo {
let title, thumbnailUrl, videoId: String
}
import Foundation
import Alamofire
/// This file will act as our YouTube manager.
class YouTubeManager {
typealias JSON = [String:AnyObject]
typealias SearchByTitleCallback = (_ response: [YoutubeVideo], _ success:Bool, _ message:String) -> Void
typealias PostVideoCallback = (String, Bool) -> Void
/** Singletone instance. */
static let sharedManager = YouTubeManager()
let apiKey = "..."
let channelID = "..."
let searchApiUrl = "https://www.googleapis.com/youtube/v3/search"
let identifier = "videoID: "
func parseIdentifier(input: String) -> String? {
let seperator = "videoID: "
if input.contains(self.identifier) {
let videoID = input.components(separatedBy: seperator)
return videoID.last
}
return nil
}
func searchVideoByTitle(title: String, completionHandler: #escaping SearchByTitleCallback) -> Void {
let eTitle = title.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
let urlString = searchApiUrl + "?part=snippet&q=" + eTitle + "&type=video&key=" + apiKey
let targetURL = URL(string: urlString)!
var returnArray = [YoutubeVideo]()
let task = URLSession.shared.dataTask(with: targetURL) { (data, response, error) in
guard error == nil else {
completionHandler(returnArray, false, "error = \(error!)")
return
}
guard let data = data else {
completionHandler(returnArray, false, "error = data is nil")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
completionHandler(returnArray, false, "response = \(response!)")
return
}
do {
let resultsDict = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! JSON
let items = resultsDict["items"] as! [JSON]
returnArray = items.map { item in
let snippetDict = item["snippet"] as! JSON
let title = snippetDict["title"] as! String
let thumbnail = ((snippetDict["thumbnails"] as! JSON)["default"] as! JSON)["url"] as! String
let videoid = (item["id"] as! JSON)["videoId"] as! String
return YoutubeVideo(title: title, thumbnailUrl: thumbnail, videoId: videoid)
}
completionHandler(returnArray, true, "")
} catch {
completionHandler(returnArray, false, "error serializing JSON: \(error)")
}
}
task.resume()
}
func postVideoToYouTube(uploadUrl: String, videoData: Data, title: String, callback: #escaping PostVideoCallback){
YoutubeTokenProvider.getAccessToken { (accessToken) in
guard let accessToken = accessToken
else { return }
let headers: HTTPHeaders = ["Authorization": "Bearer \(accessToken)"]
Alamofire.upload(
multipartFormData: { multipartFormData in
let metadata = "{'snippet':{'title' : '\(title)', 'description': 'This video was uploaded using Mr Tutor.'}}".data(using: .utf8, allowLossyConversion: false)!
multipartFormData.append(metadata, withName: "snippet", mimeType: "application/json")
multipartFormData.append(videoData, withName: "video", fileName: "sample.mp4", mimeType: "application/octet-stream")
},
to: "https://www.googleapis.com/upload/youtube/v3/videos?part=snippet",
headers: headers,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
do {
let jsonData = try JSONSerialization.jsonObject(with: response.data!, options: .allowFragments) as! JSON
let videoID = jsonData["id"] as! String
let identifierFinal = self.identifier + videoID
callback(identifierFinal, true)
} catch {
print("error serializing JSON: \(error)")
callback("", false)
}
print("Success")
}
case .failure(_):
print("Failure")
callback("", false)
}
})
}
}
}

Related

Getting statusCode other than 200...299 in HTTPURLResponse of URLSession

the following is my APIManager code, I'm using it in all my apps. But sometimes, the guard statement fails in connectToServer function, which means the statusCode of HTTPURLResponse other than 200...299 and the thing here is even after getting statusCode other than 200...299 my record got inserted into DB. I don't know what happens.
I thought that the cause of this behavior is from ServerURL, because I'm using a dev server with IP address http://00.000.0.000/ without security. Once I moved it to domain as https://XXX.XXXXXXXXXX.XXXXX/ it is working fine. Can you help me to figure out this?
And also will it supports for asynchronous calls?
import UIKit
struct APIResponse : Decodable {
let status : Bool
let message : String
let extra: String?
}
internal let BASE_URL = "http://00.000.0.00/app/v0_1/api/" // Example server URL
enum APIPath: String {
case registration = "registration"
case login = "login"
case getProfile = "get_profile"
func directURL() -> URL? {
let urlPath = BASE_URL + self.rawValue
return URL(string: urlPath)
}
func extendedURL(using parameters: [String: Any]) -> URL? {
let extendedPath = parameters.map { $0.key + "=" + "\($0.value)" }.joined(separator: "&")
let urlPath = BASE_URL + self.rawValue + "?" + extendedPath
return URL(string: urlPath)
}
}
enum APIMethod: String {
case get = "GET"
case put = "PUT"
case post = "POST"
case patch = "PATCH"
case delete = "DELETE"
}
enum APIHeaders {
case user
case app
var authorization: [String:String] {
let acceptLanguage = UserDefaults.standard.value(forKey: UDKeys.appleLanguage) as? String ?? ""
if self == .user {
let token = UserDefaults.standard.value(forKey: UDKeys.userToken) as? String ?? ""
return ["Content-Type": "application/json", "Accept": "application/json", "Accept-Language": acceptLanguage, "Token" : token]
}
return ["Content-Type": "application/json", "Accept": "application/json", "Accept-Language": acceptLanguage]
}
}
struct APIRequest {
var url: URL?
var method: String
var parameters: Data?
var headers: [String:String]
init(path: APIPath, method: APIMethod, headers: APIHeaders) {
self.url = path.directURL()
self.method = method.rawValue
self.headers = headers.authorization
}
init(path: APIPath, parameters: [String: Any], method: APIMethod, headers: APIHeaders) {
self.url = path.extendedURL(using: parameters)
self.method = method.rawValue
self.headers = headers.authorization
}
init(path: APIPath, method: APIMethod, body: [String:Any], headers: APIHeaders) {
self.url = path.directURL()
self.method = method.rawValue
self.parameters = try? JSONSerialization.data(withJSONObject: body, options: .sortedKeys)
self.headers = headers.authorization
}
init<Encode: Encodable>(path: APIPath, method: APIMethod, body: Encode, headers: APIHeaders) {
self.url = path.directURL()
self.method = method.rawValue
self.parameters = try? JSONEncoder().encode(body)
self.headers = headers.authorization
}
}
struct APIError: Error {
let reason: String
let code: String?
init(reason: String, code: String? = nil) {
self.reason = reason
self.code = code
}
}
struct APIDispatcher {
static let instance = APIDispatcher()
private init() {}
func dispatch<Decode: Decodable>(request: APIRequest, response: Decode.Type, result: #escaping (Result<Decode, APIError>) -> ()) {
DispatchQueue(label: "queue", attributes: .concurrent).async {
self.connectToServer(with: request) { (resultant) in
switch resultant {
case .success(let data):
do {
let decoded = try JSONDecoder().decode(response, from: data)
DispatchQueue.main.async {
result(.success(decoded))
}
} catch let decodedError {
print("[Decoded Error]: ", decodedError)
do {
let apiResponse = try JSONDecoder().decode(APIResponse.self, from: data)
let apiError = APIError(reason: apiResponse.message, code: apiResponse.extra)
DispatchQueue.main.async {
result(.failure(apiError))
}
} catch {
let apiError = APIError(reason: decodedError.localizedDescription)
DispatchQueue.main.async {
result(.failure(apiError))
}
}
}
case .failure(let error):
DispatchQueue.main.async {
result(.failure(error))
}
}
}
}
}
func dispatch(request: APIRequest, result: #escaping (Result<Dictionary<String,Any>, APIError>) -> ()) {
DispatchQueue(label: "queue", attributes: .concurrent).async {
self.connectToServer(with: request) { (resultant) in
switch resultant {
case .success(let data):
do {
let serialized = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! Dictionary<String,Any>
DispatchQueue.main.async {
result(.success(serialized))
}
} catch {
let error = APIError(reason: error.localizedDescription)
DispatchQueue.main.async {
result(.failure(error))
}
}
case .failure(let error):
DispatchQueue.main.async {
result(.failure(error))
}
}
}
}
}
private func connectToServer(with request: APIRequest, result: #escaping (Result<Data, APIError>) -> ()) {
guard let url = request.url else {
let error = APIError(reason: "Invalid URL")
result(.failure(error))
return
}
var urlRequest = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30)
urlRequest.httpMethod = request.method
urlRequest.httpBody = request.parameters
urlRequest.allHTTPHeaderFields = request.headers
print(urlRequest)
let urlSessionConfiguration = URLSessionConfiguration.default
urlSessionConfiguration.waitsForConnectivity = false
urlSessionConfiguration.timeoutIntervalForRequest = 30
urlSessionConfiguration.timeoutIntervalForResource = 60
let urlSession = URLSession(configuration: urlSessionConfiguration)
urlSession.dataTask(with: urlRequest) { (data, response, error) in
if let error = error {
let error = APIError(reason: error.localizedDescription)
result(.failure(error))
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
let error = APIError(reason: "Server Error")
result(.failure(error))
return
}
if let data = data {
result(.success(data))
}
}.resume()
}
}
Note: BASE_URL and APIResponse might be vary according to project.
I'm using it as
func login() {
self.startLoading()
let body = ["mobile_number": phoneNumberTF.text!, "password" : passwordTF.text!, "uuid" : UIDevice.current.identifierForVendor!.uuidString]
let apiRequest = APIRequest(path: .login, method: .post, body: body, headers: .app)
APIDispatcher.instance.dispatch(request: apiRequest) { result in
self.stopLoading()
switch result {
case .success(let response):
break
case .failure(let error):
break
}
}
}
EDIT: My bad I asked completely reverse on statsCode now I modified it.

Web service is called twice

I have a problem with a webService call.
The problem is that when I call the service, and debug code, and print log in console, I'm sure my webService is only called once (log print once in console), but my request is apparently sent twice to the server and I have duplicate data in the list.
I know that it's not a server-side problem because it only happens on IOS (not Android).
Here is my code for call services:
public class PersistencyManager {
public func SendPostHttpRequest(baseURL: String, parameter: [String:Any], content: String, closure:#escaping ((_ success:JSON,_ error:NSError?) -> Void)) {
let manager = Alamofire.SessionManager.default
debugPrint("Request parameter ------>",parameter)
debugPrint(" Service URL -------> \(baseURL)")
if let url = URL(string: baseURL) {
var urlRequest = URLRequest(url: url)
urlRequest.setValue("text/html; charset=utf-8", forHTTPHeaderField: "Content-Type")
urlRequest.setURLEncodedFormData(parameters: parameter)
manager.request(urlRequest).responseJSON { response in
switch response.result {
case .success(let JSON) :
debugPrint("get Json response ---> \((JSON)) ")
closure(JSON,nil)
case .failure(let error):
closure(nil,error as NSError)
debugPrint("get error ---> \((error.localizedDescription)) ")
}
}
}
}
}
class LibraryAPI {
static let shareInstance : LibraryAPI = { LibraryAPI() }()
private let persistencyManager : PersistencyManager
init() {
persistencyManager = PersistencyManager()
}
func GetPostResponse(baseURL : String,parameters:[String:Any],contentType: String,closure:#escaping ((_ success:PersistencyManager.JSON,_ error:NSError?) -> Void)) {
persistencyManager.SendPostHttpRequest(baseURL: baseURL, parameter: parameters, content: contentType, closure: { success, error in
closure(success, error)
})
}
}
class TransactionAPI: TransactionProtocol {
static let shareInstance: TransactionAPI = {TransactionAPI()}()
func AddNewManagerRequest(_ parameter: [String : Any], closure: #escaping (([String : Any]?, NSError?) -> Void)) {
let url = Constants.BaseURL + Constants.K_NEWREQPORTERAGE
LibraryAPI.shareInstance.GetPostResponse(baseURL: url, parameters: parameter, contentType: "JSON", closure: {success,error in
var response: [String:Any]?
if let json = success as? [String: Any] {
response = json
}
closure(response, error)
})
}
}
class AddNewOrderViewController: MainViewController {
private func RegisterForNewPorterageRequest() {
let time = Utilities.shareInstance.GetSystemTime()
guard let userID = UserDefaults.standard.value(forKey: "user_id") as? String else {
return
}
StartActivity(activityColor: Constants.ACTIVITY_COLOR)
let token = TokenCreator.shareInstance.CreateTokenWithUserID(userID: userID, methodName: Constants.M_NEWREQUESTPORTERAGE)
request.tok = token
request.time = time
request.user_id = userID
let jsonModel = Utilities.shareInstance.GetJsonForm(objectClass: request)
TransactionAPI.shareInstance.AddNewManagerRequest(jsonModel, closure: {[weak self] success,error in
guard let strongSelf = self else{
return
}
if error != nil {
OperationQueue.main.addOperation {
strongSelf.StopActivity()
strongSelf.CreateCustomTopField(text: Constants.serverError, color: Constants.ERROR_COLOR)
}
}
else {
if let response = success {
debugPrint("add request service call once")
if let status = response["status"] as? String {
if status == "succ" {
OperationQueue.main.addOperation {
strongSelf.presentResultAlert()
}
}else {
OperationQueue.main.addOperation {
strongSelf.StopActivity()
strongSelf.CreateCustomTopField(text: Constants.send_data_error, color: Constants.ERROR_COLOR)
}
}
}
}
}
})
}
}
After adding log to server, I made sure my request was sent twice to server.
All console log print once in console.
I don't know when I call service twice, and why my request was sent twice to the server.
I don't understand how the log be displayed once, but the service has been called twice?
Any help appreciated.
It's really confusing, but it works perfectly with this method.
i have this method in persistencyMangerClass and i using this method instead SendPostHttpRequest.What really is the difference between these two methods. :|
public func SendMultiPartRequestWith(baseUrl: String, parameters: [String : Any],closure: #escaping ((_ success:JSON,_ error:NSError? ) -> Void)){
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 30
manager.session.configuration.timeoutIntervalForResource = 15
debugPrint(" Service URL -------> \(baseUrl)")
debugPrint("Request parameter ------>",parameters)
let headers: HTTPHeaders = [
"Content-type": "multipart/form-data"
]
manager.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
if let data = value as? Data {
let fileName = (key as String) + ".jpg"
let mimType = (key as String) + "/jpg"
multipartFormData.append(data, withName: key as String, fileName: fileName, mimeType: mimType)
}
else {
if let v = value as? String {
multipartFormData.append("\(v)".data(using: String.Encoding.utf8)!, withName: key as String)
}else {
multipartFormData.append("".data(using: String.Encoding.utf8)!, withName: key as String)
}
}
}
}, usingThreshold: UInt64.init(), to: baseUrl, method: .post, headers: headers) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseString { response in
if let err = response.error{
closure(nil, err as NSError)
return
}
if let JSON = response.result.value {
closure(JSON, nil)
}
}
case .failure(let error):
closure(nil, error as NSError)
}
}
}

How to Upload Image as Binary

I want to upload image as binary, as in Postman we do below
Here is my code
var url = myURLString
url = url.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
guard let imageData = UIImageJPEGRepresentation(image, 0.4) else {
return
}
request.httpBody = imageData
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
Alamofire.request(request).responseJSON { (response) in
if let JSON = response.result.value as? NSDictionary {
print(JSON)
} else {
let message = response.result.error != nil ? response.result.error!.localizedDescription : "Unable to communicate."
print(message)
}
}
It seems that request is not attaching image file, returning following error message
"Response could not be serialized, input data was nil or zero length."
For swift 3, Alamofire 4
below code will work fine
var url = myURLString
url = url.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
guard let imageData = UIImageJPEGRepresentation(image, 0.4) else {
return
}
Alamofire.upload(imageData, to: URL(string: url)!, method: .post, headers: nil).responseJSON { (response) in
if let JSON = response.result.value as? NSDictionary {
print(JSON)
} else {
let message = response.result.error != nil ? response.result.error!.localizedDescription : "Unable to communicate."
print(message)
}
}
I am retrieving image from gallery and taking the name of the image by using below code:
you can use the below function.
func createMultipart(image: UIImage, callback: Bool -> Void){
// use SwiftyJSON to convert a dictionary to JSON
var parameterJSON = JSON([
"id_user": "test"
])
// JSON stringify
let parameterString = parameterJSON.rawString(encoding: NSUTF8StringEncoding, options: nil)
let jsonParameterData = parameterString!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
// convert image to binary
let imageData = UIImageJPEGRepresentation(image, 0.7)
// upload is part of AlamoFire
upload(
.POST,
URLString: "use your url here",
multipartFormData: { multipartFormData in
// fileData: puts it in "files"
multipartFormData.appendBodyPart(fileData: jsonParameterData!, name: "goesIntoFile", fileName: "json.txt", mimeType: "application/json")
multipartFormData.appendBodyPart(fileData: imageData, name: "file", fileName: "ios.jpg", mimeType: "image/jpg")
// data: puts it in "form"
multipartFormData.appendBodyPart(data: jsonParameterData!, name: "goesIntoForm")
},
encodingCompletion: { encodingResult in
switch encodingResult {
case .Success(let upload, _, _):
upload.responseJSON { request, response, data, error in
let json = JSON(data!)
println("json:: \(json)")
callback(true)
}
case .Failure(let encodingError):
callback(false)
}
}
)
}
let fotoImage = UIImage(named: "foto")
createMultipart(fotoImage!, callback: { success in
if success { }
})
Swift 4 and 5
var urlRequest = URLRequest(url: URL)
urlRequest.httpMethod = "PUT" // POST
urlRequest.setValue("image/jpeg", forHTTPHeaderField: "Content-Type")
var data = image!.jpegData(compressionQuality: CGFloat(0.5))!
URLSession.shared.uploadTask(with: urlRequest, from: data, completionHandler: { responseData, response, error in
DispatchQueue.main.async {
print(response)
guard let responseCode = (response as? HTTPURLResponse)?.statusCode, responseCode == 200 else {
if let error = error {
print(error)
}
return
}
// do your work
}
}).resume()

Having trouble with creating request to AppleMusicAPI

I am trying to send a request to Apple Music API by using this sample code (https://github.com/hirayaCM/AppleMusicSearch). I just replaced developer token to mine and run it.
However I am always getting 401 error even if I create and use new developer token. I used curl to check if my developer token is correct or not and request by curl was succeed.
Can anyone tell me what is wrong?
Here's the APIClient part of the sample code.
func search(term: String, completion: #escaping (SearchResult?) -> Swift.Void) {
let completionOnMain: (SearchResult?) -> Void = { searchResult in
DispatchQueue.main.async {
completion(searchResult)
}
}
guard var components = URLComponents(string: "https://api.music.apple.com/v1/catalog/\(APIClient.countryCode)/search") else { return }
let expectedTerms = term.replacingOccurrences(of: " ", with: "+")
let urlParameters = ["term": expectedTerms,
"limit": "10",
"types": "albums"]
var queryItems = [URLQueryItem]()
for (key, value) in urlParameters {
queryItems.append(URLQueryItem(name: key, value: value))
}
components.queryItems = queryItems
var request = URLRequest(url: components.url!)
request.httpMethod = "GET"
request.addValue("Bearer \(APIClient.developerToken)",
forHTTPHeaderField: "Authorization")
data(with: request) { data, error -> Void in
guard error == nil else {
print(#function, "URL Session Task Failed", error!)
completionOnMain(nil)
return
}
guard let searchResult = try? JSONDecoder().decode(SearchResult.self, from: data!) else {
print(#function, "JSON Decode Failed");
completionOnMain(nil)
return
}
completionOnMain(searchResult)
}
}
func album(id: String, completion: #escaping (Resource?) -> Swift.Void) {
let completionOnMain: (Resource?) -> Void = { resource in
DispatchQueue.main.async {
completion(resource)
}
}
guard let url = URL(string: "https://api.music.apple.com/v1/catalog/\(APIClient.countryCode)/albums/\(id)") else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("Bearer \(APIClient.developerToken)",
forHTTPHeaderField: "Authorization")
data(with: request) { data, error -> Void in
guard error == nil else {
print(#function, "URL Session Task Failed", error!)
completionOnMain(nil)
return
}
guard let jsonData = try? JSONSerialization.jsonObject(with: data!),
let dictionary = jsonData as? [String: Any],
let dataArray = dictionary["data"] as? [[String: Any]],
let albumDictionary = dataArray.first,
let albumData = try? JSONSerialization.data(withJSONObject: albumDictionary),
let album = try? JSONDecoder().decode(Resource.self, from: albumData) else {
print(#function, "JSON Decode Failed");
completionOnMain(nil)
return
}
completionOnMain(album)
}
}
Thanks.

POST body values are not recognized

I am calling a POST API but the post values are not getting recognised.
I am using alamofire for API handling
public func POSTRequest(postData : AnyObject?,APIName : String?,headers: HTTPHeaders?,success:#escaping (JSON) -> Void, failure:#escaping (String,String) -> Void)
{
let url = URL(string: APIName!)!
var urlRequest = URLRequest(url: url)
urlRequest.httpBody = postData as? Data
urlRequest.httpMethod = "POST"
urlRequest.allHTTPHeaderFields = headers
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 30.0
manager.request(urlRequest).responseJSON { ( responseData) -> Void in
if responseData.result.isSuccess {
let statusCode = responseData.response?.statusCode
if(statusCode == 200){
let resJson = JSON(responseData.result.value!)
success(resJson)
}
else {
let resJson = JSON(responseData.result.value!)
let errorMsg = resJson["message"].stringValue
let errorTitle = resJson["title"].stringValue
failure(errorMsg,errorTitle)
}
}
if responseData.result.isFailure {
let error : Error = responseData.result.error!
failure(error.localizedDescription,"")
}
}
}
// MARK: Login
func userLogin(postData: AnyObject, completionHandler:#escaping (JSON) -> Void,failure:#escaping (String,String) -> Void)
{
let apiName = GlobalConstants.rootUrl + GlobalConstants.loginUrl
let httpHeaders: HTTPHeaders = getHeaders()
ApiRequestHandler.shared.POSTRequest(postData: postData, APIName: apiName, headers: httpHeaders, success: { (JSON) in
print("JSON \(JSON)")
completionHandler(JSON)
})
{ (Errormsg,ErrorTitle) in
print("Error \(Errormsg)")
failure(Errormsg,ErrorTitle)
}
}
I am creating the post data here
let postDataDct = NSMutableDictionary()
postDataDct.setValue(self.userNameTextField.text, forKey: "username")
postDataDct.setValue(self.phoneNumberTextField.text, forKey: "mobile")
guard let data = try? JSONSerialization.data(withJSONObject:
postDataDct , options: JSONSerialization.WritingOptions.prettyPrinted)
as NSData else {
return;
}
print(NSString(data: data as Data, encoding:
String.Encoding.utf8.rawValue)! as String)
header creation here
func getHeaders() -> HTTPHeaders {
let headers: HTTPHeaders = ["Content-Type": "application/json" ,"Authorization" : "Basic dGFuaXNocWdfgaFF%waXJvb3Q6dzBEMFlkSnR0"]
return headers
}
I am calling the login method here
ApiManager.shared.userLogin(postData: postDataDct as AnyObject, completionHandler: { (JSON) in
print(JSON)
})
{ (ErrorMsg, ErrorTitle) in
print(ErrorMsg)
}
Please help me to find the issue.
API is giving me error response, saying user name or password is empty.
For Posting Alamofire have its own Parameters Class Try to use that like this with make share using the exact keys as on server.
let parameter: Parameters = [
"Email": txtfld_email.text!,
"Password": txtfld_password.text!
]
Cross check your keys:
let postDataDct = NSMutableDictionary()
postDataDct.setValue(self.userNameTextField.text, forKey: "username")
postDataDct.setValue(self.phoneNumberTextField.text, forKey: "mobile")
You are making the request with username and mobile It seems like the keys will be username and password according to the response you are getting name or password is empty.
let postDataDct = NSMutableDictionary()
postDataDct.setValue(self.userNameTextField.text, forKey: "username")
postDataDct.setValue(self.phoneNumberTextField.text, forKey: "password")
Suggestion:
Don't use NSMutableDictionary in Swift, use its native Swift
counterpart, Dictionary

Resources