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?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
ikevin8me
  • 4,253
  • 5
  • 44
  • 84

6 Answers6

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")
Jeshua Lacock
  • 5,730
  • 1
  • 28
  • 58
  • OK, please see the edited response. The `name` var needs a default value. Otherwise, it is uninitiated before self is allocated. – Jeshua Lacock Jan 22 '19 at 05:54
  • 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. – rmaddy Jan 22 '19 at 05:56
  • Then write your own initializer. – Jeshua Lacock Jan 22 '19 at 05:57
  • I added an example without inheriting NSObject. – Jeshua Lacock Jan 22 '19 at 05:59
  • 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. – Jeshua Lacock Jan 22 '19 at 06:12
  • 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. – Jeshua Lacock Jan 22 '19 at 19:38
3

Yet another solution is to use struct

struct Task: Codable {
    var name: String
}

let task = Task(name: "myname")
taka
  • 1,407
  • 5
  • 7
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.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
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")
Jarvis The Avenger
  • 2,750
  • 1
  • 19
  • 37
  • Why did you make the `name` property implicitly unwrapped? Don't do that needlessly. – rmaddy Jan 22 '19 at 05:54
  • @rmaddy I am hoping it will be initialized properly. – Jarvis The Avenger Jan 22 '19 at 05:56
  • Updated... Can you tell why you are so afraid of '!' ;) – Jarvis The Avenger Jan 22 '19 at 06:03
  • 1
    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. – rmaddy Jan 22 '19 at 06:06
  • @JarvisTheAvenger There are [a few very specific situations where you should use implicitly unwrapped optionals](https://stackoverflow.com/questions/24006975/why-create-implicitly-unwrapped-optionals-since-that-implies-you-know-theres?rq=1). Most of the time, either it's unnecessary or it risks crashing your app, which seems like a reasonable thing to be "afraid" of. – John Montgomery Jan 22 '19 at 06:15
  • @JohnMontgomery Yup...That's the beauty of stack overflow. we learn collaborating. Thanks. – Jarvis The Avenger Jan 22 '19 at 06:18
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("")
    }
}
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
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)"
}

}

starmarmor
  • 41
  • 3
  • 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()) – starmarmor Feb 22 '23 at 18:23