When I set c to a
var a: [Any]
var c: Array<PostCategory>
error shown:
cannot convert value of type 'Array' to expected argument type
[Any]
how to solve the problem?
The error message is a bit misleading but try initializing the array before assigning it:
var c: Array<PostCategory> = []
...or...
var c = Array<PostCategory>()
I bet your PostCategory is a struct. Apparently struct arrays aren't convertible to an Any array. This is weird because all types conforms to the Any protocol.
If you change the PostCategory to a class instead, it should work fine. You might need to create a new initializer for the class though, since classes doesn't give you the same default initializer as a struct does.
Related
In my specific case, I want something like:
var collectionWithDifferentTypes: [ObservableObject] = []
var elementOfTypeAWhichConformsToObservableObject = TypeA()
var elementOfTypeBWhichConformsToObservableObject = TypeB()
collectionWithDifferentTypes.append(elementOfTypeAWhichConformsToObservableObject)
collectionWithDifferentTypes.append(elementOfTypeBWhichConformsToObservableObject)
But letting arrays conform to ObservableObject is not possible. As the docs state, arrays, sets, and dictionaries can only contain elements of the same type. Is there any way in swift to have a collection similar to the one I've described above?
The reason you are getting this error is that ObservableObject specifies an associated type ObjectWillChangePublisher that has to be defined in classes that conform to the protocol. There's an annoying trait of Swift that any protocol that specifies an associated type can't be used as a generic parameter since the runtime needs to know how the associated type is defined in order to effectively use it.
In order to use such a protocol as a generic type, you have to do what the error message specifies and use it as a generic constraint. That means that wherever you are defining the array has to be made into a generic context using ObservableObject as a constraint.
(class field)
class SomeClass<T: ObservableObject> {
var myArray: [T] = []
}
(function variable)
func doAThing<T: ObservableObject>() {
var myArray: [T] = []
}
(See this article for a more in-depth explanation on what this error means.)
Of course, there's always the nuclear option of just defining the array as [Any].
Two ways I can think of. If you subclass TypeB with TypeA then you could use var collectionWithDifferentTypes: [TypeA] = [] or if they both conformed the same protocol. No need for the subclassing. Just use var collectionWithDifferentTypes: [protocolName] = []
I want my struct to be created with sequence object using type inference. like:
var words: Buffer = ["generics", "ftw"] // type should be Buffer<String>
so I created a struct like this
struct Buffer<Element> {
init<S>(_ s: S) where Element == S.Element, S : Sequence {
}}
I just wrote code like above for testing generic init.
the code works when I create a struct like this:
var words = Buffer(["generics", "ftw"])
but I can't create a struct like this:
var words: Buffer = ["generics", "ftw"]
or
var words: Buffer<String> = ["generics", "ftw"]
the compiler complains me:
contextual type 'Buffer' cannot be used with array literal
I think I gave enough information to compiler as:
hey compiler, you'll gonna receive a Sequence. and the Element I mentioned is Sequence's Element.
what am I missing?
To use the syntax sugar, you have to adopt the ExpressibleByArrayLiteral protocol, which will make your struct able to be initialized using an array literal.
extension Buffer: ExpressibleByArrayLiteral {
typealias ArrayLiteralElement = Element
init(arrayLiteral: Element...) {
self.init(arrayLiteral)
}
}
After adding this extension, code like
var words: Buffer = ["generics", "ftw"]
or
var words: Buffer<String> = ["generics", "ftw"]
will compile.
Note that this does not mean that words is an array, although it is initialized with an array literal. If you assign the words variable to a variable that is of the Array type, your code will not compile, and vice versa. Also, note that if you don't infer the type Buffer in the variable declaration, it will be inferred as Array, so you must include Buffer in the variable decleration.
Let's say I have the following:
protocol MyProtocol {
}
struct MyStruct: MyProtocol {
}
var s1 = MyStruct()
var s2 = MyStruct()
var s3 = MyStruct()
var structArray = [s1, s2, s3]
When I try to assign this array of structs to an array of protocols (that each struct in structArray conforms to):
var protocolArray:[MyProtocol] = structArray
I get this error: Cannot convert array of type '[MyStruct]' to specified type '[MyProtocol]'
I would expect that since each object in the array conforms to the protocol it would be ok to say that "an array of structs that conform to some protocol" is assignable to something that expects "an array of anything that conforms to that protocol". But maybe this doesn't apply when the type is "an array of " vs just "thing", if that makes any sense.
For example, this is valid:
var p1:MyProtocol = s1
Because s1 conforms to MyProtocol. But if you use arrays then it doesn't seem to hold anymore.
Incidentally, this seems to work too:
var p1Array:[MyProtocol] = [s1, s2, s3]
Presumably because the type of the array is determined to be [MyProtocol] and isn't predetermined by some previous variable (like in my example above).
So anyways, all this to ask: What's the best way around this? How can I assign an array of structs (that conform to some protocol) to another array whose type is just "an array of things that conform that protocol".
I'm fairly new to Swift so I may be missing something trivial.
I usually just map the array to the type I need:
var protocolArray: [MyProtocol] = structArray.map { $0 as MyProtocol }
When you do that, you can actually get rid of the type annotation, so that the expression as a whole isn't actually that much longer:
var protocolArray = structArray.map { $0 as MyProtocol }
Swift won't automatically convert between array types, even if they are compatible. You have to be explicit about it one way or another.
I need to use a third party C library whose source I cannot modify and which makes heavy use of implicit type casting and typedefs to set values for its structs. They are all ints underneath and this is the preferred way of interacting with this library. I've previously used it in Objective C code without issue so now I am mostly porting some of my old code, but it feels like I am constantly hitting a brick wall with Swift.
tl;dr: how can I assign a different typedef value to a C struct member in Swift while automatically handling the type conversions (all typedefs are ints underneath)?
For example, consider the following definitions:
struct library_struct {
int member;
};
typedef enum library_consts {
LIBRARY_DEFINED_VALUE = 0
} library_consts;
In C or Objective C it would be absolutely acceptable to perform the following:
library_struct a;
a.member = LIBRARY_DEFINED_VALUE
However, attempting to do the same thing in Swift
var a: library_struct = library_struct()
a.member = LIBRARY_DEFINED_VALUE
results in an error:
Cannot assign a value of type 'library_consts' to a value of type 'Int32'
I tried several approaches:
Cast using Int32(). This leads to a Cannot find an initializer for type 'Int32' that accepts and argument list of type (library_consts) error.
Use LIBRARY_DEFINED_VALUE.rawValue. This won't work, because rawValue will return an UInt32, so I'm going to get the following error: Cannot assign a value of type 'UInt32' to a value of type 'Int32'
The only alternative is to cast again the value returned by rawValue to an Int32 like this: Int32(LIBRARY_DEFINED_VALUE.rawValue)
This works, but it feels wrong to make a double cast and it doesn't solve more complicated situations such as assigning a value of a different type (but still an int underneath) to a struct member such as the following:
enum library_consts
{
LIB_FALSE=0,
LIB_TRUE=1
};
typedef int lib_bool_t;
typedef struct another_struct {
lib_bool_t aFlag;
}
var b: another_struct = another_struct()
a.aFlag = LIB_FALSE
This will error out with "Cannot assign a value of type 'library_consts' to a value of type 'lib_bool_t'"
I am afraid that there is no easier solution if you cannot change the
C interface. Using the "Generated Interface" view in Xcode 7 you can
see that
enum library_consts
{
LIB_FALSE=0,
LIB_TRUE=1
};
typedef int lib_bool_t;
are mapped to Swift as
struct library_consts : RawRepresentable {
init(_ rawValue: UInt32)
init(rawValue: UInt32)
var rawValue: UInt32
}
typealias lib_bool_t = Int32
(the C int type is Int32 in Swift).
Swift does no implicit type conversions, which means that you have
to convert the types explicitly. In the second case it would be
var b: another_struct = another_struct()
b.aFlag = lib_bool_t(LIB_FALSE.rawValue)
I'm in AppDelegate, trying to pass a reply to a WatchKit Extension Request. I cannot use an array of enums as the value in a Dictionary whose values are typed as AnyObject. Experimenting in a Playground shows this:
enum E : Int {
case a = 0
case b
}
var x : AnyObject = [0, 1] // OK
var y : AnyObject = [E.a, E.b] // [E] is not convertible to AnyObject
Of course I can work around this by converting my enums to strings or numbers, but why is this a type error in Swift?
AnyObject exists for compatibility with Objective-C. You can only put objects into an [AnyObject] array that Objective-C can interpret. Swift enums are not compatible with Objective-C, so you have to convert them to something that is.
var x: AnyObject = [0, 1] works because Swift automatically handles the translation of Int into the type NSNumber which Objective-C can handle. Unfortunately, there is no such automatic conversion for Swift enums, so you are left to do something like:
var y: AnyObject = [E.a.rawValue, E.b.rawValue]
This assumes that your enum has an underlying type that Objective-C can handle, like String or Int.
Another example of something that doesn't work is an optional.
var a: Int? = 17
var b: AnyObject = [a] // '[Int?]' is not convertible to 'AnyObject'
See Working with Cocoa Data Types for more information.