Whats wrong with my convenience initializer when I attempt to call self.init? - swift

I wrote the code below. The error I'm getting is at the end of my convenience initializer when I attempt to call self.init. What's wrong with my logic or syntax? Or how would I debug this? The error Xcode is giving is "cannot invoke with an argument list of type".
Thank you for any help on this
import Foundation
import UIKit
class Item: NSObject {
var name: String
var valueInDollars: Int
var serialNumber: String?
let dateCreated: NSDate
var stolen: Bool?
init(name: String, valueInDollars: Int, serialNumber: String?, dateCreated: NSDate, stolen: Bool?) {
self.name = name
self.valueInDollars = valueInDollars
self.serialNumber = serialNumber
self.dateCreated = NSDate()
self.stolen = stolen
//below why did I have to call super.init for this custom class that inherits from NSObject? Doesn't it automatically get called anyway once the custom object is initialized since it inherits from NSObject? It just seems like more work on my behalf which isn't fair. it should do it automatically. Why wouldn't it do it automatically if it inherits from NSObject?
super.init()
}
convenience init(random: Bool = false) {
if random {
let adjectives = ["Fluffy", "Rusty", "Shiny"]
let nouns = ["MacBook Pro", "Red Tribe Bike", "Vegan Pizzas"]
//take a variable that's random; the highest value for this random number will be the number of ojbects in the adjectives array
var idx = arc4random_uniform(UInt32(adjectives.count))
//now use this random variable and let it be the index of the adjectives array...so basically it'll be a random object from the adjectives array
let randomAdjective = adjectives[Int(idx)]
//AWESOME!! Now that the random adjective is stored in the randomAdjective constant, let's re-use the idx variable...Ayyyyeeeee re-use!
//we'll re-use it by doing the same process or close to the same process for nouns
idx = arc4random_uniform(UInt32(nouns.count))
let randomNoun = nouns[Int(idx)]
//now let's concatenate these two clever words, shall we!!
let randomName = "\(randomAdjective) \(randomNoun)"
//yayyy we're programmmminnngg!
//now let's ....whad de fuk....
let randomValue = Int(arc4random_uniform(100))
let randomSerialNumber = NSUUID().uuidString.components(separatedBy: "-").first!
let betterNotBeStolen: Bool = false
self.init(name: randomName, valueInDollars: randomValue, serialNumber: randomSerialNumber, stolen: betterNotBeStolen)
}
}
}

You got the error
"Cannot invoke 'Item.init' with an argument list of type '(name:
String, valueInDollars: Int, serialNumber: String, stolen: Bool)'"
because you missed the the dateCreated param in the self.init(params...).
So you need to replace this line
self.init(name: randomName, valueInDollars: randomValue, serialNumber: randomSerialNumber, stolen: betterNotBeStolen)
with this one
self.init(name: randomName, valueInDollars: randomValue, serialNumber: randomSerialNumber,dateCreated: NSDate(), stolen: betterNotBeStolen)
The next error which you will see after is
Self.init isn't called on all paths before returning from initializer
So you need to add else statement because the initializer don't know what to do when the random param is false.
convenience init(random: Bool = false) {
if random {
let adjectives = ["Fluffy", "Rusty", "Shiny"]
let nouns = ["MacBook Pro", "Red Tribe Bike", "Vegan Pizzas"]
//take a variable that's random; the highest value for this random number will be the number of ojbects in the adjectives array
var idx = arc4random_uniform(UInt32(adjectives.count))
//now use this random variable and let it be the index of the adjectives array...so basically it'll be a random object from the adjectives array
let randomAdjective = adjectives[Int(idx)]
//AWESOME!! Now that the random adjective is stored in the randomAdjective constant, let's re-use the idx variable...Ayyyyeeeee re-use!
//we'll re-use it by doing the same process or close to the same process for nouns
idx = arc4random_uniform(UInt32(nouns.count))
let randomNoun = nouns[Int(idx)]
//now let's concatenate these two clever words, shall we!!
let randomName = "\(randomAdjective) \(randomNoun)"
//yayyy we're programmmminnngg!
//now let's ....whad de fuk....
let randomValue = Int(arc4random_uniform(100))
let randomSerialNumber = NSUUID().uuidString.components(separatedBy: "-").first!
let betterNotBeStolen: Bool = false
self.init(name: randomName, valueInDollars: randomValue, serialNumber: randomSerialNumber,dateCreated: NSDate(), stolen: betterNotBeStolen)
} else {
self.init(name: "SomeName", valueInDollars: 3, serialNumber: "123", dateCreated: NSDate(), stolen: true)
}
}

Related

