I am getting an error that says "Cannot convert value of type 'String' to argument type 'Test'" when trying to return a value from a function in lazy stored property. I am not able to spot any issue in the lazy var's closure.
import UIKit
public struct Value {}
public class Test {
var id: String = ""
public func getValueById(id: String) -> Value {
return Value()
}
public lazy var value: Value = {
// Compiler error: Cannot convert value of 'String' to expected argument type 'Test'
return getValueById(self.id)
}()
}
The compiler is confused about getValueById and the error message is meaningless - if not misleading.
What you need is to add self in front of getValueById(self.id) inside the closure:
public struct Value {}
public class Test {
var id: String = ""
public func getValueById(id: String) -> Value {
return Value()
}
public lazy var value: Value = {
return self.getValueById(self.id)
}()
}
Related
I have a simple protocol with an associated type, and a protocol extension that returns an array of this type.
protocol Foo {
associatedtype Unit
}
extension Foo {
var allTheFoos: [Unit] {
return []
}
}
I then have a struct which returns some Foo in a computed property, and another computed property that returns the allTheFoos array.
struct FakeFoo: Foo {
typealias Unit = Int
}
struct FooFactory {
var myFoo: some Foo {
return FakeFoo()
}
/* WHICH RETURN TYPE WILL
PLEASE THE SWIFT GODS?!
*/
var allTheFoos: [Foo.Unit] {
return myFoo.allTheFoos
}
}
The return type of allTheFoos matches Xcode's autocomplete type suggestion for the myFoo.allTheFoos call, but understandably, this yields a:
// var allTheFoos: [Foo.Unit] {}
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
My question is: What return type will make Xcode happy?
Below are my attempts, and their corresponding errors
// var allTheFoos: [some Foo.Unit] {}
ERROR: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions
// func allTheFoos() -> some [Foo.Unit]
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
// func allTheFoos<U: Foo.Unit>() -> [U]
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
ERROR: Cannot convert return expression of type '[(some Foo).Unit]' to return type '[U]'
// func allTheFoos<U>() -> [U] where U: (some Foo).Unit
ERROR: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions
FYI: The reason I'm doing this in a computed property in the first place is to keep things clean in some SwiftUI code.
Thanks for any help you can give!
=========== UPDATE ===========
I missed some important stuff in my sample code, so to give some context: the code is used in a unit conversion app, so something that can turn Celsius into Kelvin, Kg into lbs, and anything else into anything else.
protocol Unit: Equatable {
var suffix: String { get }
}
struct Value<UnitType: Unit> {
let amount: Double
let unit: UnitType
var description: String {
let formatted = String(format: "%.2f", amount)
return "\(formatted)\(unit.suffix)"
}
}
Value is constrained to a unit type, so that it's not possible to convert Celsius into Litres.
Therefore, we have a Conversion protocol that stores all similar units together:
protocol Conversion {
associatedtype UnitType: Unit
var allUnits: [UnitType] { get }
func convert(value: Value<UnitType>, to unit: UnitType) -> Value<UnitType>
}
extension Conversion {
func allConversions(for value: Value<UnitType>) -> [Value<UnitType>] {
let units = self.allUnits.filter { $0 != value.unit }
return units.map { convert(value: value, to: $0) }
}
}
So an example of a conversion for Temperature would be:
struct Temperature: Conversion {
enum Units: String, Unit, CaseIterable {
case celsius, farenheit, kelvin
var suffix: String {
switch self {
case .celsius: return "˚C"
case .farenheit: return "˚F"
case .kelvin: return "K"
}
}
}
var allUnits: [Units] { return Units.allCases }
func convert(value: Value<Units>, to unit: Units) -> Value<Units> {
/* ... */
}
}
Finally, the actual app code where the problem occurs is here:
struct MyApp {
var current: some Conversion {
return Temperature()
}
// ERROR: Associated type 'UnitType' can only be used with a concrete type or generic parameter base
var allConversions: [Value<Conversion.UnitType>] {
// This value is grabbed from the UI
let amount = 100.0
let unit = current.allUnits.first!
let value = Value(amount: amount, unit: unit)
return current.allConversions(for: value)
}
}
Looking at how you've implemented AnyValue, I think what you want here is just:
var allConversions: [String] {
let units = self.allUnits.filter { $0 != value.unit }
return units.map { convert(value: value, to: $0) }.description
}
Or something like that. All the algorithms that match what you're describing are just "conversion -> string." If that's the case, all you really want is CustomStringConvertible.
Managed to solve this issue using some Type Erasure:
struct AnyValue {
let description: String
init<U: Unit>(_ value: Value<U>) {
self.description = value.description
}
}
allowing for:
var allConversions: [AnyValue] {
// This value is grabbed from the UI
let amount = 100.0
let unit = current.allUnits.first!
let value = Value(amount: amount, unit: unit)
return current.allConversions(for: value).map(AnyValue.init)
}
However, this feels like a clunky solution (and one that opaque return types was introduced to avoid). Is there a better way?
enum FooEnum: Int {
case fooEnumCase = 13512
case fooEnumCase2 = 425156
}
class FooClass {
public func myFunction(chosenEnumCase: fooEnum) -> String {
/* Logic */
}
}
fooClass.myFunction(fooEnum.fooEnumCase)
I am getting the error:
FooEnum is not convertible to FooClass
What is wrong with this code?
First of all the code is very hard to read with lowercase class names.
Two issues:
With this syntax the method is supposed to be a class function.
public static func myFunction(chosenEnumCase: FooEnum) -> String
Which raises a new error
Missing argument label 'chosenEnumCase:' in call
and you have to add the parameter label:
enum FooEnum: Int {
case fooEnumCase = 13512
case fooEnumCase2 = 425156
}
class FooClass {
public static func myFunction(chosenEnumCase: FooEnum) -> String {
//logic
}
}
FooClass.myFunction(chosenEnumCase: FooEnum.fooEnumCase)
To explain the error message let's consider fooClass.myFunction:
let f: (fooClass) -> ((fooEnum) -> String) = fooClass.myFunction
It's a function that expects an instance of fooClass, and returns another function ((fooEnum) -> String). Whereas in your code it is fed an instance of type fooEnum.
Call that function on a instance:
let myInstance = fooClass()
myInstance.myFunction(chosenEnumCase: fooEnum.fooEnumCase)
Or make myFunction a class function:
public class func myFunction(chosenEnumCase: fooEnum) -> String
PS: To conform to the Swift naming conventions use FooEnum and FooClass.
I am trying to change the default associated type to String. but getting this error
explicitly specify the generic arguments to fix this issue.
Below code works fine for type Int but not for String. Am i missing something?
//override/Change the associated Type of Protocol
protocol Familiable{
associatedtype FamilyType = Int
func getName()->[FamilyType]
}
class NumberFamily:Familiable{
func getName() -> [Int] {
return [1,2,3,4,5]
}
}
let numRef = NumberFamily()
print(numRef.getName())
type(of: numRef)
struct NormalFamily<T:ExpressibleByStringLiteral>: Familiable{
func getName() -> [T] {
return ["name1","name2"]
}
}
let normalRef = NormalFamily()
normalRef.getName()
So I have this custom struct
public struct Feature {
var featureID: String = ""
var featureName: String = ""
var matchingFieldValue: String = ""
var polygonCollection = [MyPolygon]()
mutating func setFeatureID(featureID: String) {
self.featureID = featureID
}
func getMatchingFieldValue() -> String {
return matchingFieldValue
}
mutating func setMatchingFieldvalue(matchingFieldValue: String) {
self.matchingFieldValue = matchingFieldValue
}
public func getPolygonCollection() -> [MyPolygon] {
return polygonCollection
}
}
and I am trying to append a polygon to my polygonCollection by calling this function
feature.getPolygonCollection().append(polygon)
but I am getting the error
cannot use mutating member on immutable value: function call returns immutable value
by the way, I am defining the polygon in another class, it is a long class so just put the relevant calling code that gives the error.
All the previously asked ques
I appreciate all the help.
Due to value semantics getPolygonCollection() returns an immutable copy of polygonCollection. You cannot change it. That's what the error message says.
Add this function in the struct
mutating func add(polygon: MyPolygon) {
self.polygonCollection.append(polygon)
}
and call it
feature.add(polygon)
When I try to pass Swift nested class to function expecting AnyClass parameter I get following compiler error:
Expected member name or constructor call after type name
Is there a way I can fix above error and pass nested class as parameter?
func getInfo(type: AnyClass) -> UInt32
{
var outPropCount: UInt32 = 0
let properties: UnsafeMutablePointer<objc_property_t> = class_copyPropertyList(type, &outPropCount);
free(properties)
return outPropCount
}
public class Outer: NSObject
{
public class Data: NSObject
{
public var groups: [Int] = []
}
}
public class Data: NSObject
{
public var groups: [Int] = []
}
let o = getInfo(Outer) // works
let d = getInfo(Data) // works
let i = getInfo(Outer.Data) // fails to compile
Solution is to use .self to access type
let i = getInfo(Outer.Data.self)