Realm with Alamofire and Swift - ios

I am making my first attempt at using Realm in Swift. I have my model setup and I successfully receive the response using Alamofire and I map it to the object. I am then attempting to store the data in realm. It creates the object, but the values are the default values and not what is received from the backend. What am I missing? Here's the model:
import Foundation
import ObjectMapper
import RealmSwift
#objcMembers class LoginResponseModel: Object, Mappable {
dynamic var status = ""
dynamic var id = UUID().uuidString
dynamic var userId = 0
dynamic var authToken = ""
required convenience init?(map: Map) {
self.init()
}
convenience init(status: String, userId: Int, authToken: String) {
self.init()
self.status = status
self.userId = userId
self.authToken = authToken
}
override static func primaryKey() -> String? {
return "id"
}
func mapping(map: Map) {
status <- map["status"]
userId <- map["user_id"]
authToken <- map["auth_token"]
}
}
Here is where I attempt to save it:
APIHelper.shared.login(username: username!, password: password!, success: { (result) in
if let login = result {
if (login.status != "failed") {
let userId = login.userId
let authToken = login.authToken
UserDefaults.standard.setValue(String(userId), forKey: "USERID")
UserDefaults.standard.setValue(authToken, forKey: "AUTHTOKEN")
UserDefaults.standard.synchronize()
vc.modalPresentationStyle = .fullScreen
do {
let realm = try! Realm()
try realm.write {
realm.add(login)
}
} catch(let error) {
print(error)
}
InstanceID.instanceID().instanceID { (result, error) in
if let error = error {
print(error)
} else if let result = result {
self.sendToken(userId: String(userId), authToken: authToken, fcmToken: result.token)
}
}
self.present(vc, animated: true, completion: nil)
}
}
Any ideas?
API call:
public func login(username: String, password: String, success: #escaping(_ response: LoginResponseModel?) -> Void, failure: #escaping(_ error: Error?) -> Void) {
let url = "\(baseUrl)appLogin"
AF.request(url, method: .post, parameters: ["emailOrPhone": username, "password": password]).responseString { response in
print(type(of: response).self)
switch response.result {
case .success(let json):
let resp = LoginResponseModel(JSONString: json)
success(resp)
case .failure(let error):
failure(error)
}
}
}
I get the object here:
let login = realm.objects(LoginResponseModel.self).first
Value of object:
status = ""
id = "1820A5D4-A714-4774-9707-4709ED39B570"
userId = 0
authToken = ""

Related

Posting data to database with Alamofire failed

I am having an issue with posting data using Alamofire. I am making a comment box. I grab user data from the server and post his comment using his information with his comment on the article id, but when I post it sends no information to the server! I see only empty data.
The user data are successfully loaded from the server and I can see it in the console using the print accountDetails but after posting nothing is shown!
Breakpoint gives valid data too!
My code:
class DetailsViewController: UIViewController {
var data: JSON?
var userData = [JSON]()
var accountDetails = ["name": "", "email": "", "phone": ""]
#IBOutlet weak var CommentTableView: UITableView!
#IBOutlet weak var CommentTXTField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
getUserData()
print("Account:\(accountDetails)")
if let id = Helper.getUserId() {
ContactBtn.isHidden = false
} else {
ContactBtn.isHidden = true
}
}
#IBAction func AddCommentBTN(_ sender: Any) {
let commentTXT = CommentTXTField.text
print(commentTXT!)
let name = self.accountDetails["name"]
let mobile = self.accountDetails["phone"]
let email = self.accountDetails["email"]
let articleId = data!["id"].string!
API.AddComment(articleId: articleId, name: name!, email: email!, phone: mobile!, message: commentTXT!) { (error: Error?, success: Bool) in
if success {
print("Registerd Successfuly")
} else {
print("Faile To Comment")
}
}
}
func getUserData() {
guard let UserId = Helper.getUserMob() else { return }
let url = "https://site.co/apis/getprofile.php?mob=" + UserId
AF.request(url).validate().responseJSON { [self] response in
switch response.result
{
case .failure(let error):
print(error)
case .success(let value):
let json = JSON(value)
if let id = json["data"]["id"].string {
print("id: \(id)")
}
self.accountDetails["name"] = json["data"]["name"].string
self.accountDetails["email"] = json["data"]["email"].string
self.accountDetails["phone"] = json["data"]["phone"].string
}
}
}
}
API.AddComment function
class func AddComment(articleId: String, name: String, email: String, message: String, completion: #escaping (_ error: Error?, _ success: Bool)->Void){
let url = URLs.AddComment
let parameters = [
"article_id": articleId,
"name": name,
"email": email,
"message": message
]
AF.request(url, method: .post, parameters: parameters, encoding: URLEncoding.default , headers: nil)
.validate(statusCode: 200..<300)
.responseJSON { response in
switch response.result
{
case .failure(let error):
completion(error, false)
print(error)
case .success(let value):
let json = JSON(value)
if let id = json["data"]["id"].string {
print("id: \(id)")
completion(nil, true)
}
}
}
}

