Occuring issue while passing data between classes - ios

I'm trying to pass data from my Data class to my Login class (both files are separate.), but it throws 2 errors from "Login" class as it appears unable to receive the data
Below are the errors shown in Login.swift :
Error 1
Instance member 'dataObj' cannot be used on type 'Login'
Error 2
Expected declaration
Login.swift code
import Foundation
import Alamofire
import SwiftyJSON
class Login {let dataObj = Data(userName: "username", passWord: "password")
let endPoint = dataObj.todoEndPoint
let parameters = [
"username": dataObj.userName,
"password": dataObj.passWord
]
Alamofire.request(.POST, endPoint, parameters:parameters)
.responseJSON { response in
print(response.request)
print(response.response)
print(response.result)
if let JSON = response.result.value {
print("Did receive JSON data: \(JSON)")
}
else {
print("JSON data is nil.")
}
}
}
Data.swift code
import Foundation
import Alamofire
import SwiftyJSON
class Data {
var userName:String!
var passWord:String!
let todoEndpoint: String = "http://jsonplaceholder.typicode.com/todos/1"
init(userName : String, passWord : String) {
self.userName = userName
self.passWord = passWord
}
}
Screenshot of errors

You just have to change the Data instance property to class property by using static
class Data {
static var userName:String!
static var passWord:String!
static let todoEndpoint: String = "http://jsonplaceholder.typicode.com/todos/1"
}
and set direct values in userName and passWord.
in from login set value
Data.userName = "yourusername"
Data.passWord = "password"
2.
or you can also create Data object
class Data {
var userName:String!
var passWord:String!
let todoEndpoint: String = "http://jsonplaceholder.typicode.com/todos/1"
init(userName : String, passWord : String) {
self.userName = userName
self.passWord = passWord
}
and in login class
class Login {
static let dataObj = Data(userName: "usename", passWord: "password")
lazy var endPoint = Login.dataObj.todoEndpoint
let parameters = [
"username": dataObj.userName,
"password": dataObj.passWord
]
func getRequest() {
Alamofire.request(.POST, endPoint, parameters:parameters)
.responseJSON { response in
print(response.request)
print(response.response)
print(response.result)
if let JSON = response.result.value {
print("Did receive JSON data: \(JSON)")
}
else {
print("JSON data is nil.")
}
}
}
}
UPDATED
I guess you should create a singleton object to get a values of users everywhere.
class Data {
static let sharedInstance = Data()
static let todoEndpoint: String = "http://jsonplaceholder.typicode.com/todos/1"
var userName: String?
var passWord: String?
// for prevent from creating this class object
private init() { }
}
And in Login class.
class Login {
init(userName: String, passWord: String) {
Data.sharedInstance.userName = userName
Data.sharedInstance.passWord = passWord
}
/// call this method to login
func getRequest() {
print(Data.sharedInstance.userName)
print(Data.sharedInstance.passWord)
Alamofire.request(.POST, Data.endPoint, parameters: ["userName": Data.sharedInstance.userName, "passWord": Data.sharedInstance.passWord])
.responseJSON { response in
print(response.request)
print(response.response)
print(response.result)
if let JSON = response.result.value {
print("Did receive JSON data: \(JSON)")
}
else {
print("JSON data is nil.")
}
}
}
}
And from some other class create a Login instance and pass userName and Password values from there and call getRequest method using Login object.

According to your Data class implementation you should use it like :
class Login {
let dataObj = Data(userName : "username", passWord : "password")
let endPoint = dataObj.todoEndPoint
let parameters = [
"username": dataObj.userName,
"password": dataObj.passWord
]
Alamofire.request(.POST, endPoint, parameters:parameters)
.responseJSON { response in
print(response.request)
print(response.response)
print(response.result)
if let JSON = response.result.value {
print("Did receive JSON data: \(JSON)")
}
else {
print("JSON data is nil.")
}
}
}
If you want to get static data from Data then use singleton/shared instance.

Related

How to parse JSON dictionary using SwiftyJSON and Alamofire

I have a problem with my alamofire.request. I tried to to decode JSON response with Struct using SwiftyJSON.
. But my model data is getting nil.
here is my API response
{
"userDetails" :
{ "id":2,
"roleID":1,
"successFlag":1
},
"settingID" : "20"
}
Model class
import Foundation
import SwiftyJSON
struct User {
var settingID : String?
var userdetails : UserDetails?
init(json : JSON?) {
self.settingID = json?["settingID"].string
if let value = json?["userDetails"].dictionaryObject {
let new = UserDetails(json: JSON(value))
self.userdetails = new
}
}
}
struct UserDetails {
var id : Int?
var roleID : Int?
var successFlag : Int?
init(json : JSON?) {
self.id = json?["id"].int
self.roleID = json?["roleID"].int
self.successFlag = json?["successFlag"].int
}
}
My code for Data fetching using Alamofire and SwiftyJSON
import Alamofire
import SwiftyJSON
var userData : [User] = []
func fetchData() {
Alamofire.request(base_url + APIManager.loginApi, method: .post, parameters: params, encoding: URLEncoding.queryString, headers: nil).responseJSON { (resp) in
switch resp.result {
case .success :
print(resp.result)
do {
let myResult = try JSON(data: resp.data!)
print(myResult)
myResult.dictionaryValue.forEach({(user) in
let newUser = User(json: JSON(user))
self.userData.append(newUser)
print(self.userData)
})
}catch {
print(error)
}
break
case .failure :
break
}
}
}
But if i print self.userData , getting nill response.
Have you any idea why I can't decode my struct with my JSON data?.
Thanks a lot for your help
Try using Codable instead. It is easier to create a Codable model and is Apple recommended.
struct Root: Decodable {
let userDetails: User
let settingID: String
}
struct User: Decodable {
let id: Int
let roleID: Int
let successFlag: Int
}
Parse the data like,
do {
let response = try JSONDecoder().decode(Root.self, from: data)
print(response)
} catch {
print(error)
}
Change your response code like below
switch response.result {
case .success(let value):
let response = JSON(value)
print("Response JSON: \(response)")
let newUser = User(json: response)
self.userData.append(newUser)
print(self.userData)
case .failure(let error):
print(error)
break
}

"Bad Request" response on PredictHQ API ["error": invalid_client] "authentication" request

Currently looking to make an authentication request on the PredictHQ API using Alamofire.
I have generated client credentials in the developer's console, and transferred them to my project. The problem lies when I use the URL https://api.predicthq.com/oauth2/token/ in my function *receiveJSONData() to receive a network status of my requests.
Below are examples I followed from the PredictHQ website https://developer.predicthq.com/oauth2/ on how the cURL request should be formatted as.
1) I first created my networkServiceClient class
import UIKit
import Alamofire
class networkServiceClient {
private let token = "GENERATED_TOKEN_FROM_PREDICTHQ_API".data(using: String.Encoding.utf8)!.base64EncodedString()
private let secret = "GENERATED_SECRET_KEY_FROM_PREDICTHQ_API".data(using: String.Encoding.utf8)!.base64EncodedString()
private let id = "GENERATED_ID_FROM_PREDICTHQ_API".data(using: String.Encoding.utf8)!.base64EncodedString()
private let contentType = "application/json"
private var authURL = URL(string: "https://api.predicthq.com/oauth2/token/")
private var tokenDataURL = URL(string: "https://api.predicthq.com/v1/events/")
typealias webResponse = ([[String: Any]]?, Error?) -> Void
func receiveJSONData(completion: #escaping webResponse){
let loginString = String(format: "Basic %#:%#", id, secret)
let postHeaders:HTTPHeaders = ["Authorization": loginString, "Accept": contentType]
let params: [String : Any] = ["grant_type": "client_credentials", "scope": "account events signals"]
//post authentication request
AF.request(authURL!, method: .post, parameters: params, headers: postHeaders).responseJSON { (response) in
if let error = response.error {
completion(nil, error)
} else if let jsonArray = response.value as? [[String: Any]] {
completion(jsonArray, nil)
} else if let jsonDict = response.value as? [String: Any] {
completion([jsonDict], nil)
}
}
}
2) Then I implemented it in my TestingViewingController
import UIKit
import Alamofire
class TestingViewController: UIViewController {
private let networkingClient = networkServiceClient()
override func viewDidLoad() {
super.viewDidLoad()
networkingClient.receiveJSONData() { (json, error) in
if let error = error {
print(error.localizedDescription)
} else if let json = json {
print(json.description)
}
}
}
}
3) Lastly, here is my output in the TestingViewController console.
"error": invalid_client
Hey it might be because you're base64 encoding the client_id and secret separately but they should be encoded together (separated by colon) like the screenshot shows.