Why is this showing 'Expected 'func' keyword in instance method declaration'

I'm new to coding and using SwiftUI on Xcode, and I don't see what's wrong with this code:
class NormalSpace {
var name = ""
var value = 0
var rent = 0
var owned = false
}
var newRoad = NormalSpace()
newRoad.name = "New Road"
newRoad.value = 600
newRoad.rent = 25
newRoad.owned = false
the error 'Expected 'func' keyword in instance method declaration' only shows on the newRoad.name line. The same line also has the error: Invalid redeclaration of 'newRoad'.
What have I done wrong?
In an normal project, this is not valid:
class NormalSpace {
var name = ""
var value = 0
var rent = 0
var owned = false
}
var newRoad = NormalSpace()
newRoad.name = "New Road"
newRoad.value = 600
newRoad.rent = 25
newRoad.owned = false
You can do that in a playground (where it just runs this code directly), but in an app, code (such as the setting of the properties) belongs in a function or initializer.
That initialization code needs to be placed within some context. Let us imagine that it is inside a struct. But this still is not valid:
class NormalSpace {
var name = ""
var value = 0
var rent = 0
var owned = false
}
struct Foo {
var newRoad = NormalSpace()
newRoad.name = "New Road"
newRoad.value = 600
newRoad.rent = 25
newRoad.owned = false
}
The property, newRoad is fine, but the values are not. You need to wrap it inside a func (hence the error) or an init. E.g., this initializes newRoad during the init of the struct:
struct Foo {
let newRoad: NormalSpace
init() {
newRoad = NormalSpace()
newRoad.name = "New Road"
newRoad.value = 600
newRoad.rent = 25
newRoad.owned = false
}
}
Or you might initialize it in a func:
struct Foo {
var newRoad: NormalSpace?
mutating func bar() {
let road = NormalSpace()
road.name = "New Road"
road.value = 600
road.rent = 25
road.owned = false
newRoad = road
}
}
Or, alternatively, you can initialize this property with a closure (note the extra () at the end):
struct Foo {
let newRoad: NormalSpace = {
let road = NormalSpace()
road.name = "New Road"
road.value = 600
road.rent = 25
road.owned = false
return road
}()
}
But the code where you initialize the properties must be placed within some context, so that the compiler knows when those lines of code should be run.
Note, we would generally give NormalSpace a “memberwise initializer”, e.g.:
class NormalSpace {
let name: String
let value: Int
let rent: Int
let owned: Bool
init(name: String, value: Int, rent: Int, owned: Bool) {
self.name = name
self.value = value
self.rent = rent
self.owned = owned
}
}
Or, if a struct (and we would generally prefer to make our model objects struct value-types rather than class reference-types), this memberwise initializer would be created for you:
struct NormalSpace {
let name: String
let value: Int
let rent: Int
let owned: Bool
}
Either way, you can then provide all the desired values during initialization, e.g.:
struct Foo {
let newRoad = NormalSpace(name: "New Road", value: 600, rent: 25, owned: false)
}
Note, that I've removed the “default” values because those really are not appropriate. If you wanted to say that they do not need to be provided, then you would make them “optionals”. But there is generally a big difference between, say, a rent of zero (i.e. it is my grandmother’s house and she's not charging me) and that no rent has been specified. In Swift, we generally avoid using “sentinel” values like "" or 0 for “no value provided”.
Also, now that we have a memberwise initializer, I have also made the properties immutable (let rather than var). If you need to make them mutable (e.g. to let someone change the rent later), fine, revert back to var. But only make properties mutable if you really need to change them later on.

'(key: String, value: Any)' is not convertible to '[String : Any]'