Wait response result before proceeding next code iOS swift

I'm beginner in iOS swift.
I have a problem: when I do a network request, the compiler executed the code below without waiting the server response.
func callingRiderLoginCopy(userID: String, Password:String, completeCode: Int) {
print("I am in callingRiderLoginCopy And Complete verification Code is \(completeCode)")
let parameters : [String : Any] = ["userId": userID, "password": Password, "verificationCode": completeCode]
guard let url = URL(string: "\(Constents.baseURL)/rider/logIn") else {
print("Invalid URL")
return
}
AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default)
.responseJSON { response in
switch response.result {
case .success:
if let result = response.data {
do {
let resultIs = try JSONDecoder().decode(RiderLoginCopy.self, from:result)
self.riderSuc = resultIs.success // Strore the success state in riderSuc
print("Data is riderSuc ** \(self.riderSuc)")
if let results = resultIs.user {
print("This data came from RiderLoginCopy API : \(resultIs)")
self.setToken = KeychainWrapper.standard.set(resultIs.token!, forKey: "token")
self.retrivedToken = KeychainWrapper.standard.string(forKey: "token")!
print("Retrived Token ::: \(self.retrivedToken)")
}
} catch {
print(error)
}
}
case .failure(let error):
print(error)
}
}
}
#IBAction func verifyAct(_ sender: UIButton) {
let userId = enteredUserID
KeychainWrapper.standard.string(forKey: "Password")!
let password = enteredPassword
KeychainWrapper.standard.string(forKey: "UserID")!
let completeCode:Int = Int(textField1.text! + textField2.text! + textField3.text! + textField4.text!)!
self.callingRiderLoginCopy(userID: userId, Password: password, completeCode: completeCode)
if self.riderSuc == 1 {
let storyboard = UIStoryboard(name: "Rider", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "signin2VC") as! SignIn2VC
vc.verifyCode = completeCode
self.navigationController?.pushViewController(vc, animated: true)
}else{
print("Plese Try Try again RiderSuc is not equal to 1 !: ")
}
}
Use a closure completionHandler: parameter in your function definition of type (String?) -> Void to know when the response is received and then proceed with the rest of the code.
Modify your function from:
func callingRiderLoginCopy(userID: String, Password: String, completeCode: Int) {
To this:
func callingRiderLoginCopy(userID: String, Password:String, completeCode: Int, completionHandler: #escaping (String?) -> Void) {
And return the retrivedToken when it's received successfully and return nil when it's not.
And when you call this method modify your call from this:
self.callingRiderLoginCopy(userID: userId, Password: password, completeCode: completeCode)
To this:
self.callingRiderLoginCopy(userID: userId, Password: password, completeCode: completeCode) { token in
// Do stuff related to token
}

Load and save struct into UserDefaults

I am trying to save the user in UserDefaults from a struct that fetches the data from an API when a user logs in successfully.
Here is my Webservice class:
import Foundation
import UIKit
struct Resource<T: Codable> {
let url : URL
let httpMethod = HTTPMethod.post
var body : Data? = nil
}
extension Resource {
init(url: URL) {
self.url = url
}
}
enum HTTPMethod : String {
case get = "GET"
case post = "POST"
}
enum NetworkingError: Error {
case domainError
case badResponse
case encodingError
case decodingError
}
class Webservice {
func load<T>(resource: Resource<T>, caller: UIViewController ,completion: #escaping (Result<T, NetworkingError>) -> Void) {
var request = URLRequest(url: resource.url)
request.httpMethod = resource.httpMethod.rawValue
request.httpBody = resource.body
request.addValue("application/JSON", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
return completion(.failure(.domainError))
}
let json = try? JSONSerialization.jsonObject(with: data, options: [])
print(json)
do {
let result = try JSONDecoder().decode(T.self, from: data)
//save to UserDefaults
UserDefaults.standard.set(PropertyListEncoder().encode(T), forKey: "user")\\ here I am getting error that T.type does not conform to Encodable protocol.
completion(.success(result))
}catch {
do {
if let result = try? JSONDecoder().decode(LoginErrorResponse.self, from: data){
print(result.errors.msg)
DispatchQueue.main.async {
let alert = AlertService().alert(message: "\(result.errors.msg[0])")
caller.present(alert, animated: true)
}
completion(.failure(.decodingError))
}
if let result = try? JSONDecoder().decode(SignUpErrorResponse.self, from: data){
print(result.errors.msg)
DispatchQueue.main.async {
let alert = AlertService().alert(message: "\(result.errors.msg)")
caller.present(alert, animated: true)
}
completion(.failure(.decodingError))
}
}
}
}.resume()
}
}
Here is my model class:
import Foundation
struct User: Encodable, Decodable {
let name: String
let email: String
let password: String
let first_name: String
let last_name: String
}
extension User {
static var all : Resource<User> = {
guard let url = URL(string: "http://orderahead.gagzweblab.xyz:3001/login") else {
fatalError("url is incorrect")
}
return Resource<User>(url: url)
}()
static func create(vm : UserViewModel) -> Resource<UserResponseModel?> {
let user = User(vm)
guard let url = URL(string: "http://orderahead.gagzweblab.xyz:3001/register") else {
fatalError("url is incorrect")
}
guard let data = try? JSONEncoder().encode(user) else {
fatalError("error encoding user")
}
var resource = Resource<UserResponseModel?>(url: url)
resource.body = data
return resource
}
}
extension User {
init?(_ vm: UserViewModel) {
let email = vm.email
let password = vm.password
let first_name = vm.first_name
let last_name = vm.last_name
let name = vm.name
self.password = password
self.email = email
self.first_name = first_name
self.last_name = last_name
self.name = name
}
}
And here is my view model:
import Foundation
struct UserViewModel : Codable {
let user : User
}
extension UserViewModel {
var name : String {
return self.user.name
}
var email : String {
return self.user.email
}
var password : String {
self.user.password
}
var first_name: String {
self.user.first_name
}
var last_name: String {
self.user.last_name
}
}
This is how I am calling it:
let login = LoginUser(email: email, password: password)
let vm = UserViewModel(loginUser: login)
Webservice().load(resource: User.create(vm: vm), caller: self) { (result) in
My model and view model conform to Codable as well as my Resource is Codable too.
What is the reason of the error that T.type does not conform to protocol Encodable? How to resolve it?
Is this approach to send and receive data appropriate?
You didn't specify that T should be Encodable for load(resource:... function of class Webservice:
Change this:
class Webservice {
func load<T>(resource: Resource<T>, caller: UIViewController ,completion: #escaping (Result<T, NetworkingError>) -> Void) {
To this:
class Webservice {
func load<T: Encodable>(resource: Resource<T>, caller: UIViewController ,completion: #escaping (Result<T, NetworkingError>) -> Void) {
And also you need to encode value, not generic type here:
UserDefaults.standard.set(PropertyListEncoder().encode(T.self), forKey: "user")
should be
UserDefaults.standard.set(try PropertyListEncoder().encode(result), forKey: "user")
But another question is: Why do you encode from JSON and then encode it to PropertyList? Why not save JSON data in UserDefaults?
may be it will work for you.
extension UserDefaults {
func save<T: Codable>(_ object: T, forKey key: String) {
let encoder = JSONEncoder()
if let encodedObject = try? encoder.encode(object) {
UserDefaults.standard.set(encodedObject, forKey: key)
UserDefaults.standard.synchronize()
}
}
func getObject<T: Codable>(forKey key: String) -> T? {
if let object = UserDefaults.standard.object(forKey: key) as? Data {
let decoder = JSONDecoder()
if let decodedObject = try? decoder.decode(T.self, from: object) {
return decodedObject
}
}
return nil
}}
this is how to store
func setCoableInUser<T: Codable>(_ object:T, key: String)-> Void{
UserDefaults.standard.save(object, forKey: key)}

Wrapping Alamofire calls into custom objects in Swift?

I am using Alamofire to make REST calls to my server to get, add, update, and delete objects. What I'm wondering is, is it possible (and recommended) to wrap the Alamofire calls into my own custom objects (DTO), so that I can simply do things like user.delete(id) and user.add(newUser) instead of having Alamofire code all over my codebase? If so, how can I do it so I can return the success and failure handlers (the "promise" from the javascript world)? Something like this:
user.add(newUser)
.success(userObject){
}
.error(response){
}
Here is my current code:
//register a user
let parameters = [“username”: txtUsername.text! , "password": txtPassword.text!]
Alamofire.request(.POST, “http://myserver.com/users", parameters: parameters, encoding: .JSON)
.validate()
.responseObject { (response: Response<User, NSError>) in
if let user = response.result.value {
self.user = user
}
}
}
User.swift
final class User : ResponseObjectSerializable, ResponseCollectionSerializable {
var id: Int
var firstName: String?
var lastName: String?
var email: String?
var password: String?
init?(response: NSHTTPURLResponse, representation: AnyObject) {
id = representation.valueForKeyPath("id") as! Int
firstName = representation.valueForKeyPath("first_name") as? String
lastName = representation.valueForKeyPath("last_name") as? String
email = representation.valueForKeyPath("email") as? String
password = representation.valueForKeyPath("password") 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
}
//Can I wrap my alamofire calls in methods like this:
func getById(id: Int)
func getAll()
func add(user: User)
func update (user: User)
func delete(id: int)
}
i think a static func is what you want , i wrote a example for it :
final class User{
var valueHandle :((AnyObject) -> ())?
var errorHandle :((NSError)->())?
func success(value:(AnyObject) -> ())->Self{
//pass success handle
self.valueHandle = value
return self
}
func error(error:(NSError)->())->Self{
//pass error handle
self.errorHandle = error
return self
}
static func getById(id: Int)->User{
return userRequest(.GET, urlString: "https://httpbin.org/get", param: ["foo": "bar"])
}
static func userRequest(method:Alamofire.Method , urlString:String ,param:[String: AnyObject]?) -> User {
let user = User()
Alamofire.request(method, urlString, parameters:param)
.validate()
.responseJSON { response in
switch response.result {
case .Success(let value):
//invoke with your back userobj
user.valueHandle?(value)
print(value)
case .Failure(let error):
user.errorHandle?(error)
}
}
return user
}
}
then you can use like :
User.getById(1)
.success { (value) in
print("value = \(value)")
}
.error { (error) in
print("error = \(error)")
}
hope it be helpful :D

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)

Resources