Alamofire completion handler issue

I have a POST request with certain data that I would like to receive. However when I use responseArray I would be thrown this error
json data is nil
but when I use responseJSON everything would be fine. Why is this so?
This code does not work:
Alamofire.request(.POST, Data.todoEndpoint, parameters: parameters)
.responseArray { (response: Response<[Particulars], NSError>) in
print(response.request)
print(response.response)
print(response.result)
if let result = response.result.value
{
do{
print(Realm.Configuration.defaultConfiguration.fileURL)
let realm = try Realm()
realm.add(result, update: true)
}
catch let err as NSError {
print("Error with realm: " + err.localizedDescription)
}
}
else
{
print("JSON data is nil.")
}
}
But this is fine:
Alamofire.request(.POST, Data.todoEndpoint, parameters: parameters)
.responseJSON { response in
print(response.request)
print(response.response)
print(response.result)
if let result = response.result.value
{
print(result)
}
else
{
print("JSON data is nil.")
}
}
I need responseArray so that I can have (response: Response<[Particulars], NSError>) and store my JSON response into realm
UPDATE
This is the Particulars class that I want to connect to. I'm trying to map my JSON objects to Realm based on this article https://blog.hyphe.me/realm-and-alamofire-in-a-effective-harmony/
import Foundation
import RealmSwift
import ObjectMapper
class Particulars: Object, Mappable {
dynamic var name = ""
dynamic var email = ""
dynamic var id = ""
dynamic var profilePicture = ""
dynamic var username = ""
dynamic var apiToken = ""
override static func primaryKey() -> String? {
return "id"
}
//Impl. of Mappable protocol
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
email <- map["email"]
profilePicture <- map["profile_picture"]
username <- map["username"]
apiToken <- map["api_token"]
}
}
And this is the JSON response:
[
"name" : "Jonny Walker",
"api_token" : "qwertyuiop1234567890",
"profile_picture" : "http:default_profile_picture.jpg",
"id" : 10,
"email" : "jwalker#gmail.com",
"username" : "jonny"
]
UPDATE 2
My completion handler is working fine with responseObject but my realm.add xxx is throwing up this error
Cannot convert value of type 'String' to expected argument type 'Object'
My code can be found here https://codeshare.io/v4M9M (lines 19- 25)
The Alamofire page shows how to handle response and does not list the responseArray method. https://github.com/Alamofire/Alamofire#response-serialization
You can use the responseJSON to get the JSON and convert into an array that you want. It would look something like this, (make changes to this based on your JSON response)
Alamofire.request(.POST, Data.todoEndpoint, parameters: parameters)
.responseJSON { response in
guard response.result.isSuccess else
{
//handle error
return
}
guard let value = response.result.value as? [String: AnyObject],
particularsArrayJson = value["particulars"] as? [[String: AnyObject]]
else{
//Malformed JSON, handle this case
}
var particulars = [Particulars]()
for particularsDict in paricularsArrayJson{
particulars.append(Pariculars(json:particularsDict))
}
}
You have to have an initializer in your Particulars that will initialise from the JSON provided.
Update:
The realm add method takes an instance of a class which extends from Object
Object is a class provided by Realm. So you may have to read up the documentation more.
You should be doing this instead
realm.add(particulars, updated:true)