I'm trying to refactor this code:
var indices = [String:[Int:Double]]()
apps.forEach { app in indices[app] = [Int:Double]()}
var index = 0
timeSeries.forEach { entry in
entry.apps.forEach{ (arg: (key: String, value: Double)) in
let (app, value) = arg
indices[app]?[index] = value
}
index += 1
}
so I have the signature:
var parameters = timeSeries.map{ entry in entry.apps as [String:Any] }
var indices = getIndices(with: apps, in: parameters) as? [String:[Int:Double]] ?? [String:[Int:Double]]()
and the method:
func getIndices(with source: [String], in entryParameters: [[String:Any]]) -> [String:[Int:Any]] {
var indices = [String:[Int:Any]]()
source.forEach { item in indices[item] = [Int:Any]() }
var index = 0
entryParameters.forEach { (arg: (key: String, value: Any)) in
let (key, value) = arg
indices[key]?[index] = value
index += 1
}
return indices
}
But this (only in the method, not the original, which works fine) gives: '(key: String, value: Any)' is not convertible to '[String : Any]' at the entryParameters line
The reason I must use Any is because the other source is [String:[Int:Bool]]
edit: some more details:
timeSeries is [TimeSeriesEntry]
// this will need to be defined per-model, so in a different file in final project
struct TimeSeriesEntry: Codable, Equatable {
let id: String
let uid: String
let date: Date
let apps: [String:Double]
let locations: [String:Bool]
func changeApp(app: String, value: Double) -> TimeSeriesEntry {
var apps = self.apps
apps[app] = value
return TimeSeriesEntry(id: self.id, uid: self.uid, date: self.date, apps: apps, locations: self.locations)
}
}
notes:
changed calling signature, thanks impression. problem remains.
The problem is that every value in entryParameters is a dictionary so when you do entryParameters.forEach you get dictionary type in the closure not (key, value).
You will get (key,value) when you call the forEach on this dictionary. So your method should look something like this:
func getIndices(with source: [String], in entryParameters: [[String:Any]]) -> [String:[Int:Any]] {
var indices = [String:[Int:Any]]()
source.forEach { item in indices[item] = [Int:Any]() }
var index = 0
entryParameters.forEach { entry in
entry.forEach {(arg: (key: String, value: Any)) in
let (key, value) = arg
indices[key]?[index] = value
}
index += 1
}
return indices
}
I just tested this briefly in Playground.
var double = [String:[Int:Double]]()
double["Taco"] = [2: 3.2]
func myFunc(double: [String:[Int:Double]]) {
print(double.count) //Prints 1
print(double["Taco"]!) //Prints [2:3.2]
}
func myFunc2(all: [String:Any]) {
print(all.count) //Prints 1
print(all["Taco"]!) //Prints [2:3.2]
}
myFunc(double: double)
myFunc2(all: double as [String:Any])
I have my initial [String:[Int:Double]](). Inside this dictionary I set double["Taco"] = [2: 3.2]. I can use 2 different functions, one that is taken as the original type [String:[Int:Double]] and it is easy to use as the functions take in the same parameter type. However, now I created a function that takes in a dictionary of [String:Any]. Now, to USE this method, we MUST typecast variables as [String:Any] when calling the method, as you can see below.

Swift dictionary with mix types (optional and non-optional)

I have a struct that has a method to return a dictionary representation. The member variables were a combination of different types (String and Double?)
With the following code example, there would be a warning from Xcode (Expression implicitly coerced from 'Double?' to Any)
struct Record {
let name: String
let frequency: Double?
init(name: String, frequency: Double?) {
self.name = name
self.frequency = frequency
}
func toDictionary() -> [String: Any] {
return [
"name": name,
"frequency": frequency
]
}
}
However if it was returning a type [String: Any?], the warning goes away:
struct Record {
let name: String
let frequency: Double?
init(name: String, frequency: Double?) {
self.name = name
self.frequency = frequency
}
func toDictionary() -> [String: Any?] {
return [
"name": name,
"frequency": frequency
]
}
}
My question is: Is this correct? And if it is, can you point me to some Swift documentation that explains this?
If it isn't, what should it be?
== EDIT ==
The following works too:
struct Record {
let name: String
let frequency: Double?
init(name: String, frequency: Double?) {
self.name = name
self.frequency = frequency
}
func toDictionary() -> [String: Any] {
return [
"name": name,
"frequency": frequency as Any
]
}
}
You can cast frequency to Any since the latter can hold any type. It is like casting instances of specific Swift type to the Objective-C id type. Eventually, you'll have to downcast objects of the type Any to a specific class to be able to call methods and access properties.
I would not recommend structuring data in your code using Any, or if you want to be specific Any? (when the object may or may not hold some value). That would be a sign of bad data-modeling.
From the documentation:
Any can represent an instance of any type at all, including function types.[...] Use Any and AnyObject only when you explicitly need the behavior and capabilities they provide. It is always better to be specific about the types you expect to work within your code.
(emphasis is mine)
Instead, use the Data type. And you would be able to decode Record or encode it from and into Data:
struct Record : Codable {
let name: String
let frequency: Double?
init(name: String, frequency: Double?) {
self.name = name
self.frequency = frequency
}
init(data: Data) throws {
self = try JSONDecoder().decode(Record.self, from: data)
}
func toData() -> Data {
guard let data = try? JSONEncoder().encode(self) else {
fatalError("Could not encode Record into Data")
}
return data
}
}
And use it like so:
let record = Record(name: "Hello", frequency: 13.0)
let data = record.toData()
let decodedRecord = try Record(data: data)
print(decodedRecord.name)
print(decodedRecord.frequency ?? "No frequency")
I'd recommend adding Codable conformance, and letting JSONEncoder do all the heavy lifting. If however you are constrained to the toDictionary approach, then I would advise against [String:Any?], since that might result in undefined behaviour (try to print the dictionary for more details).
A possible solution for toDictionary is to use an array of tuples that gets converted to a dictionary:
func toDictionary() -> [String: Any] {
let propsMap: [(String, Any?)] = [
("name", name),
("frequency", frequency)
]
return propsMap.reduce(into: [String:Any]()) { $0[$1.0] = $1.1 }
}
This way the nil properties simply don't receive entries in the output dictionary.

