0

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?

Arsh Bhullar
  • 61
  • 1
  • 5

2 Answers2

3

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?

Vitalii Gozhenko
  • 9,220
  • 2
  • 48
  • 66
  • how will I retrieve the value sir? – Arsh Bhullar Mar 03 '20 at 11:44
  • It can be something like this: ```if let userData = UserDefaults.standard.data(forKey: "user"), let user = try? PropertyListDecoder().decode(User.self, from: user) else { //... do something with user } else { //... user doens't exists }``` – Vitalii Gozhenko Mar 03 '20 at 14:21
1

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)}