Deserialize a JSON array to a Swift array of objects

I am new to Swift, and am not able to figure out how to deserialize a JSON array to an array of Swift objects. I'm able to deserialize a single JSON user to a Swift user object fine, but just not sure how to do it with a JSON array of users.
Here is my User.swift class:
class User {
var id: Int
var firstName: String?
var lastName: String?
var email: String
var password: String?
init (){
id = 0
email = ""
}
init(user: NSDictionary) {
id = (user["id"] as? Int)!
email = (user["email"] as? String)!
if let firstName = user["first_name"] {
self.firstName = firstName as? String
}
if let lastName = user["last_name"] {
self.lastName = lastName as? String
}
if let password = user["password"] {
self.password = password as? String
}
}
}
Here's the class where I'm trying to deserialize the JSON:
//single user works.
Alamofire.request(.GET, muURL/user)
.responseJSON { response in
if let user = response.result.value {
var swiftUser = User(user: user as! NSDictionary)
}
}
//array of users -- not sure how to do it. Do I need to loop?
Alamofire.request(.GET, muURL/users)
.responseJSON { response in
if let users = response.result.value {
var swiftUsers = //how to get [swiftUsers]?
}
}
The best approach is the use Generic Response Object Serialization provided by Alamofire here is an example :
1) Add the extension in your API Manager or on a separate file
public protocol ResponseObjectSerializable {
init?(response: NSHTTPURLResponse, representation: AnyObject)
}
extension Request {
public func responseObject<T: ResponseObjectSerializable>(completionHandler: Response<T, NSError> -> Void) -> Self {
let responseSerializer = ResponseSerializer<T, NSError> { request, response, data, error in
guard error == nil else { return .Failure(error!) }
let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let result = JSONResponseSerializer.serializeResponse(request, response, data, error)
switch result {
case .Success(let value):
if let
response = response,
responseObject = T(response: response, representation: value)
{
return .Success(responseObject)
} else {
let failureReason = "JSON could not be serialized into response object: \(value)"
let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
return .Failure(error)
}
case .Failure(let error):
return .Failure(error)
}
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
public protocol ResponseCollectionSerializable {
static func collection(response response: NSHTTPURLResponse, representation: AnyObject) -> [Self]
}
extension Alamofire.Request {
public func responseCollection<T: ResponseCollectionSerializable>(completionHandler: Response<[T], NSError> -> Void) -> Self {
let responseSerializer = ResponseSerializer<[T], NSError> { request, response, data, error in
guard error == nil else { return .Failure(error!) }
let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let result = JSONSerializer.serializeResponse(request, response, data, error)
switch result {
case .Success(let value):
if let response = response {
return .Success(T.collection(response: response, representation: value))
} else {
let failureReason = "Response collection could not be serialized due to nil response"
let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
return .Failure(error)
}
case .Failure(let error):
return .Failure(error)
}
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
2) update your model object like this:
final class User: ResponseObjectSerializable, ResponseCollectionSerializable {
let username: String
let name: String
init?(response: NSHTTPURLResponse, representation: AnyObject) {
self.username = response.URL!.lastPathComponent!
self.name = representation.valueForKeyPath("name") as! String
}
static func collection(response response: NSHTTPURLResponse, representation: AnyObject) -> [User] {
var users: [User] = []
if let representation = representation as? [[String: AnyObject]] {
for userRepresentation in representation {
if let user = User(response: response, representation: userRepresentation) {
users.append(user)
}
}
}
return users
}
}
3) then you can use it like that :
Alamofire.request(.GET, "http://example.com/users")
.responseCollection { (response: Response<[User], NSError>) in
debugPrint(response)
}
Source: Generic Response Object Serialization
Useful Link: Alamofire JSON Serialization of Objects and Collections
Since you are using Alamofire to make your requests why don't you give a chance to Hearst-DD ObjectMapper it has an Alamofire extension AlamofireObjectMapper. I think it'll save you time!
I would loop through them then add each user to an array (preferably a property of the VC and not an instance variable) but here is an example.
Alamofire.request(.GET, "YourURL/users")
.responseJSON { response in
if let users = response.result.value {
for user in users {
var swiftUser = User(user: user as! NSDictionary)
//should ideally be a property of the VC
var userArray : [User]
userArray.append(swiftUser)
}
}
}
You could also try EVReflection https://github.com/evermeer/EVReflection
It's even more simple, i.e. to parse JSON (code snippet taken from EVReflection link):
let json:String = "{
\"id\": 24,
\"name\": \"Bob Jefferson\",
\"friends\": [{
\"id\": 29,
\"name\":
\"Jen Jackson\"}]}"
you can use this class:
class User: EVObject {
var id: Int = 0
var name: String = ""
var friends: [User]? = []
}
in this way:
let user = User(json: json)

how to extract inner response values and pass it to UserModel

I'm using alamofire to get HTTP response from web api. just want to ask how can parse JSON values from response without using swift object mapper frameworks and pass it to authenticatedUser.I have some problem with loadUserData because response return Response type and my function work fine with NSArray how can I converted to work with Response
func GetUserCredential(username:String,password:String)->UserModel
{
let authenticatedUser = UserModel()
let user = username
let password = password
let credential = NSURLCredential(user: user, password: password, persistence: .ForSession)
Alamofire.request(.GET, "https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(usingCredential: credential)
.responseJSON { response in
return self.loadUserData(response);
}
return authenticatedUser;
}
func loadUserData(response: Response<AnyObject, NSError>) -> UserModel
{
var userObj = UserModel()
for resp as? Response<AnyObject, NSError> in response
{
let user:String = (resp["user"] as! String)
let isAuthenticated:Bool = (resp["authenticated"] as! Bool)
let isManager:Bool = true
userObj = UserModel(username:user,isAuthenticated:isAuthenticated,isManager:isManager)
}
return userObj
}
I believe you can do this with an Alamofire response object.
if let validResponse = response.result.value as? [String : AnyObject] {
return loadUserData(validResponse);
}
I would change your loadUserData to this:
func loadUserData(response: [String : AnyObject]) -> UserModel
// Do everything you're doing in loadUserData
}
I would also do more error checking in your loadUserData method as it's best not to assume that user and authenticated will always exist (your app will crash if they don't or if their values are of different types).
I would also suggest you name GetUserCredential with a lowercase getUserCredential to keep in best practice for naming functions.

Resources