error: instance member 'tomato' cannot be used on type 'hamburger'

1.what I code
class hamburger {
var tomato: String
var patty: String
var bread: String
var number: Int
init(_ tomato: String, _ patty: String, _ bread: String, _ number: Int) {
self.tomato = tomato
self.patty = patty
self.bread = bread
self.number = number
}
init() {
self.tomato = "tomato"
self.patty = "patty"
self.bread = "bread"
self.number = 10
}
}
let sandwich = hamburger("texas" , "iii" , "iii" , 10)
print(hamburger.tomato)
2.error message
Playground execution failed:
error: dotinstall.playground:342:7: error: instance member 'tomato'
cannot be used on type 'hamburger'
print(hamburger.tomato)
^~~~~~~~~ ~~~~~~
3.The sample I followed
enter code here// Class
class User {
let name: String // property
var score: Int
init(name: String, score: Int) {
init(_ name: String, _ score: Int) {
self.name = name
self.score = score
}
init() {
self.name = "bob"
self.score = 30
}
}
//let tom = User(name: "tom", score: 23)
let tom = User("tom", 23)
print(tom.name)
print(tom.score)
let bob = User()
print(bob.name)
print(bob.score)
I have coded like 1 following 3, but I got a error message like 2.
what I did to solve
・anyway follow this sample to be like same
・studied a basic of class syntax, initializer, instance on website
・was looking for a mistypes
・I checked the order of property
I don't why it is not worked even if I just follow the sample code.
please give me tips on the solution.
thanks
You're making a mistake regarding object oriented programming. With hamburger.tomato you try to access the property tomato of the Class Hamburger, not the Object, which is sandwich here. So the solution would be:
print(sandwich.tomato)
In the future, what you might want to do is take a look at styling your code better. Classes(Hamburger) are written starting with an uppercased letter, while the objects, or instances(sandwich), of the Classes are written starting with a lowercased letter.

Swift: Unable to save tuples having more number of records in NSUserDefaults

I am trying to store tuple in NSUserDefaults. My tuple is below.
var primaryNumberAndName: [(name:String , number: String, numberType: String, imageProfile: UIImage, imageLogo: UIImage)] = []
Adding datas to tuples
for (var i : Int = 0; i < primaryNameArr.count; i++)
{
primaryNumberAndName.append(name: primaryNameArr[i] as! String,
number: primaryNumberArr[i] as! String,
numberType: primaryNumberTypeArr[i] as! String,
imageProfile: UIImage(named: "profile_man.png")!,
imageLogo: UIImage(named: "Our_Logo.png")!)
}
Coding:
var dictOfObjectsData = NSKeyedArchiver.archivedDataWithRootObject(primaryNumberAndName)
myUserDefaul.setObject(dictOfObjectsData, forKey: "PrimaryContacts")
Error:
Cannot invoke 'archivedDataWithRootObject' with an argument list of type {[(name:String , number: String, numberType: String, imageProfile: UIImage, imageLogo: UIImage)]}
Kindly guide me how to solve this.
One option could be to convert tuple into Dictionary before saving it. Something like this:
let primaryNumberKey = "primaryNumber"
let nameKey = "name"
func serializeTuple(tuple: AccessTuple) -> myDictionary {
return [
primaryNumberKey : tuple.primaryNumber,
nameKey : tuple.name
]
}
func deserializeDictionary(dictionary: myDictionary) -> myTuple {
return myTuple (
dictionary[primaryNumberKey] as String!,
dictionary[nameKey] as String!
)
}
This is how you can use it:
// Writing to defaults
let myConvertedDictionary = serializeTuple(myTuple)
NSUserDefaults.standardUserDefaults().setObject(myConvertedDictionary, forKey: "UserDetailslKey")
// Reading from defaults
let myFetchedDictionary = NSUserDefaults.standardUserDefaults().dictionaryForKey("UserDetailslKey") as myDictionary
let myFetchedTuple = deserializeDictionary(myFetchedDictionary)
You can now archive Dictionary the regular way!