What is the simplest way to instantiate a new Codable object in Swift?
Asked Answered
D

6

13

I have a Codable class:

class Task: Codable {
    var name: String
}

When I try to instantiate it:

let newTask = Task()
allTasks.append(newTask)

It gives me error:

Missing argument for parameter 'from' in call

All I want is to insert a new object (newTask) into an array. What is the simplest way to do this?

Diplococcus answered 22/1, 2019 at 5:40 Comment(2)
why do u want to use codable if you are not using any coding keys ??Lauzon
My answer has been upvoted more than your question. Please consider accepting it, or explain why you will not accept it. Thank you.Noakes
N
15

You can inherit the initializer from NSObject:

class Task: NSObject, Codable {
    var name: String = ""
}

let newTask = Task()

If you don't want to inherit NSObject, then just create your own initializer:

class Task: Codable {
    var name: String?

    init() {

    }
}

If you don't want to make name optional (or set it to a default), it has to be initialized in init() such as:

class Task: Codable {
    var name: String

    init(withName name: String) {
        self.name = name
    }
}
let newTask = Task(withName: "ikevin8me")
Noakes answered 22/1, 2019 at 5:51 Comment(6)
OK, please see the edited response. The name var needs a default value. Otherwise, it is uninitiated before self is allocated.Noakes
OK, that makes it work but this is a really not the best solution. It needlessly brings in NSObject and all of the Objective-C baggage with it. And defaulting the name to the empty string is very non-Swifty.Orlon
Then write your own initializer.Noakes
I added an example without inheriting NSObject.Noakes
If you don't want an empty string, you can make the string optional or you have to initialize it to something in init(). I changed the second example to make it optional.Noakes
By the way, your question is what is the simplest way to instantiate a Codeable object, and I believe my first example is just that - its arguably the simplest answer here and you can, of course, change the name var to an optional if you don't like having it preset.Noakes
W
3

Yet another solution is to use struct

struct Task: Codable {
    var name: String
}

let task = Task(name: "myname")
Workmanlike answered 22/1, 2019 at 5:54 Comment(0)
O
2

Your Task class doesn't provide its own initializer so it gets the one defined in the Codable protocol (which it gets from the Decodable protocol).

Either add your own explicit init that takes a name parameter or change your class to a struct. Either way, you need to create a Task by passing in a name value so the name property can be initialized.

None of this addresses the fact that the code you posted makes no use of Codable so maybe there is no need for your class (or struct) to conform to Codable.

Orlon answered 22/1, 2019 at 5:48 Comment(0)
L
0

The Task class doesn't provide any initializer to initialize an object that's why it's taking initializer from Codable protocol, Provide your own initializer to initialize an object.

Usage:

1.With Class

class Task: Codable {
    var name: String
    init(_ name : String) {
        self.name = name
    }
}

var task = Task("Jarvis")

2.With struct:

struct Task: Codable {
    var name: String
}

let task = Task(name: "jarvis")
Lauzon answered 22/1, 2019 at 5:53 Comment(6)
Why did you make the name property implicitly unwrapped? Don't do that needlessly.Orlon
@Orlon I am hoping it will be initialized properly.Lauzon
Updated... Can you tell why you are so afraid of '!' ;)Lauzon
I'm not afraid of it. I'm pointing out to you that you should not use it when it isn't appropriate. There's no need for it here.Orlon
@JarvisTheAvenger There are a few very specific situations where you should use implicitly unwrapped optionals. Most of the time, either it's unnecessary or it risks crashing your app, which seems like a reasonable thing to be "afraid" of.Sphene
@JohnMontgomery Yup...That's the beauty of stack overflow. we learn collaborating. Thanks.Lauzon
B
0

I would not assign a default value to the property but implement the expected init method and a convenience variant that didn't take any arguments or alternatively have a factory method that creates an "empty" Task

Here is the code with both options

class Task: Codable {
    var name: String

    init(_ name: String) {
        self.name = name
    }

    convenience init() {
        self.init("")
    }

    static func emptyTask() -> Task {
        return Task("")
    }
}
Barbey answered 22/1, 2019 at 6:19 Comment(0)
F
0

You could instantiate a object from json (for example when you use an API) like this:

struct Person: Codable {
var firstName: String
var lastName: String
var age: Int

enum CodingKeys: String, CodingKey {
    case firstName = "first_name"
    case lastName = "last_name"
    case age = "age"
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    
    firstName = try container.decode(String.self, forKey: .firstName)
    lastName = try container.decode(String.self, forKey: .lastName)
    age = try container.decode(Int.self, forKey: .age)
}

init(data: Data) {
    let decoder = JSONDecoder()
    do {
        let userDecoded = try decoder.decode(Person.self, from: data)
        
        self = userDecoded
    }
    catch {
        age = 0
        firstName = ""
        lastName = ""
    }
}

func fullName() -> String {
    return "\(self.firstName) \(self.lastName)"
}

}

Fredericksburg answered 22/2, 2023 at 18:22 Comment(1)
An example to test: let json = """ { "first_name": "C", "last_name": "Tangana", "age": 26 } """ let data = Data(json.utf8) let person = Person(data: data) print(person.fullName())Fredericksburg

© 2022 - 2024 — McMap. All rights reserved.